mirror of
https://github.com/djhackersdev/bemanitools.git
synced 2026-09-11 18:05:08 -05:00
Bemanitools v5.26 release
This commit is contained in:
263
src/imports/SMX.h
Normal file
263
src/imports/SMX.h
Normal file
@@ -0,0 +1,263 @@
|
||||
#ifndef SMX_H
|
||||
#define SMX_H
|
||||
|
||||
#ifndef __cplusplus
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef SMX_EXPORTS
|
||||
#define SMX_API __declspec(dllexport)
|
||||
#else
|
||||
#define SMX_API __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define SMX_EXTERN_C extern "C"
|
||||
#else
|
||||
#define SMX_EXTERN_C
|
||||
#endif
|
||||
|
||||
struct SMXInfo;
|
||||
struct SMXConfig;
|
||||
enum SensorTestMode;
|
||||
enum SMXUpdateCallbackReason;
|
||||
struct SMXSensorTestModeData;
|
||||
|
||||
// All functions are nonblocking. Getters will return the most recent state. Setters will
|
||||
// return immediately and do their work in the background. No functions return errors, and
|
||||
// setting data on a pad which isn't connected will have no effect.
|
||||
|
||||
// Initialize, and start searching for devices.
|
||||
//
|
||||
// UpdateCallback will be called when something happens: connection or disconnection, inputs
|
||||
// changed, configuration updated, test data updated, etc. It doesn't specify what's changed,
|
||||
// and the user should check all state that it's interested in.
|
||||
//
|
||||
// This is called asynchronously from a helper thread, so the receiver must be thread-safe.
|
||||
typedef void SMXUpdateCallback(int pad, enum SMXUpdateCallbackReason reason, void *pUser);
|
||||
SMX_EXTERN_C SMX_API void SMX_Start(SMXUpdateCallback UpdateCallback, void *pUser);
|
||||
|
||||
// Shut down and disconnect from all devices. This will wait for any user callbacks to complete,
|
||||
// and no user callbacks will be called after this returns. This must not be called from within
|
||||
// the update callback.
|
||||
SMX_EXTERN_C SMX_API void SMX_Stop();
|
||||
|
||||
// Set a function to receive diagnostic logs. By default, logs are written to stdout.
|
||||
// This can be called before SMX_Start, so it affects any logs sent during initialization.
|
||||
typedef void SMXLogCallback(const char *log);
|
||||
SMX_EXTERN_C SMX_API void SMX_SetLogCallback(SMXLogCallback callback);
|
||||
|
||||
// Get info about a pad. Use this to detect which pads are currently connected.
|
||||
SMX_EXTERN_C SMX_API void SMX_GetInfo(int pad, struct SMXInfo *info);
|
||||
|
||||
// Get a mask of the currently pressed panels.
|
||||
SMX_EXTERN_C SMX_API uint16_t SMX_GetInputState(int pad);
|
||||
|
||||
// Update the lights. Both pads are always updated together. lightsData is a list of 8-bit RGB
|
||||
// colors, one for each LED. Each panel has lights in the following order:
|
||||
//
|
||||
// 0123
|
||||
// 4567
|
||||
// 89AB
|
||||
// CDEF
|
||||
//
|
||||
// Panels are in the following order:
|
||||
//
|
||||
// 012 9AB
|
||||
// 345 CDE
|
||||
// 678 F01
|
||||
//
|
||||
// With 18 panels, 16 LEDs per panel and 3 bytes per LED, each light update has 864 bytes of data.
|
||||
//
|
||||
// Lights will update at up to 30 FPS. If lights data is sent more quickly, a best effort will be
|
||||
// made to send the most recent lights data available, but the panels won't update more quickly.
|
||||
//
|
||||
// The panels will return to automatic lighting if no lights are received for a while, so applications
|
||||
// controlling lights should send light updates continually, even if the lights aren't changing.
|
||||
SMX_EXTERN_C SMX_API void SMX_SetLights(const char lightsData[864]);
|
||||
|
||||
// By default, the panels light automatically when stepped on. If a lights command is sent by
|
||||
// the application, this stops happening to allow the application to fully control lighting.
|
||||
// If no lights update is received for a few seconds, automatic lighting is reenabled by the
|
||||
// panels.
|
||||
//
|
||||
// SMX_ReenableAutoLights can be called to immediately reenable auto-lighting, without waiting
|
||||
// for the timeout period to elapse. Games don't need to call this, since the panels will return
|
||||
// to auto-lighting mode automatically after a brief period of no updates.
|
||||
SMX_EXTERN_C SMX_API void SMX_ReenableAutoLights();
|
||||
|
||||
// Get the current controller's configuration.
|
||||
//
|
||||
// Return true if a configuration is available. If false is returned, no panel is connected
|
||||
// and no data will be set.
|
||||
SMX_EXTERN_C SMX_API bool SMX_GetConfig(int pad, struct SMXConfig *config);
|
||||
|
||||
// Update the current controller's configuration. This doesn't block, and the new configuration will
|
||||
// be sent in the background. SMX_GetConfig will return the new configuration as soon as this call
|
||||
// returns, without waiting for it to actually be sent to the controller.
|
||||
SMX_EXTERN_C SMX_API void SMX_SetConfig(int pad, const struct SMXConfig *config);
|
||||
|
||||
// Reset a pad to its original configuration.
|
||||
SMX_EXTERN_C SMX_API void SMX_FactoryReset(int pad);
|
||||
|
||||
// Request an immediate panel recalibration. This is normally not necessary, but can be helpful
|
||||
// for diagnostics.
|
||||
SMX_EXTERN_C SMX_API void SMX_ForceRecalibration(int pad);
|
||||
|
||||
// Set a panel test mode and request test data. This is used by the configuration tool.
|
||||
SMX_EXTERN_C SMX_API void SMX_SetTestMode(int pad, enum SensorTestMode mode);
|
||||
SMX_EXTERN_C SMX_API bool SMX_GetTestData(int pad, struct SMXSensorTestModeData *data);
|
||||
|
||||
// Return the build version of the DLL, which is based on the git tag at build time. This
|
||||
// is only intended for diagnostic logging, and it's also the version we show in SMXConfig.
|
||||
SMX_EXTERN_C SMX_API const char *SMX_Version();
|
||||
|
||||
// General info about a connected controller. This can be retrieved with SMX_GetInfo.
|
||||
struct SMXInfo
|
||||
{
|
||||
// True if we're fully connected to this controller. If this is false, the other
|
||||
// fields won't be set.
|
||||
bool m_bConnected;
|
||||
|
||||
// This device's serial number. This can be used to distinguish devices from each
|
||||
// other if more than one is connected. This is a null-terminated string instead
|
||||
// of a C++ string for C# marshalling.
|
||||
char m_Serial[33];
|
||||
|
||||
// This device's firmware version.
|
||||
uint16_t m_iFirmwareVersion;
|
||||
};
|
||||
|
||||
enum SMXUpdateCallbackReason {
|
||||
// This is called when a generic state change happens: connection or disconnection, inputs changed,
|
||||
// test data updated, etc. It doesn't specify what's changed. We simply check the whole state.
|
||||
SMXUpdateCallback_Updated,
|
||||
|
||||
// This is called when SMX_FactoryReset completes, indicating that SMX_GetConfig will now return
|
||||
// the reset configuration.
|
||||
SMXUpdateCallback_FactoryResetCommandComplete
|
||||
};
|
||||
|
||||
// The configuration for a connected controller. This can be retrieved with SMX_GetConfig
|
||||
// and modified with SMX_SetConfig.
|
||||
//
|
||||
// The order and packing of this struct corresponds to the configuration packet sent to
|
||||
// the master controller, so it must not be changed.
|
||||
struct SMXConfig
|
||||
{
|
||||
#if 0
|
||||
// These fields are unused and must be left at their existing values.
|
||||
uint8_t unused1 = 0xFF, unused2 = 0xFF;
|
||||
uint8_t unused3 = 0xFF, unused4 = 0xFF;
|
||||
uint8_t unused5 = 0xFF, unused6 = 0xFF;
|
||||
|
||||
// Panel thresholds are labelled by their numpad position, eg. Panel8 is up.
|
||||
// If m_iFirmwareVersion is 1, Panel7 corresponds to all of up, down, left and
|
||||
// right, and Panel2 corresponds to UpLeft, UpRight, DownLeft and DownRight. For
|
||||
// later firmware versions, each panel is configured independently.
|
||||
//
|
||||
// Setting a value to 0xFF disables that threshold.
|
||||
uint16_t masterDebounceMilliseconds = 0;
|
||||
uint8_t panelThreshold7Low = 0xFF, panelThreshold7High = 0xFF; // was "cardinal"
|
||||
uint8_t panelThreshold4Low = 0xFF, panelThreshold4High = 0xFF; // was "center"
|
||||
uint8_t panelThreshold2Low = 0xFF, panelThreshold2High = 0xFF; // was "corner"
|
||||
|
||||
// These are internal tunables and should be left unchanged.
|
||||
uint16_t panelDebounceMicroseconds = 4000;
|
||||
uint16_t autoCalibrationPeriodMilliseconds = 1000;
|
||||
uint8_t autoCalibrationMaxDeviation = 100;
|
||||
uint8_t badSensorMinimumDelaySeconds = 15;
|
||||
uint16_t autoCalibrationAveragesPerUpdate = 60;
|
||||
|
||||
uint8_t unused7 = 0xFF, unused8 = 0xFF;
|
||||
|
||||
uint8_t panelThreshold1Low = 0xFF, panelThreshold1High = 0xFF; // was "up"
|
||||
|
||||
// Which sensors on each panel to enable. This can be used to disable sensors that
|
||||
// we know aren't populated. This is packed, with four sensors on two pads per byte:
|
||||
// enabledSensors[0] & 1 is the first sensor on the first pad, and so on.
|
||||
uint8_t enabledSensors[5];
|
||||
|
||||
// How long the master controller will wait for a lights command before assuming the
|
||||
// game has gone away and resume auto-lights. This is in 128ms units.
|
||||
uint8_t autoLightsTimeout = 1000/128; // 1 second
|
||||
|
||||
// The color to use for each panel when auto-lighting in master mode. This doesn't
|
||||
// apply when the pads are in autonomous lighting mode (no master), since they don't
|
||||
// store any configuration by themselves. These colors should be scaled to the 0-170
|
||||
// range.
|
||||
uint8_t stepColor[3*9];
|
||||
|
||||
// The rotation of the panel, where 0 is the standard rotation, 1 means the panel is
|
||||
// rotated right 90 degrees, 2 is rotated 180 degrees, and 3 is rotated 270 degrees.
|
||||
// This value is unused.
|
||||
uint8_t panelRotation;
|
||||
|
||||
// This is an internal tunable that should be left unchanged.
|
||||
uint16_t autoCalibrationSamplesPerAverage = 500;
|
||||
|
||||
// The firmware version of the master controller. Where supported (version 2 and up), this
|
||||
// will always read back the firmware version. This will default to 0xFF on version 1, and
|
||||
// we'll always write 0xFF here so it doesn't change on that firmware version.
|
||||
//
|
||||
// We don't need this since we can read the "I" command which also reports the version, but
|
||||
// this allows panels to also know the master version.
|
||||
uint8_t masterVersion = 0xFF;
|
||||
|
||||
// The version of this config packet. This can be used by the firmware to know which values
|
||||
// have been filled in. Any values not filled in will always be 0xFF, which can be tested
|
||||
// for, but that doesn't work for values where 0xFF is a valid value. This value is unrelated
|
||||
// to the firmware version, and just indicates which fields in this packet have been set.
|
||||
// Note that we don't need to increase this any time we add a field, only when it's important
|
||||
// that we be able to tell if a field is set or not.
|
||||
//
|
||||
// Versions:
|
||||
// - 0xFF: This is a config packet from before configVersion was added.
|
||||
// - 0x00: configVersion added
|
||||
// - 0x02: panelThreshold0Low through panelThreshold8High added
|
||||
uint8_t configVersion = 0x02;
|
||||
|
||||
// The remaining thresholds (configVersion >= 2).
|
||||
uint8_t unused9[10];
|
||||
uint8_t panelThreshold0Low, panelThreshold0High;
|
||||
uint8_t panelThreshold3Low, panelThreshold3High;
|
||||
uint8_t panelThreshold5Low, panelThreshold5High;
|
||||
uint8_t panelThreshold6Low, panelThreshold6High;
|
||||
uint8_t panelThreshold8Low, panelThreshold8High;
|
||||
#endif
|
||||
};
|
||||
//static_assert(sizeof(SMXConfig) == 84, "Expected 84 bytes");
|
||||
|
||||
// The values (except for Off) correspond with the protocol and must not be changed.
|
||||
enum SensorTestMode {
|
||||
SensorTestMode_Off = 0,
|
||||
// Return the raw, uncalibrated value of each sensor.
|
||||
SensorTestMode_UncalibratedValues = '0',
|
||||
|
||||
// Return the calibrated value of each sensor.
|
||||
SensorTestMode_CalibratedValues = '1',
|
||||
|
||||
// Return the sensor noise value.
|
||||
SensorTestMode_Noise = '2',
|
||||
|
||||
// Return the sensor tare value.
|
||||
SensorTestMode_Tare = '3',
|
||||
};
|
||||
|
||||
// Data for the current SensorTestMode. The interpretation of sensorLevel depends on the mode.
|
||||
struct SMXSensorTestModeData
|
||||
{
|
||||
// If false, sensorLevel[n][*] is zero because we didn't receive a response from that panel.
|
||||
bool bHaveDataFromPanel[9];
|
||||
|
||||
int16_t sensorLevel[9][4];
|
||||
bool bBadSensorInput[9][4];
|
||||
|
||||
// The DIP switch settings on each panel. This is used for diagnostics
|
||||
// displays.
|
||||
int iDIPSwitchPerPanel[9];
|
||||
};
|
||||
|
||||
#endif
|
||||
7
src/imports/avs-ea3.h
Normal file
7
src/imports/avs-ea3.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef IMPORTS_AVS_EA3_H
|
||||
#define IMPORTS_AVS_EA3_H
|
||||
|
||||
void ea3_boot(struct property_node *conf);
|
||||
void ea3_shutdown(void);
|
||||
|
||||
#endif
|
||||
202
src/imports/avs.h
Normal file
202
src/imports/avs.h
Normal file
@@ -0,0 +1,202 @@
|
||||
#ifndef IMPORTS_AVS_H
|
||||
#define IMPORTS_AVS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum property_create_flag {
|
||||
PROPERTY_FLAG_READ = 0x1,
|
||||
PROPERTY_FLAG_WRITE = 0x2,
|
||||
PROPERTY_FLAG_CREATE = 0x4,
|
||||
PROPERTY_FLAG_BINARY = 0x8,
|
||||
PROPERTY_FLAG_APPEND = 0x10,
|
||||
};
|
||||
|
||||
enum property_node_traversal {
|
||||
TRAVERSE_PARENT = 0,
|
||||
TRAVERSE_FIRST_CHILD = 1,
|
||||
TRAVERSE_FIRST_ATTR = 2,
|
||||
TRAVERSE_FIRST_SIBLING = 3,
|
||||
TRAVERSE_NEXT_SIBLING = 4,
|
||||
TRAVERSE_PREVIOUS_SIBLING = 5,
|
||||
TRAVERSE_LAST_SIBLING = 6,
|
||||
TRAVERSE_NEXT_SEARCH_RESULT = 7,
|
||||
TRAVERSE_PREV_SEARCH_RESULT = 8,
|
||||
};
|
||||
|
||||
enum property_type {
|
||||
PROPERTY_TYPE_VOID = 1,
|
||||
PROPERTY_TYPE_S8 = 2,
|
||||
PROPERTY_TYPE_U8 = 3,
|
||||
PROPERTY_TYPE_S16 = 4,
|
||||
PROPERTY_TYPE_U16 = 5,
|
||||
PROPERTY_TYPE_S32 = 6,
|
||||
PROPERTY_TYPE_U32 = 7,
|
||||
PROPERTY_TYPE_S64 = 8,
|
||||
PROPERTY_TYPE_U64 = 9,
|
||||
PROPERTY_TYPE_BIN = 10,
|
||||
PROPERTY_TYPE_STR = 11
|
||||
};
|
||||
|
||||
struct property;
|
||||
struct property_node;
|
||||
|
||||
struct avs_net_interface {
|
||||
uint8_t mac_addr[6];
|
||||
uint8_t unknown[30];
|
||||
};
|
||||
|
||||
enum psmap_type {
|
||||
PSMAP_TYPE_S8 = 2,
|
||||
PSMAP_TYPE_U8 = 3,
|
||||
PSMAP_TYPE_S16 = 4,
|
||||
PSMAP_TYPE_U16 = 5,
|
||||
PSMAP_TYPE_S32 = 6,
|
||||
PSMAP_TYPE_U32 = 7,
|
||||
PSMAP_TYPE_S64 = 8,
|
||||
PSMAP_TYPE_U64 = 9,
|
||||
PSMAP_TYPE_STR = 10,
|
||||
/* Used on avs 803 instead of value 10 */
|
||||
PSMAP_TYPE_STR_LEGACY = 11,
|
||||
PSMAP_TYPE_ATTR = 45,
|
||||
PSMAP_TYPE_BOOL = 50,
|
||||
};
|
||||
|
||||
#define PSMAP_FLAG_HAVE_DEFAULT 0x01
|
||||
|
||||
struct property_psmap {
|
||||
uint8_t type;
|
||||
uint8_t flags; /* A guess. Might just be a bool. */
|
||||
uint16_t offset;
|
||||
uint32_t size;
|
||||
const char *path;
|
||||
intptr_t xdefault;
|
||||
};
|
||||
|
||||
#define PSMAP_BEGIN(name) \
|
||||
struct property_psmap name[] = {
|
||||
|
||||
#define PSMAP_REQUIRED(type, xstruct, field, path) \
|
||||
{ \
|
||||
type, \
|
||||
0, \
|
||||
offsetof(xstruct, field), \
|
||||
sizeof( ((xstruct *) 0)->field ), \
|
||||
path, \
|
||||
0, \
|
||||
}, \
|
||||
|
||||
#define PSMAP_OPTIONAL(type, xstruct, field, path, xdefault) \
|
||||
{ \
|
||||
type, \
|
||||
PSMAP_FLAG_HAVE_DEFAULT, \
|
||||
offsetof(xstruct, field), \
|
||||
sizeof( ((xstruct *) 0)->field ), \
|
||||
path, \
|
||||
(intptr_t) xdefault, \
|
||||
}, \
|
||||
|
||||
#define PSMAP_END \
|
||||
{ 0xFF, 0, 0, 0, NULL, 0 } \
|
||||
};
|
||||
|
||||
#if AVS_VERSION >= 1500
|
||||
# define AVS_LOG_WRITER(name, chars, nchars, ctx) \
|
||||
void name(const char * chars , uint32_t nchars , void * ctx )
|
||||
|
||||
typedef void (*avs_log_writer_t)(const char *chars, uint32_t nchars,
|
||||
void *ctx);
|
||||
#else
|
||||
# define AVS_LOG_WRITER(name, chars, nchars, ctx) \
|
||||
void name(void * ctx , const char * chars , uint32_t nchars )
|
||||
|
||||
typedef void (*avs_log_writer_t)(void *ctx, const char *chars,
|
||||
uint32_t nchars);
|
||||
#endif
|
||||
|
||||
typedef int (*avs_reader_t)(uint32_t context, void *bytes, size_t nbytes);
|
||||
|
||||
#if AVS_VERSION >= 1600
|
||||
/* "avs" and "std" heaps have been unified */
|
||||
typedef void (*avs_boot_t)(
|
||||
struct property_node *config, void *com_heap, size_t sz_com_heap,
|
||||
void *reserved, avs_log_writer_t log_writer, void *log_context);
|
||||
|
||||
void avs_boot(
|
||||
struct property_node *config, void *com_heap, size_t sz_com_heap,
|
||||
void *reserved, avs_log_writer_t log_writer, void *log_context);
|
||||
#else
|
||||
typedef void (*avs_boot_t)(
|
||||
struct property_node *config, void *std_heap, size_t sz_std_heap,
|
||||
void *avs_heap, size_t sz_avs_heap, avs_log_writer_t log_writer,
|
||||
void* log_context);
|
||||
|
||||
void avs_boot(
|
||||
struct property_node *config, void *std_heap, size_t sz_std_heap,
|
||||
void *avs_heap, size_t sz_avs_heap, avs_log_writer_t log_writer,
|
||||
void *log_context);
|
||||
#endif
|
||||
|
||||
void avs_shutdown(void);
|
||||
|
||||
void log_body_fatal(const char *module, const char *fmt, ...);
|
||||
void log_body_info(const char *module, const char *fmt, ...);
|
||||
void log_body_misc(const char *module, const char *fmt, ...);
|
||||
void log_body_warning(const char *module, const char *fmt, ...);
|
||||
void log_boot(avs_log_writer_t log_writer, void *log_context);
|
||||
void log_change_level(int level);
|
||||
|
||||
int avs_net_ctrl(int ioctl, void *bytes, uint32_t nbytes);
|
||||
|
||||
int avs_thread_create(int (*proc)(void *), void *ctx, uint32_t sz_stack,
|
||||
unsigned int priority);
|
||||
void avs_thread_destroy(int thread_id);
|
||||
void avs_thread_exit(int result);
|
||||
void avs_thread_join(int thread_id, int *result);
|
||||
|
||||
uint32_t property_read_query_memsize(
|
||||
avs_reader_t reader, uint32_t context, int unk0, int unk1);
|
||||
struct property *property_create(
|
||||
int flags, void *buffer, uint32_t buffer_size);
|
||||
struct property_node *property_search(
|
||||
struct property *prop, struct property_node *root, const char *path);
|
||||
int property_insert_read(
|
||||
struct property *prop, struct property_node *root, avs_reader_t reader,
|
||||
uint32_t context);
|
||||
int property_mem_write(struct property *prop, void *bytes, int nbytes);
|
||||
void *property_desc_to_buffer(struct property *prop);
|
||||
void property_file_write(struct property *prop, const char *path);
|
||||
int property_set_flag(struct property *prop, int flags, int mask);
|
||||
void property_destroy(struct property *prop);
|
||||
|
||||
int property_psmap_import(struct property *prop, struct property_node *root,
|
||||
void *dest, const struct property_psmap *psmap);
|
||||
int property_psmap_export(struct property *prop, struct property_node *root,
|
||||
const void *src, const struct property_psmap *psmap);
|
||||
|
||||
struct property_node *property_node_clone(
|
||||
struct property *new_parent, int unk0,
|
||||
struct property_node *src, bool deep);
|
||||
struct property_node *property_node_create(
|
||||
struct property *prop, struct property_node *parent, int type,
|
||||
const char *key, ...);
|
||||
void property_node_name(
|
||||
struct property_node *node, char *chars, int nchars);
|
||||
const char *property_node_refdata(struct property_node *node);
|
||||
int property_node_refer(struct property *prop,
|
||||
struct property_node *node, const char *name,
|
||||
enum property_type type, void *bytes, uint32_t nbytes);
|
||||
void property_node_remove(struct property_node *node);
|
||||
enum property_type property_node_type(
|
||||
struct property_node *node);
|
||||
struct property_node *property_node_traversal(
|
||||
struct property_node *node, enum property_node_traversal direction);
|
||||
void property_node_datasize(struct property_node* node);
|
||||
|
||||
bool std_getenv(const char *key, char *val, uint32_t nbytes);
|
||||
void std_setenv(const char *key, const char *val);
|
||||
|
||||
void *avs_fs_mount (char* mountpoint, char* fsroot, void* fstype, int flags);
|
||||
|
||||
#endif
|
||||
11
src/imports/eapki.h
Normal file
11
src/imports/eapki.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef IMPORTS_EAPKI_H
|
||||
#define IMPORTS_EAPKI_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
typedef bool (*dll_entry_init_t)(char *, struct property_node *);
|
||||
typedef bool (*dll_entry_main_t)(void);
|
||||
|
||||
#endif
|
||||
5
src/imports/import_32_0_avs-ea3.def
Normal file
5
src/imports/import_32_0_avs-ea3.def
Normal file
@@ -0,0 +1,5 @@
|
||||
LIBRARY libavs-win32-ea3
|
||||
|
||||
EXPORTS
|
||||
ea3_boot
|
||||
ea3_shutdown
|
||||
36
src/imports/import_32_0_avs.def
Normal file
36
src/imports/import_32_0_avs.def
Normal file
@@ -0,0 +1,36 @@
|
||||
LIBRARY libavs-win32
|
||||
|
||||
EXPORTS
|
||||
avs_boot
|
||||
avs_net_ctrl
|
||||
avs_shutdown
|
||||
avs_thread_create
|
||||
avs_thread_destroy
|
||||
avs_thread_exit
|
||||
avs_thread_join
|
||||
log_body_fatal
|
||||
log_body_info
|
||||
log_body_misc
|
||||
log_body_warning
|
||||
log_boot
|
||||
log_change_level
|
||||
property_create
|
||||
property_desc_to_buffer
|
||||
property_destroy
|
||||
property_file_write
|
||||
property_insert_read
|
||||
property_mem_write
|
||||
property_read_query_memsize
|
||||
property_search
|
||||
property_set_flag
|
||||
property_node_clone
|
||||
property_node_create
|
||||
property_node_name
|
||||
property_node_refer
|
||||
property_node_remove
|
||||
property_node_type
|
||||
property_node_traversal
|
||||
property_node_refdata
|
||||
std_getenv
|
||||
std_setenv
|
||||
|
||||
5
src/imports/import_32_1101_avs-ea3.def
Normal file
5
src/imports/import_32_1101_avs-ea3.def
Normal file
@@ -0,0 +1,5 @@
|
||||
LIBRARY libavs-win32-ea3
|
||||
|
||||
EXPORTS
|
||||
ea3_boot @47 NONAME
|
||||
ea3_shutdown @130 NONAME
|
||||
28
src/imports/import_32_1101_avs.def
Normal file
28
src/imports/import_32_1101_avs.def
Normal file
@@ -0,0 +1,28 @@
|
||||
LIBRARY libavs-win32
|
||||
|
||||
EXPORTS
|
||||
avs_boot @22 NONAME
|
||||
avs_net_ctrl @107 NONAME
|
||||
avs_shutdown @140 NONAME
|
||||
avs_thread_create @156 NONAME
|
||||
avs_thread_destroy @158 NONAME
|
||||
avs_thread_exit @159 NONAME
|
||||
avs_thread_join @161 NONAME
|
||||
log_assert_body @196 NONAME
|
||||
log_body_misc @199 NONAME
|
||||
log_body_info @198 NONAME
|
||||
log_body_warning @200 NONAME
|
||||
log_body_fatal @197 NONAME
|
||||
property_create @245 NONAME
|
||||
property_desc_to_buffer @246 NONAME
|
||||
property_destroy @247 NONAME
|
||||
property_insert_read @255 NONAME
|
||||
property_node_create @266 NONAME
|
||||
property_node_refer @278 NONAME
|
||||
property_node_remove @279 NONAME
|
||||
property_psmap_import @288 NONAME
|
||||
property_psmap_export @287 NONAME
|
||||
property_read_query_memsize @291 NONAME
|
||||
property_search @294 NONAME
|
||||
std_getenv @308 NONAME
|
||||
std_setenv @322 NONAME
|
||||
5
src/imports/import_32_1304_avs-ea3.def
Normal file
5
src/imports/import_32_1304_avs-ea3.def
Normal file
@@ -0,0 +1,5 @@
|
||||
LIBRARY libavs-win32-ea3
|
||||
|
||||
EXPORTS
|
||||
ea3_boot @94 NONAME
|
||||
ea3_shutdown @97 NONAME
|
||||
27
src/imports/import_32_1304_avs.def
Normal file
27
src/imports/import_32_1304_avs.def
Normal file
@@ -0,0 +1,27 @@
|
||||
LIBRARY libavs-win32
|
||||
|
||||
EXPORTS
|
||||
avs_boot @237 NONAME
|
||||
avs_net_ctrl @15 NONAME
|
||||
avs_shutdown @333 NONAME
|
||||
avs_thread_create @183 NONAME
|
||||
avs_thread_destroy @76 NONAME
|
||||
avs_thread_exit @147 NONAME
|
||||
avs_thread_join @92 NONAME
|
||||
log_body_misc @44 NONAME
|
||||
log_body_info @339 NONAME
|
||||
log_body_warning @219 NONAME
|
||||
log_body_fatal @128 NONAME
|
||||
property_create @256 NONAME
|
||||
property_desc_to_buffer @201 NONAME
|
||||
property_destroy @264 NONAME
|
||||
property_insert_read @23 NONAME
|
||||
property_node_create @316 NONAME
|
||||
property_node_refer @268 NONAME
|
||||
property_node_remove @129 NONAME
|
||||
property_psmap_import @102 NONAME
|
||||
property_psmap_export @110 NONAME
|
||||
property_read_query_memsize @100 NONAME
|
||||
property_search @244 NONAME
|
||||
std_getenv @226 NONAME
|
||||
std_setenv @114 NONAME
|
||||
5
src/imports/import_32_1403_avs-ea3.def
Normal file
5
src/imports/import_32_1403_avs-ea3.def
Normal file
@@ -0,0 +1,5 @@
|
||||
LIBRARY libavs-win32-ea3
|
||||
|
||||
EXPORTS
|
||||
ea3_boot @8 NONAME
|
||||
ea3_shutdown @9 NONAME
|
||||
24
src/imports/import_32_1403_avs.def
Normal file
24
src/imports/import_32_1403_avs.def
Normal file
@@ -0,0 +1,24 @@
|
||||
LIBRARY libavs-win32
|
||||
|
||||
EXPORTS
|
||||
avs_boot @298 NONAME
|
||||
avs_net_ctrl @100 NONAME
|
||||
avs_shutdown @299 NONAME
|
||||
avs_thread_create @6 NONAME
|
||||
avs_thread_destroy @8 NONAME
|
||||
avs_thread_join @13 NONAME
|
||||
log_body_info @363 NONAME
|
||||
log_body_misc @364 NONAME
|
||||
log_body_warning @362 NONAME
|
||||
log_body_fatal @365 NONAME
|
||||
property_create @129 NONAME
|
||||
property_desc_to_buffer @131 NONAME
|
||||
property_destroy @130 NONAME
|
||||
property_insert_read @133 NONAME
|
||||
property_node_remove @148 NONAME
|
||||
property_psmap_import @163 NONAME
|
||||
property_psmap_export @164 NONAME
|
||||
property_read_query_memsize @161 NONAME
|
||||
property_search @146 NONAME
|
||||
std_getenv @208 NONAME
|
||||
std_setenv @209 NONAME
|
||||
7
src/imports/import_32_1508_avs-ea3.def
Normal file
7
src/imports/import_32_1508_avs-ea3.def
Normal file
@@ -0,0 +1,7 @@
|
||||
LIBRARY libavs-win32-ea3
|
||||
|
||||
EXPORTS
|
||||
ea3_boot @8 NONAME
|
||||
ea3_get_boot_status @11 NONAME
|
||||
ea3_get_pp_status @10 NONAME
|
||||
ea3_shutdown @9 NONAME
|
||||
32
src/imports/import_32_1508_avs.def
Normal file
32
src/imports/import_32_1508_avs.def
Normal file
@@ -0,0 +1,32 @@
|
||||
LIBRARY libavs-win32
|
||||
|
||||
EXPORTS
|
||||
avs_boot @285 NONAME
|
||||
avs_fs_close @65 NONAME
|
||||
avs_fs_lseek @59 NONAME
|
||||
avs_fs_lseek64 @60 NONAME
|
||||
avs_fs_open @58 NONAME
|
||||
avs_fs_read @61 NONAME
|
||||
avs_net_ctrl @98 NONAME
|
||||
avs_shutdown @286 NONAME
|
||||
avs_thread_create @6 NONAME
|
||||
avs_thread_destroy @8 NONAME
|
||||
avs_thread_exit @12 NONAME
|
||||
avs_thread_join @13 NONAME
|
||||
log_body_fatal @355 NONAME
|
||||
log_body_info @357 NONAME
|
||||
log_body_misc @358 NONAME
|
||||
log_body_warning @356 NONAME
|
||||
property_create @127 NONAME
|
||||
property_desc_to_buffer @129 NONAME
|
||||
property_destroy @128 NONAME
|
||||
property_insert_read @131 NONAME
|
||||
property_node_create @145 NONAME
|
||||
property_node_refer @158 NONAME
|
||||
property_node_remove @146 NONAME
|
||||
property_psmap_export @162 NONAME
|
||||
property_psmap_import @161 NONAME
|
||||
property_read_query_memsize @159 NONAME
|
||||
property_search @144 NONAME
|
||||
std_getenv @207 NONAME
|
||||
std_setenv @208 NONAME
|
||||
7
src/imports/import_32_1601_avs-ea3.def
Normal file
7
src/imports/import_32_1601_avs-ea3.def
Normal file
@@ -0,0 +1,7 @@
|
||||
LIBRARY libavs-win32-ea3
|
||||
|
||||
EXPORTS
|
||||
ea3_boot @7 NONAME
|
||||
ea3_shutdown @8 NONAME
|
||||
ea3_get_pp_status @9 NONAME
|
||||
ea3_get_boot_status @10 NONAME
|
||||
28
src/imports/import_32_1601_avs.def
Normal file
28
src/imports/import_32_1601_avs.def
Normal file
@@ -0,0 +1,28 @@
|
||||
LIBRARY libavs-win32
|
||||
|
||||
EXPORTS
|
||||
avs_thread_create @5 NONAME
|
||||
avs_thread_destroy @7 NONAME
|
||||
avs_thread_exit @11 NONAME
|
||||
avs_thread_join @12 NONAME
|
||||
avs_net_ctrl @98 NONAME
|
||||
property_create @124 NONAME
|
||||
property_destroy @125 NONAME
|
||||
property_desc_to_buffer @126 NONAME
|
||||
property_insert_read @128 NONAME
|
||||
property_search @141 NONAME
|
||||
property_node_create @142 NONAME
|
||||
property_node_remove @143 NONAME
|
||||
property_node_refer @155 NONAME
|
||||
property_read_query_memsize @156 NONAME
|
||||
property_psmap_export @159 NONAME
|
||||
property_psmap_import @158 NONAME
|
||||
std_getenv @204 NONAME
|
||||
std_setenv @205 NONAME
|
||||
avs_boot @283 NONAME
|
||||
avs_shutdown @284 NONAME
|
||||
log_body_fatal @361 NONAME
|
||||
log_body_warning @362 NONAME
|
||||
log_body_info @363 NONAME
|
||||
log_body_misc @364 NONAME
|
||||
|
||||
7
src/imports/import_32_1603_avs-ea3.def
Normal file
7
src/imports/import_32_1603_avs-ea3.def
Normal file
@@ -0,0 +1,7 @@
|
||||
LIBRARY libavs-win32-ea3
|
||||
|
||||
EXPORTS
|
||||
ea3_boot @8 NONAME
|
||||
ea3_shutdown @9 NONAME
|
||||
ea3_get_pp_status @10 NONAME
|
||||
ea3_get_boot_status @11 NONAME
|
||||
27
src/imports/import_32_1603_avs.def
Normal file
27
src/imports/import_32_1603_avs.def
Normal file
@@ -0,0 +1,27 @@
|
||||
LIBRARY libavs-win32
|
||||
|
||||
EXPORTS
|
||||
avs_thread_create @5 NONAME
|
||||
avs_thread_destroy @7 NONAME
|
||||
avs_thread_exit @11 NONAME
|
||||
avs_thread_join @12 NONAME
|
||||
avs_net_ctrl @119 NONAME
|
||||
property_create @145 NONAME
|
||||
property_destroy @146 NONAME
|
||||
property_desc_to_buffer @147 NONAME
|
||||
property_insert_read @149 NONAME
|
||||
property_search @162 NONAME
|
||||
property_node_create @163 NONAME
|
||||
property_node_remove @164 NONAME
|
||||
property_node_refer @176 NONAME
|
||||
property_read_query_memsize @177 NONAME
|
||||
property_psmap_import @179 NONAME
|
||||
property_psmap_export @180 NONAME
|
||||
std_getenv @212 NONAME
|
||||
std_setenv @213 NONAME
|
||||
avs_boot @298 NONAME
|
||||
avs_shutdown @299 NONAME
|
||||
log_body_fatal @379 NONAME
|
||||
log_body_warning @380 NONAME
|
||||
log_body_info @381 NONAME
|
||||
log_body_misc @382 NONAME
|
||||
5
src/imports/import_32_1700_avs-ea3.def
Normal file
5
src/imports/import_32_1700_avs-ea3.def
Normal file
@@ -0,0 +1,5 @@
|
||||
LIBRARY avs2-ea3
|
||||
|
||||
EXPORTS
|
||||
ea3_boot @37 NONAME
|
||||
ea3_shutdown @38 NONAME
|
||||
28
src/imports/import_32_1700_avs.def
Normal file
28
src/imports/import_32_1700_avs.def
Normal file
@@ -0,0 +1,28 @@
|
||||
LIBRARY avs2-core
|
||||
|
||||
EXPORTS
|
||||
avs_thread_create @5 NONAME
|
||||
avs_thread_destroy @7 NONAME
|
||||
avs_thread_exit @11 NONAME
|
||||
avs_thread_join @12 NONAME
|
||||
avs_fs_mount @76 NONAME
|
||||
avs_net_ctrl @119 NONAME
|
||||
property_create @145 NONAME
|
||||
property_destroy @146 NONAME
|
||||
property_desc_to_buffer @147 NONAME
|
||||
property_insert_read @149 NONAME
|
||||
property_search @162 NONAME
|
||||
property_node_create @163 NONAME
|
||||
property_node_remove @164 NONAME
|
||||
property_node_refer @176 NONAME
|
||||
property_read_query_memsize @177 NONAME
|
||||
property_psmap_import @179 NONAME
|
||||
property_psmap_export @180 NONAME
|
||||
std_getenv @212 NONAME
|
||||
std_setenv @213 NONAME
|
||||
avs_boot @298 NONAME
|
||||
avs_shutdown @299 NONAME
|
||||
log_body_fatal @379 NONAME
|
||||
log_body_warning @380 NONAME
|
||||
log_body_info @381 NONAME
|
||||
log_body_misc @382 NONAME
|
||||
5
src/imports/import_32_803_avs-ea3.def
Normal file
5
src/imports/import_32_803_avs-ea3.def
Normal file
@@ -0,0 +1,5 @@
|
||||
LIBRARY libavs-win32-ea3
|
||||
|
||||
EXPORTS
|
||||
ea3_boot
|
||||
ea3_shutdown
|
||||
37
src/imports/import_32_803_avs.def
Normal file
37
src/imports/import_32_803_avs.def
Normal file
@@ -0,0 +1,37 @@
|
||||
LIBRARY libavs-win32
|
||||
|
||||
EXPORTS
|
||||
avs_boot
|
||||
avs_net_ctrl
|
||||
avs_shutdown
|
||||
avs_thread_create
|
||||
avs_thread_destroy
|
||||
avs_thread_exit
|
||||
avs_thread_join
|
||||
log_body_fatal
|
||||
log_body_info
|
||||
log_body_misc
|
||||
log_body_warning
|
||||
log_boot
|
||||
log_change_level
|
||||
property_create
|
||||
property_desc_to_buffer
|
||||
property_destroy
|
||||
property_file_write
|
||||
property_insert_read
|
||||
property_mem_write
|
||||
property_read_query_memsize
|
||||
property_search
|
||||
property_set_flag
|
||||
property_node_clone
|
||||
property_node_create
|
||||
property_node_datasize
|
||||
property_node_name
|
||||
property_node_refer
|
||||
property_node_remove
|
||||
property_node_type
|
||||
property_node_traversal
|
||||
property_node_refdata
|
||||
std_getenv
|
||||
std_setenv
|
||||
|
||||
17
src/imports/import_32_indep_SMX.def
Normal file
17
src/imports/import_32_indep_SMX.def
Normal file
@@ -0,0 +1,17 @@
|
||||
LIBRARY SMX
|
||||
|
||||
EXPORTS
|
||||
SMX_Start
|
||||
SMX_Stop
|
||||
SMX_SetLogCallback
|
||||
SMX_GetInfo
|
||||
SMX_GetInputState
|
||||
SMX_SetLights
|
||||
SMX_ReenableAutoLights
|
||||
SMX_GetConfig
|
||||
SMX_SetConfig
|
||||
SMX_FactoryReset
|
||||
SMX_ForceRecalibration
|
||||
SMX_SetTestMode
|
||||
SMX_GetTestData
|
||||
SMX_Version
|
||||
7
src/imports/import_64_1508_avs-ea3.def
Normal file
7
src/imports/import_64_1508_avs-ea3.def
Normal file
@@ -0,0 +1,7 @@
|
||||
LIBRARY libavs-win64-ea3
|
||||
|
||||
EXPORTS
|
||||
ea3_boot @8 NONAME
|
||||
ea3_get_boot_status @11 NONAME
|
||||
ea3_get_pp_status @10 NONAME
|
||||
ea3_shutdown @9 NONAME
|
||||
32
src/imports/import_64_1508_avs.def
Normal file
32
src/imports/import_64_1508_avs.def
Normal file
@@ -0,0 +1,32 @@
|
||||
LIBRARY libavs-win64
|
||||
|
||||
EXPORTS
|
||||
avs_boot @285 NONAME
|
||||
avs_fs_close @65 NONAME
|
||||
avs_fs_lseek @59 NONAME
|
||||
avs_fs_lseek64 @60 NONAME
|
||||
avs_fs_open @58 NONAME
|
||||
avs_fs_read @61 NONAME
|
||||
avs_net_ctrl @98 NONAME
|
||||
avs_shutdown @286 NONAME
|
||||
avs_thread_create @6 NONAME
|
||||
avs_thread_destroy @8 NONAME
|
||||
avs_thread_exit @12 NONAME
|
||||
avs_thread_join @13 NONAME
|
||||
log_body_fatal @355 NONAME
|
||||
log_body_info @357 NONAME
|
||||
log_body_misc @358 NONAME
|
||||
log_body_warning @356 NONAME
|
||||
property_create @127 NONAME
|
||||
property_desc_to_buffer @129 NONAME
|
||||
property_destroy @128 NONAME
|
||||
property_insert_read @131 NONAME
|
||||
property_node_create @145 NONAME
|
||||
property_node_refer @158 NONAME
|
||||
property_node_remove @146 NONAME
|
||||
property_psmap_export @162 NONAME
|
||||
property_psmap_import @161 NONAME
|
||||
property_read_query_memsize @159 NONAME
|
||||
property_search @144 NONAME
|
||||
std_getenv @207 NONAME
|
||||
std_setenv @208 NONAME
|
||||
7
src/imports/import_64_1601_avs-ea3.def
Normal file
7
src/imports/import_64_1601_avs-ea3.def
Normal file
@@ -0,0 +1,7 @@
|
||||
LIBRARY libavs-win64-ea3
|
||||
|
||||
EXPORTS
|
||||
ea3_boot @7 NONAME
|
||||
ea3_shutdown @8 NONAME
|
||||
ea3_get_pp_status @9 NONAME
|
||||
ea3_get_boot_status @10 NONAME
|
||||
28
src/imports/import_64_1601_avs.def
Normal file
28
src/imports/import_64_1601_avs.def
Normal file
@@ -0,0 +1,28 @@
|
||||
LIBRARY libavs-win64
|
||||
|
||||
EXPORTS
|
||||
avs_thread_create @5 NONAME
|
||||
avs_thread_destroy @7 NONAME
|
||||
avs_thread_exit @11 NONAME
|
||||
avs_thread_join @12 NONAME
|
||||
avs_net_ctrl @98 NONAME
|
||||
property_create @124 NONAME
|
||||
property_destroy @125 NONAME
|
||||
property_desc_to_buffer @126 NONAME
|
||||
property_insert_read @128 NONAME
|
||||
property_search @141 NONAME
|
||||
property_node_create @142 NONAME
|
||||
property_node_remove @143 NONAME
|
||||
property_node_refer @155 NONAME
|
||||
property_read_query_memsize @156 NONAME
|
||||
property_psmap_export @159 NONAME
|
||||
property_psmap_import @158 NONAME
|
||||
std_getenv @204 NONAME
|
||||
std_setenv @205 NONAME
|
||||
avs_boot @283 NONAME
|
||||
avs_shutdown @284 NONAME
|
||||
log_body_fatal @361 NONAME
|
||||
log_body_warning @362 NONAME
|
||||
log_body_info @363 NONAME
|
||||
log_body_misc @364 NONAME
|
||||
|
||||
7
src/imports/import_64_1603_avs-ea3.def
Normal file
7
src/imports/import_64_1603_avs-ea3.def
Normal file
@@ -0,0 +1,7 @@
|
||||
LIBRARY libavs-win64-ea3
|
||||
|
||||
EXPORTS
|
||||
ea3_boot @8 NONAME
|
||||
ea3_shutdown @9 NONAME
|
||||
ea3_get_pp_status @10 NONAME
|
||||
ea3_get_boot_status @11 NONAME
|
||||
27
src/imports/import_64_1603_avs.def
Normal file
27
src/imports/import_64_1603_avs.def
Normal file
@@ -0,0 +1,27 @@
|
||||
LIBRARY libavs-win64
|
||||
|
||||
EXPORTS
|
||||
avs_thread_create @5 NONAME
|
||||
avs_thread_destroy @7 NONAME
|
||||
avs_thread_exit @11 NONAME
|
||||
avs_thread_join @12 NONAME
|
||||
avs_net_ctrl @119 NONAME
|
||||
property_create @145 NONAME
|
||||
property_destroy @146 NONAME
|
||||
property_desc_to_buffer @147 NONAME
|
||||
property_insert_read @149 NONAME
|
||||
property_search @162 NONAME
|
||||
property_node_create @163 NONAME
|
||||
property_node_remove @164 NONAME
|
||||
property_node_refer @176 NONAME
|
||||
property_read_query_memsize @177 NONAME
|
||||
property_psmap_import @179 NONAME
|
||||
property_psmap_export @180 NONAME
|
||||
std_getenv @212 NONAME
|
||||
std_setenv @213 NONAME
|
||||
avs_boot @298 NONAME
|
||||
avs_shutdown @299 NONAME
|
||||
log_body_fatal @379 NONAME
|
||||
log_body_warning @380 NONAME
|
||||
log_body_info @381 NONAME
|
||||
log_body_misc @382 NONAME
|
||||
5
src/imports/import_64_1700_avs-ea3.def
Normal file
5
src/imports/import_64_1700_avs-ea3.def
Normal file
@@ -0,0 +1,5 @@
|
||||
LIBRARY avs2-ea3
|
||||
|
||||
EXPORTS
|
||||
ea3_boot @37 NONAME
|
||||
ea3_shutdown @38 NONAME
|
||||
28
src/imports/import_64_1700_avs.def
Normal file
28
src/imports/import_64_1700_avs.def
Normal file
@@ -0,0 +1,28 @@
|
||||
LIBRARY avs2-core
|
||||
|
||||
EXPORTS
|
||||
avs_thread_create @5 NONAME
|
||||
avs_thread_destroy @7 NONAME
|
||||
avs_thread_exit @11 NONAME
|
||||
avs_thread_join @12 NONAME
|
||||
avs_fs_mount @76 NONAME
|
||||
avs_net_ctrl @119 NONAME
|
||||
property_create @145 NONAME
|
||||
property_destroy @146 NONAME
|
||||
property_desc_to_buffer @147 NONAME
|
||||
property_insert_read @149 NONAME
|
||||
property_search @162 NONAME
|
||||
property_node_create @163 NONAME
|
||||
property_node_remove @164 NONAME
|
||||
property_node_refer @176 NONAME
|
||||
property_read_query_memsize @177 NONAME
|
||||
property_psmap_import @179 NONAME
|
||||
property_psmap_export @180 NONAME
|
||||
std_getenv @212 NONAME
|
||||
std_setenv @213 NONAME
|
||||
avs_boot @298 NONAME
|
||||
avs_shutdown @299 NONAME
|
||||
log_body_fatal @379 NONAME
|
||||
log_body_warning @380 NONAME
|
||||
log_body_info @381 NONAME
|
||||
log_body_misc @382 NONAME
|
||||
86
src/main/acio/acio.h
Normal file
86
src/main/acio/acio.h
Normal 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
src/main/acio/h44b.h
Normal file
19
src/main/acio/h44b.h
Normal 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
src/main/acio/icca.h
Normal file
68
src/main/acio/icca.h
Normal 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
src/main/acio/iccb.h
Normal file
48
src/main/acio/iccb.h
Normal 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
src/main/acio/kfca.h
Normal file
46
src/main/acio/kfca.h
Normal 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
src/main/aciodrv/Module.mk
Normal file
9
src/main/aciodrv/Module.mk
Normal file
@@ -0,0 +1,9 @@
|
||||
libs += aciodrv
|
||||
|
||||
libs_aciodrv := \
|
||||
|
||||
src_aciodrv := \
|
||||
device.c \
|
||||
icca.c \
|
||||
port.c \
|
||||
|
||||
335
src/main/aciodrv/device.c
Normal file
335
src/main/aciodrv/device.c
Normal 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
src/main/aciodrv/device.h
Normal file
52
src/main/aciodrv/device.h
Normal 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
src/main/aciodrv/icca.c
Normal file
107
src/main/aciodrv/icca.c
Normal 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
src/main/aciodrv/icca.h
Normal file
60
src/main/aciodrv/icca.h
Normal 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
src/main/aciodrv/port.c
Normal file
157
src/main/aciodrv/port.c
Normal 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
src/main/aciodrv/port.h
Normal file
42
src/main/aciodrv/port.h
Normal 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
src/main/acioemu/Module.mk
Normal file
11
src/main/acioemu/Module.mk
Normal file
@@ -0,0 +1,11 @@
|
||||
libs += acioemu
|
||||
|
||||
src_acioemu := \
|
||||
addr.c \
|
||||
emu.c \
|
||||
h44b.c \
|
||||
hdxs.c \
|
||||
icca.c \
|
||||
iccb.c \
|
||||
pipe.c \
|
||||
|
||||
39
src/main/acioemu/addr.c
Normal file
39
src/main/acioemu/addr.c
Normal 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
src/main/acioemu/addr.h
Normal file
15
src/main/acioemu/addr.h
Normal 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
src/main/acioemu/emu.c
Normal file
194
src/main/acioemu/emu.c
Normal 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
src/main/acioemu/emu.h
Normal file
39
src/main/acioemu/emu.h
Normal 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
src/main/acioemu/h44b.c
Normal file
118
src/main/acioemu/h44b.c
Normal 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
src/main/acioemu/h44b.h
Normal file
26
src/main/acioemu/h44b.h
Normal 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
src/main/acioemu/hdxs.c
Normal file
133
src/main/acioemu/hdxs.c
Normal 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
src/main/acioemu/hdxs.h
Normal file
21
src/main/acioemu/hdxs.h
Normal 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
src/main/acioemu/icca.c
Normal file
385
src/main/acioemu/icca.c
Normal 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
src/main/acioemu/icca.h
Normal file
34
src/main/acioemu/icca.h
Normal 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
src/main/acioemu/iccb.c
Normal file
227
src/main/acioemu/iccb.c
Normal 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
src/main/acioemu/iccb.h
Normal file
27
src/main/acioemu/iccb.h
Normal 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
src/main/acioemu/pipe.c
Normal file
366
src/main/acioemu/pipe.c
Normal 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
src/main/acioemu/pipe.h
Normal file
66
src/main/acioemu/pipe.h
Normal 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
src/main/aciotest/Module.mk
Normal file
9
src/main/aciotest/Module.mk
Normal file
@@ -0,0 +1,9 @@
|
||||
exes += aciotest
|
||||
|
||||
libs_aciotest := \
|
||||
aciodrv \
|
||||
util \
|
||||
|
||||
src_aciotest := \
|
||||
icca.c \
|
||||
main.c \
|
||||
16
src/main/aciotest/handler.h
Normal file
16
src/main/aciotest/handler.h
Normal 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
src/main/aciotest/icca.c
Normal file
83
src/main/aciotest/icca.c
Normal 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
src/main/aciotest/icca.h
Normal file
10
src/main/aciotest/icca.h
Normal 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
src/main/aciotest/main.c
Normal file
120
src/main/aciotest/main.c
Normal 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
src/main/bemanitools/bstio.h
Normal file
49
src/main/bemanitools/bstio.h
Normal 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
src/main/bemanitools/ddrio.h
Normal file
84
src/main/bemanitools/ddrio.h
Normal 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
src/main/bemanitools/eamio.h
Normal file
132
src/main/bemanitools/eamio.h
Normal 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
src/main/bemanitools/glue.h
Normal file
47
src/main/bemanitools/glue.h
Normal 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
src/main/bemanitools/iidxio.h
Normal file
155
src/main/bemanitools/iidxio.h
Normal 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
src/main/bemanitools/input.h
Normal file
87
src/main/bemanitools/input.h
Normal 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
src/main/bemanitools/jbio.h
Normal file
83
src/main/bemanitools/jbio.h
Normal 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
src/main/bemanitools/sdvxio.h
Normal file
95
src/main/bemanitools/sdvxio.h
Normal 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
src/main/bemanitools/vefxio.h
Normal file
56
src/main/bemanitools/vefxio.h
Normal 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
src/main/bsthook/Module.mk
Normal file
20
src/main/bsthook/Module.mk
Normal 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
src/main/bsthook/acio.c
Normal file
93
src/main/bsthook/acio.c
Normal 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
src/main/bsthook/acio.h
Normal file
12
src/main/bsthook/acio.h
Normal 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
src/main/bsthook/bsthook.def
Normal file
4
src/main/bsthook/bsthook.def
Normal file
@@ -0,0 +1,4 @@
|
||||
LIBRARY bsthook
|
||||
|
||||
EXPORTS
|
||||
DllMain@12 @1 NONAME
|
||||
136
src/main/bsthook/dllmain.c
Normal file
136
src/main/bsthook/dllmain.c
Normal 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
src/main/bsthook/gfx.c
Normal file
83
src/main/bsthook/gfx.c
Normal 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
src/main/bsthook/gfx.h
Normal file
7
src/main/bsthook/gfx.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef BSTHOOK_GFX_H
|
||||
#define BSTHOOK_GFX_H
|
||||
|
||||
void gfx_init(void);
|
||||
void gfx_set_windowed(void);
|
||||
|
||||
#endif
|
||||
136
src/main/bsthook/kfca.c
Normal file
136
src/main/bsthook/kfca.c
Normal 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
src/main/bsthook/kfca.h
Normal file
11
src/main/bsthook/kfca.h
Normal 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
src/main/bsthook/settings.c
Normal file
91
src/main/bsthook/settings.c
Normal 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
src/main/bsthook/settings.h
Normal file
10
src/main/bsthook/settings.h
Normal 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
src/main/bstio/Module.mk
Normal file
8
src/main/bstio/Module.mk
Normal file
@@ -0,0 +1,8 @@
|
||||
dlls += bstio
|
||||
|
||||
libs_bstio := \
|
||||
geninput \
|
||||
|
||||
src_bstio := \
|
||||
bstio.c \
|
||||
|
||||
42
src/main/bstio/bstio.c
Normal file
42
src/main/bstio/bstio.c
Normal 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
src/main/bstio/bstio.def
Normal file
8
src/main/bstio/bstio.def
Normal 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
src/main/cconfig/Module.mk
Normal file
11
src/main/cconfig/Module.mk
Normal 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
src/main/cconfig/cconfig-hook.c
Normal file
96
src/main/cconfig/cconfig-hook.c
Normal 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
src/main/cconfig/cconfig-hook.h
Normal file
10
src/main/cconfig/cconfig-hook.h
Normal 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
src/main/cconfig/cconfig-util.c
Normal file
211
src/main/cconfig/cconfig-util.c
Normal 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
src/main/cconfig/cconfig-util.h
Normal file
44
src/main/cconfig/cconfig-util.h
Normal 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
src/main/cconfig/cconfig.c
Normal file
109
src/main/cconfig/cconfig.c
Normal 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
src/main/cconfig/cconfig.h
Normal file
31
src/main/cconfig/cconfig.h
Normal 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
src/main/cconfig/cmd.c
Normal file
132
src/main/cconfig/cmd.c
Normal 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
src/main/cconfig/cmd.h
Normal file
21
src/main/cconfig/cmd.h
Normal 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
src/main/cconfig/conf.c
Normal file
117
src/main/cconfig/conf.c
Normal 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
src/main/cconfig/conf.h
Normal file
16
src/main/cconfig/conf.h
Normal 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
src/main/config/Module.mk
Normal file
30
src/main/config/Module.mk
Normal 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
src/main/config/analogs.c
Normal file
540
src/main/config/analogs.c
Normal 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
src/main/config/bind-adv.c
Normal file
530
src/main/config/bind-adv.c
Normal 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);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user