Let us start here, from square one. No, from Zero!

This commit is contained in:
smpn2
2022-09-30 22:45:51 +09:00
parent 14215af0d1
commit b7a60638a4
1005 changed files with 303069 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
#define APSTUDIO_HIDDEN_SYMBOLS
#include "windows.h"
#undef APSTUDIO_HIDDEN_SYMBOLS
#undef APSTUDIO_READONLY_SYMBOLS
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
IDR_CHANGELOG RCDATA "../changelog.txt"
IDR_LICENSES RCDATA "../licenses.txt"
IDR_PATCHES RCDATA "../build/patches.json"
IDR_README RCDATA "../readme.txt"
#endif
+160
View File
@@ -0,0 +1,160 @@
#include "analog.h"
#include <numeric>
#include <math.h>
#include "rawinput/rawinput.h"
#include "util/logging.h"
#include "util/time.h"
#include "util/utils.h"
std::string Analog::getDisplayString(rawinput::RawInputManager *manager) {
// device must be existing
if (this->device_identifier.empty()) {
return "";
}
// get index string
auto index = this->getIndex();
std::string indexString = fmt::format("{:#x}", index);
// get device
auto device = manager->devices_get(this->device_identifier);
if (!device) {
return "Device missing (" + indexString + ")";
}
// return string based on device type
switch (device->type) {
case rawinput::MOUSE: {
const char *name;
switch (index) {
case rawinput::MOUSEPOS_X:
name = "X";
break;
case rawinput::MOUSEPOS_Y:
name = "Y";
break;
case rawinput::MOUSEPOS_WHEEL:
name = "Scroll Wheel";
break;
default:
name = "?";
break;
}
return fmt::format("{} ({})", name, device->desc);
}
case rawinput::HID: {
auto hid = device->hidInfo;
if (index < hid->value_caps_names.size()) {
return hid->value_caps_names[index] + " (" + device->desc + ")";
}
return "Invalid Axis (" + indexString + ")";
}
case rawinput::MIDI: {
auto midi = device->midiInfo;
if (index < midi->controls_precision.size()) {
return "MIDI PREC " + indexString + " (" + device->desc + ")";
} else if (index < midi->controls_precision.size() + midi->controls_single.size()) {
return "MIDI CTRL " + indexString + " (" + device->desc + ")";
} else if (index < midi->controls_precision.size() + midi->controls_single.size()
+ midi->controls_onoff.size())
{
return "MIDI ONOFF " + indexString + " (" + device->desc + ")";
} else if (index == midi->controls_precision.size() + midi->controls_single.size()
+ midi->controls_onoff.size())
{
return "MIDI Pitch Bend (" + device->desc + ")";
} else {
return "MIDI Unknown " + indexString + " (" + device->desc + ")";
}
}
case rawinput::DESTROYED:
return "Device unplugged (" + indexString + ")";
default:
return "Unknown Axis (" + indexString + ")";
}
}
float Analog::getSmoothedValue(float raw_rads) {
auto now = get_performance_milliseconds();
// prevent extremely frequent polling
if ((now - vector_history.at(vector_history_index).time_in_ms) < 0.9) {
return smoothed_last_state;
}
// calculate derived values for the newly-read analog value
vector_history_index = (vector_history_index + 1) % vector_history.size();
auto &current = vector_history.at(vector_history_index);
current.time_in_ms = now;
current.sine = sin(raw_rads);
current.cosine = cos(raw_rads);
// calculated the weighted sum of sines and cosines
auto sines = 0.f;
auto cosines = 0.f;
for (auto &vector : vector_history) {
auto time_diff = now - vector.time_in_ms;
// time from QPC should never roll backwards, but just in case
if (time_diff < 0.f) {
time_diff = 0.f;
}
// the weight falls of linearly; value from 24ms ago counts as half, 48ms ago counts as 0
double weight = (-time_diff / 48.f) + 1.f;
if (weight > 0.f) {
sines += weight * vector.sine;
cosines += weight * vector.cosine;
}
}
// add a tiny bit so that cosine is never 0.0f when fed to atan2
if (cosines == 0.f) {
cosines = std::nextafter(0.f, 1.f);
}
// average for angles:
// arctan[(sum of sines of all angles) / (sum of cosines of all angles)]
// atan2 will give [-pi, +pi], so normalize to make [0, 2pi]
smoothed_last_state = normalizeAngle(atan2(sines, cosines));
return smoothed_last_state;
}
float Analog::calculateAngularDifference(float old_rads, float new_rads) {
float delta = new_rads - old_rads;
// assumes value doesn't change more than PI (180 deg) compared to last poll
if (std::abs(delta) < M_PI) {
return delta;
} else {
// use the coterminal angle instead
if (delta < 0.f) {
return M_TAU + delta;
} else {
return -(M_TAU - delta);
}
}
}
float Analog::applyAngularSensitivity(float raw_rads) {
float delta = calculateAngularDifference(previous_raw_rads, raw_rads);
previous_raw_rads = raw_rads;
adjusted_rads = normalizeAngle(adjusted_rads + (delta * sensitivity));
return adjusted_rads;
}
float Analog::normalizeAngle(float rads) {
// normalizes radian value into [0, 2pi] range.
// for small angles, this is MUCH faster than fmodf.
float angle = rads;
while (angle > M_TAU) {
angle -= M_TAU;
}
while (angle < 0.f) {
angle += M_TAU;
}
return angle;
}
+154
View File
@@ -0,0 +1,154 @@
#pragma once
#include <array>
#include <string>
#include <cmath>
#define ANALOG_HISTORY_CNT 10
#define M_TAU (2 * M_PI)
#define M_1_TAU (0.5 * M_1_PI)
namespace rawinput {
class RawInputManager;
}
struct AnalogMovingAverage {
double time_in_ms;
float sine;
float cosine;
};
class Analog {
private:
std::string name;
std::string device_identifier = "";
unsigned short index = 0xFF;
float sensitivity = 1.f;
float deadzone = 0.f;
bool deadzone_mirror = false;
bool invert = false;
float last_state = 0.5f;
bool sensitivity_set = false;
bool deadzone_set = false;
// smoothing function
bool smoothing = false;
std::array<AnalogMovingAverage, ANALOG_HISTORY_CNT> vector_history;
int vector_history_index = 0;
float smoothed_last_state = 0.f;
// angular scaling for sensitivity
float previous_raw_rads = 0.f;
float adjusted_rads = 0.f;
float calculateAngularDifference(float old_rads, float new_rads);
float normalizeAngle(float rads);
public:
// overrides
bool override_enabled = false;
float override_state = 0.5f;
explicit Analog(std::string name) : name(std::move(name)) {
vector_history.fill({0.0, 0.f, 0.f});
};
std::string getDisplayString(rawinput::RawInputManager* manager);
float getSmoothedValue(float raw_rads);
float applyAngularSensitivity(float raw_rads);
inline bool isSet() {
if (this->override_enabled) {
return true;
}
return this->index != 0xFF;
}
inline void clearBindings() {
device_identifier = "";
index = 0xFF;
setSensitivity(1.f);
setDeadzone(0.f);
invert = false;
smoothing = false;
}
inline const std::string &getName() const {
return this->name;
}
inline const std::string &getDeviceIdentifier() const {
return this->device_identifier;
}
inline void setDeviceIdentifier(std::string device_identifier) {
this->device_identifier = std::move(device_identifier);
}
inline unsigned short getIndex() const {
return this->index;
}
inline void setIndex(unsigned short index) {
this->index = index;
}
inline float getSensitivity() const {
return this->sensitivity;
}
inline float getDeadzone() const {
return this->deadzone;
}
inline void setSensitivity(float sensitivity) {
this->sensitivity = sensitivity;
this->sensitivity_set = (sensitivity < 0.99f || 1.01f < sensitivity);
}
inline void setDeadzone(float deadzone) {
this->deadzone = std::max(-1.f, std::min(1.f, deadzone));
this->deadzone_set = fabsf(deadzone) > 0.01f;
}
inline bool isSensitivitySet() {
return this->sensitivity_set;
}
inline bool isDeadzoneSet() {
return this->deadzone_set;
}
inline bool getDeadzoneMirror() const {
return this->deadzone_mirror;
}
inline void setDeadzoneMirror(bool deadzone_mirror) {
this->deadzone_mirror = deadzone_mirror;
}
inline bool getInvert() const {
return this->invert;
}
inline void setInvert(bool invert) {
this->invert = invert;
}
inline bool getSmoothing() const {
return this->smoothing;
}
inline void setSmoothing(bool smoothing) {
this->smoothing = smoothing;
}
inline float getLastState() const {
return this->last_state;
}
inline void setLastState(float last_state) {
this->last_state = last_state;
}
};
+924
View File
@@ -0,0 +1,924 @@
#include "api.h"
#include "rawinput/rawinput.h"
#include "rawinput/piuio.h"
#include "util/time.h"
#include "util/utils.h"
#include "config.h"
using namespace GameAPI;
std::vector<Button> GameAPI::Buttons::getButtons(const std::string &game_name) {
return Config::getInstance().getButtons(game_name);
}
std::vector<Button> GameAPI::Buttons::getButtons(Game *game) {
return Config::getInstance().getButtons(game);
}
std::vector<Button> GameAPI::Buttons::sortButtons(
const std::vector<Button> &buttons,
const std::vector<std::string> &button_names,
const std::vector<unsigned short> *vkey_defaults)
{
std::vector<Button> sorted;
bool button_found;
int index = 0;
for (auto &name : button_names) {
button_found = false;
for (auto &bt : buttons) {
if (name == bt.getName()) {
button_found = true;
sorted.push_back(bt);
break;
}
}
if (!button_found) {
auto &button = sorted.emplace_back(name);
if (vkey_defaults) {
button.setVKey(vkey_defaults->at(index));
}
}
++index;
}
return sorted;
}
GameAPI::Buttons::State GameAPI::Buttons::getState(rawinput::RawInputManager *manager, Button &_button, bool check_alts) {
// check override
if (_button.override_enabled) {
return _button.override_state;
}
// for iterating button alternatives
auto current_button = &_button;
auto alternatives = check_alts ? &current_button->getAlternatives() : nullptr;
unsigned int button_count = 0;
while (true) {
// naive behavior
if (current_button->isNaive()) {
// read
auto vkey = current_button->getVKey();
GameAPI::Buttons::State state;
if (vkey == 0xFF) {
state = BUTTON_NOT_PRESSED;
} else {
state = (GetAsyncKeyState(current_button->getVKey()) & 0x8000) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
}
// invert
if (current_button->getInvert()) {
if (state == BUTTON_PRESSED) {
state = BUTTON_NOT_PRESSED;
} else {
state = BUTTON_PRESSED;
}
}
// return state
if (state != BUTTON_NOT_PRESSED) {
return state;
}
// get next button
button_count++;
if (!alternatives || alternatives->empty() || button_count - 1 >= alternatives->size()) {
return BUTTON_NOT_PRESSED;
} else {
current_button = &alternatives->at(button_count - 1);
continue;
}
}
// get device
auto &devid = current_button->getDeviceIdentifier();
auto device = manager->devices_get(devid, false); // TODO: fix to update only
// get state if device was marked as updated
GameAPI::Buttons::State state = current_button->getLastState();
double *last_up = nullptr;
double *last_down = nullptr;
if (device) {
// lock device
device->mutex->lock();
// get vkey
auto vKey = current_button->getVKey();
// update state based on device type
switch (device->type) {
case rawinput::MOUSE: {
if (vKey < sizeof(device->mouseInfo->key_states)) {
auto mouse = device->mouseInfo;
state = mouse->key_states[vKey] ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
last_up = &mouse->key_up[vKey];
last_down = &mouse->key_down[vKey];
}
break;
}
case rawinput::KEYBOARD: {
if (vKey < sizeof(device->keyboardInfo->key_states)) {
auto kb = device->keyboardInfo;
state = kb->key_states[vKey] ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
last_up = &kb->key_up[vKey];
last_down = &kb->key_down[vKey];
}
break;
}
case rawinput::HID: {
auto hid = device->hidInfo;
auto bat = current_button->getAnalogType();
switch (bat) {
case BAT_NONE: {
auto button_states_it = hid->button_states.begin();
auto button_up_it = hid->button_up.begin();
auto button_down_it = hid->button_down.begin();
while (button_states_it != hid->button_states.end()) {
auto size = button_states_it->size();
if (vKey < size) {
state = (*button_states_it)[vKey] ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
last_up = &(*button_up_it)[vKey];
last_down = &(*button_down_it)[vKey];
break;
} else {
vKey -= size;
++button_states_it;
++button_up_it;
++button_down_it;
}
}
break;
}
case BAT_NEGATIVE:
case BAT_POSITIVE: {
auto value_states = &hid->value_states;
if (vKey < value_states->size()) {
auto value = value_states->at(vKey);
if (current_button->getAnalogType() == BAT_POSITIVE) {
state = value > 0.6f ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
} else {
state = value < 0.4f ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
}
} else {
state = BUTTON_NOT_PRESSED;
}
break;
}
case BAT_HS_UP:
case BAT_HS_UPRIGHT:
case BAT_HS_RIGHT:
case BAT_HS_DOWNRIGHT:
case BAT_HS_DOWN:
case BAT_HS_DOWNLEFT:
case BAT_HS_LEFT:
case BAT_HS_UPLEFT:
case BAT_HS_NEUTRAL: {
auto &value_states = hid->value_states;
if (vKey < value_states.size()) {
auto value = value_states.at(vKey);
// get hat switch values
ButtonAnalogType buffer[3];
Button::getHatSwitchValues(value, buffer);
// check if one of the values match our analog type
state = BUTTON_NOT_PRESSED;
for (ButtonAnalogType &buffer_bat : buffer) {
if (buffer_bat == bat) {
state = BUTTON_PRESSED;
break;
}
}
} else
state = BUTTON_NOT_PRESSED;
break;
}
default:
state = BUTTON_NOT_PRESSED;
break;
}
break;
}
case rawinput::MIDI: {
auto bat = current_button->getAnalogType();
auto midi = device->midiInfo;
switch (bat) {
case BAT_NONE: {
if (vKey < 16 * 128) {
// check for event
auto midi_event = midi->states_events[vKey];
if (midi_event) {
// choose state based on event
state = (midi_event % 2) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
// update event
if (!midi->states[vKey] || midi_event > 1)
midi->states_events[vKey]--;
} else
state = BUTTON_NOT_PRESSED;
}
break;
}
case BAT_MIDI_CTRL_PRECISION: {
if (vKey < 16 * 32)
state = midi->controls_precision[vKey] > 0 ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
else
state = BUTTON_NOT_PRESSED;
break;
}
case BAT_MIDI_CTRL_SINGLE: {
if (vKey < 16 * 44)
state = midi->controls_single[vKey] > 0 ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
else
state = BUTTON_NOT_PRESSED;
break;
}
case BAT_MIDI_CTRL_ONOFF: {
if (vKey < 16 * 6)
state = midi->controls_onoff[vKey] ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
else
state = BUTTON_NOT_PRESSED;
break;
}
case BAT_MIDI_PITCH_DOWN:
state = midi->pitch_bend < 0x2000 ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
break;
case BAT_MIDI_PITCH_UP:
state = midi->pitch_bend > 0x2000 ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
break;
default: {
state = BUTTON_NOT_PRESSED;
break;
}
}
break;
}
case rawinput::PIUIO_DEVICE: {
state = device->piuioDev->IsPressed(vKey) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
}
default:
break;
}
// unlock device
device->mutex->unlock();
}
// debounce
if (state == BUTTON_NOT_PRESSED) {
if (last_up) {
auto debounce_up = current_button->getDebounceUp();
if (debounce_up > 0.0 && get_performance_seconds() - *last_up < debounce_up) {
state = BUTTON_PRESSED;
}
}
} else {
if (last_down) {
auto debounce_down = current_button->getDebounceDown();
if (debounce_down > 0.0 && get_performance_seconds() - *last_down < debounce_down) {
state = BUTTON_NOT_PRESSED;
}
}
}
// set last state
current_button->setLastState(state);
// invert
if (current_button->getInvert()) {
if (state == BUTTON_PRESSED) {
state = BUTTON_NOT_PRESSED;
} else {
state = BUTTON_PRESSED;
}
}
// early quit
if (state == BUTTON_PRESSED) {
return state;
}
// get next button
button_count++;
if (!alternatives || alternatives->empty() || button_count - 1 >= alternatives->size()) {
return BUTTON_NOT_PRESSED;
} else {
current_button = &alternatives->at(button_count - 1);
}
}
}
Buttons::State Buttons::getState(std::unique_ptr<rawinput::RawInputManager> &manager, Button &button, bool check_alts) {
if (manager) {
return getState(manager.get(), button, check_alts);
} else {
return button.getLastState();
}
}
static float getVelocityHelper(rawinput::RawInputManager *manager, Button &button) {
// check override
if (button.override_enabled) {
return button.override_velocity;
}
// naive behavior
if (button.isNaive()) {
if (button.getInvert()) {
return (GetAsyncKeyState(button.getVKey()) & 0x8000) ? 0.f : 1.f;
} else {
return (GetAsyncKeyState(button.getVKey()) & 0x8000) ? 1.f : 0.f;
}
}
// get button state
Buttons::State button_state = Buttons::getState(manager, button, false);
// check if button isn't being pressed
if (button_state != Buttons::BUTTON_PRESSED) {
return 0.f;
}
// get device
auto &devid = button.getDeviceIdentifier();
auto device = manager->devices_get(devid, false);
// return last velocity if device wasn't found
if (!device) {
return button.getLastVelocity();
}
// prepare
float velocity = 1.f;
auto vKey = button.getVKey();
// lock device
device->mutex->lock();
// determine velocity based on device type
switch (device->type) {
case rawinput::MIDI: {
// read control
if (vKey < 16 * 128) {
velocity = (float) device->midiInfo->velocity[vKey] / 127.f;
} else {
velocity = 0.f;
}
// invert
if (button.getInvert()) {
velocity = 1.f - velocity;
}
break;
}
default:
break;
}
// unlock device
device->mutex->unlock();
// set last velocity
button.setLastVelocity(velocity);
// return determined velocity
return velocity;
}
float GameAPI::Buttons::getVelocity(rawinput::RawInputManager *manager, Button &button) {
// get button velocity
auto velocity = getVelocityHelper(manager, button);
// check alternatives
for (auto &alternative : button.getAlternatives()) {
auto alt_velocity = getVelocityHelper(manager, alternative);
if (alt_velocity > velocity) {
velocity = alt_velocity;
}
}
// return highest velocity detected
return velocity;
}
float Buttons::getVelocity(std::unique_ptr<rawinput::RawInputManager> &manager, Button &button) {
if (manager) {
return getVelocity(manager.get(), button);
} else {
return button.getLastVelocity();
}
}
float GameAPI::Analogs::getState(rawinput::Device *device, Analog &analog) {
float value = 0.5f;
if (!device) {
return value;
}
auto index = analog.getIndex();
auto inverted = analog.getInvert();
device->mutex->lock();
// get value from device
switch (device->type) {
case rawinput::MOUSE: {
// get mouse position
auto mouse = device->mouseInfo;
long pos;
switch (index) {
case rawinput::MOUSEPOS_X:
pos = mouse->pos_x;
break;
case rawinput::MOUSEPOS_Y:
pos = mouse->pos_y;
break;
case rawinput::MOUSEPOS_WHEEL:
pos = mouse->pos_wheel;
break;
default:
pos = 0;
break;
}
// apply sensitivity
auto val = (int) roundf(pos * analog.getSensitivity());
if (val < 0) {
inverted = !inverted;
}
// modulo & normalize to [0.0, 1.0]
if (index != rawinput::MOUSEPOS_WHEEL) {
val = std::abs(val) % 257;
value = val / 256.f;
} else {
val = std::abs(val) % 65;
value = val / 64.f;
}
// invert
if (inverted) {
value = 1.f - value;
}
break;
}
case rawinput::HID: {
// get value
if (inverted) {
value = 1.f - device->hidInfo->value_states[index];
} else {
value = device->hidInfo->value_states[index];
}
// smoothing/sensitivity
if (analog.getSmoothing() || analog.isSensitivitySet()) {
float rads = value * (float) M_TAU;
// smoothing
if (analog.getSmoothing()) {
// preserve direction
if (rads >= M_TAU) {
rads -= 0.0001f;
}
// calculate angle
rads = analog.getSmoothedValue(rads);
}
// sensitivity
if (analog.isSensitivitySet()) {
rads = analog.applyAngularSensitivity(rads);
}
// apply to value
value = rads * (float) M_1_TAU;
}
break;
}
case rawinput::MIDI: {
// get sizes
auto midi = device->midiInfo;
auto prec_count = (int) midi->controls_precision.size();
auto single_count = (int) midi->controls_single.size();
auto onoff_count = (int) midi->controls_onoff.size();
// decide on value
if (index < prec_count)
value = midi->controls_precision[index] / 16383.f;
else if (index < prec_count + single_count)
value = midi->controls_single[index - prec_count] / 127.f;
else if (index < prec_count + single_count + onoff_count)
value = midi->controls_onoff[index - prec_count - single_count] ? 1.f : 0.f;
else if (index == prec_count + single_count + onoff_count)
value = midi->pitch_bend / 16383.f;
// invert value
if (inverted) {
value = 1.f - value;
}
}
default:
break;
}
// deadzone logic
switch (device->type) {
case rawinput::HID:
case rawinput::MIDI: {
// check if set
if (analog.isDeadzoneSet()) {
// check sign
auto deadzone = analog.getDeadzone();
if (deadzone > 0) {
// calculate values
auto delta = value - 0.5f;
auto dtlen = 1.f - deadzone;
// check mirror
if (analog.getDeadzoneMirror()) {
// deadzone on the edges
if (dtlen != 0.f) {
value = std::max(0.f, std::min(1.f, 0.5f + (delta / dtlen)));
} else {
value = 0.5f;
}
} else {
// deadzone around the middle
auto limit = deadzone * 0.5f;
if (dtlen != 0.f) {
if (delta > limit) {
value = std::min(1.f, 0.5f + std::max(0.f, (delta - limit) / dtlen));
} else if (delta < -limit) {
value = std::max(0.f, 0.5f + std::min(0.f, (delta + limit) / dtlen));
} else {
value = 0.5f;
}
} else {
value = 0.5f;
}
}
} else if (deadzone < 0) {
// invert for mirror
if (analog.getDeadzoneMirror()) {
value = 1.f - value;
}
// deadzone from minimum value
if (deadzone > -1 && value > -deadzone) {
value = std::min(1.f, (value + deadzone) / (1.f + deadzone));
} else {
value = 0.f;
}
// revert value for mirror
if (analog.getDeadzoneMirror()) {
value = 1.f - value;
}
}
}
}
default:
break;
}
device->mutex->unlock();
return value;
}
std::vector<Analog> GameAPI::Analogs::getAnalogs(const std::string &game_name) {
return Config::getInstance().getAnalogs(game_name);
}
std::vector<Analog> GameAPI::Analogs::sortAnalogs(
const std::vector<Analog> &analogs,
const std::vector<std::string> &analog_names)
{
std::vector<Analog> sorted;
bool analog_found;
for (auto &name : analog_names) {
analog_found = false;
for (auto &analog : analogs) {
if (name == analog.getName()) {
analog_found = true;
sorted.push_back(analog);
break;
}
}
if (!analog_found) {
sorted.emplace_back(name);
}
}
return sorted;
}
float GameAPI::Analogs::getState(rawinput::RawInputManager *manager, Analog &analog) {
// check override
if (analog.override_enabled) {
return analog.override_state;
}
// get device
auto &devid = analog.getDeviceIdentifier();
auto device = manager->devices_get(devid, false); // TODO: fix to update only
// return last state if device wasn't updated
if (!device) {
return analog.getLastState();
}
float state = getState(device, analog);
analog.setLastState(state);
return state;
}
float Analogs::getState(std::unique_ptr<rawinput::RawInputManager> &manager, Analog &analog) {
if (manager) {
return getState(manager.get(), analog);
} else {
return analog.getLastState();
}
}
std::vector<Light> GameAPI::Lights::getLights(const std::string &game_name) {
return Config::getInstance().getLights(game_name);
}
std::vector<Light> GameAPI::Lights::sortLights(
const std::vector<Light> &lights,
const std::vector<std::string> &light_names)
{
std::vector<Light> sorted;
bool light_found;
for (auto &name : light_names) {
light_found = false;
for (auto &light : lights) {
if (name == light.getName()) {
light_found = true;
sorted.push_back(light);
break;
}
}
if (!light_found) {
sorted.emplace_back(name);
}
}
return sorted;
}
void GameAPI::Lights::writeLight(rawinput::Device *device, int index, float value) {
// check device
if (!device) {
return;
}
// clamp to range [0,1]
value = CLAMP(value, 0.f, 1.f);
// lock device
device->mutex->lock();
// enable output
device->output_enabled = true;
// check type
switch (device->type) {
case rawinput::HID: {
auto hid = device->hidInfo;
// find in buttons
bool button_found = false;
for (auto &button_states : hid->button_output_states) {
if ((size_t) index < button_states.size()) {
auto new_state = value > 0.5f;
if (button_states[index] != new_state) {
button_states[index] = new_state;
device->output_pending = true;
}
button_found = true;
break;
} else
index -= button_states.size();
}
// find in values
if (!button_found) {
auto &value_states = hid->value_output_states;
if ((size_t) index < value_states.size()) {
auto cur_state = &value_states[index];
if (*cur_state != value) {
*cur_state = value;
device->output_pending = true;
}
}
}
break;
}
case rawinput::SEXTET_OUTPUT: {
if (index < rawinput::SextetDevice::LIGHT_COUNT) {
device->sextetInfo->light_state[index] = value > 0;
device->sextetInfo->push_light_state();
} else {
log_warning("api", "invalid sextet light index: {}", index);
}
break;
}
case rawinput::PIUIO_DEVICE: {
if (index < rawinput::PIUIO::PIUIO_MAX_NUM_OF_LIGHTS) {
device->piuioDev->SetLight(index, value > 0);
} else {
log_warning("api", "invalid piuio light index: {}", index);
}
break;
}
default:
break;
}
// unlock device
device->mutex->unlock();
}
void GameAPI::Lights::writeLight(rawinput::RawInputManager *manager, Light &light, float value) {
// clamp to range [0,1]
value = CLAMP(value, 0.f, 1.f);
// write to last state
light.last_state = value;
// get device
auto &devid = light.getDeviceIdentifier();
auto device = manager->devices_get(devid, false);
// check device
if (device) {
// write state
if (light.override_enabled) {
writeLight(device, light.getIndex(), light.override_state);
} else {
writeLight(device, light.getIndex(), value);
}
}
// alternatives
for (auto &alternative : light.getAlternatives()) {
if (light.override_enabled) {
alternative.override_enabled = true;
alternative.override_state = light.override_state;
writeLight(manager, alternative, light.override_state);
} else {
alternative.override_enabled = false;
writeLight(manager, alternative, value);
}
}
}
void Lights::writeLight(std::unique_ptr<rawinput::RawInputManager> &manager, Light &light, float value) {
if (manager) {
writeLight(manager.get(), light, value);
}
}
float GameAPI::Lights::readLight(rawinput::Device *device, int index) {
float ret = 0.f;
// lock device
device->mutex->lock();
// check type
switch (device->type) {
case rawinput::HID: {
auto hid = device->hidInfo;
// find in buttons
bool button_found = false;
for (auto &button_states : hid->button_output_states) {
if ((size_t) index < button_states.size()) {
ret = button_states[index] ? 1.f : 0.f;
button_found = true;
break;
} else
index -= button_states.size();
}
// find in values
if (!button_found) {
auto value_states = &hid->value_output_states;
if ((size_t) index < value_states->size()) {
ret = (*value_states)[index];
}
}
break;
}
default:
break;
}
// unlock device
device->mutex->unlock();
// return result
return ret;
}
float GameAPI::Lights::readLight(rawinput::RawInputManager *manager, Light &light) {
// check override
if (light.override_enabled) {
return light.override_state;
}
// just return last state since that reflects the last value being written
return light.last_state;
}
float Lights::readLight(std::unique_ptr<rawinput::RawInputManager> &manager, Light &light) {
if (manager) {
return readLight(manager.get(), light);
} else {
// check override
if (light.override_enabled) {
return light.override_state;
}
// just return last state since that reflects the last value being written
return light.last_state;
}
}
std::vector<Option> GameAPI::Options::getOptions(const std::string &gameName) {
return Config::getInstance().getOptions(gameName);
}
void GameAPI::Options::sortOptions(std::vector<Option> &options, const std::vector<OptionDefinition> &definitions) {
std::vector<Option> sorted;
bool option_found;
for (const auto &definition : definitions) {
option_found = false;
for (auto &option : options) {
if (definition.name == option.get_definition().name) {
option_found = true;
auto &new_option = sorted.emplace_back(option);
new_option.set_definition(definition);
break;
}
}
if (!option_found) {
sorted.emplace_back(definition);
}
}
options = std::move(sorted);
}
+158
View File
@@ -0,0 +1,158 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
#include <windows.h>
#include "option.h"
namespace rawinput {
class RawInputManager;
struct Device;
}
class Button;
class Analog;
class Light;
class Game;
class Option;
namespace GameAPI {
namespace Buttons {
enum State {
BUTTON_PRESSED = true,
BUTTON_NOT_PRESSED = false
};
/**
* Parses the config and returns the buttons set by the user.
*
* @param a std::string containing the game name OR a pointer to a Game
* @return a vector pointer containing pointers of Button's set by the user
*/
std::vector<Button> getButtons(const std::string &game_name);
std::vector<Button> getButtons(Game *);
/**
* Sorts Buttons by their name
*
* @param a pointer to a vector pointer of Button pointers that will be sorted by the order of a vector pointer of strings
* OR a parameter pack of std::string... can be used.
* @return void
*/
std::vector<Button> sortButtons(
const std::vector<Button> &buttons,
const std::vector<std::string> &button_names,
const std::vector<unsigned short> *vkey_defaults = nullptr);
template<typename T>
void sortButtons(std::vector<Button> *buttons, T t) {
const std::vector<std::string> names { t };
if (buttons) {
*buttons = GameAPI::Buttons::sortButtons(*buttons, names);
}
}
template<typename T, typename... Rest>
void sortButtons(std::vector<Button> *buttons, T t, Rest... rest) {
const std::vector<std::string> names { t, rest... };
if (buttons) {
*buttons = GameAPI::Buttons::sortButtons(*buttons, names);
}
}
/**
* Returns the state of whether a button is pressed or not.
* Highly recommended to use either of these two functions than the other two below them.
*
* @return either a GameAPI::Buttons::State::BUTTON_PRESSED or a Game::API::Buttons::State::BUTTON_NOT_PRESSED
*/
State getState(rawinput::RawInputManager *manager, Button &button, bool check_alts = true);
State getState(std::unique_ptr<rawinput::RawInputManager> &manager, Button &button, bool check_alts = true);
/**
* Returns the current velocity of a button.
* When not pressed, the returned velocity is 0.
* When pressed, the velocity can be anywhere in the range [0, 1]
* @return velocity in the range [0, 1]
*/
float getVelocity(rawinput::RawInputManager *manager, Button &button);
float getVelocity(std::unique_ptr<rawinput::RawInputManager> &manager, Button &button);
}
namespace Analogs {
std::vector<Analog> getAnalogs(const std::string &game_name);
std::vector<Analog> sortAnalogs(
const std::vector<Analog> &analogs,
const std::vector<std::string> &analog_names);
template<typename T>
void sortAnalogs(std::vector<Analog> *analogs, T t) {
const std::vector<std::string> analog_names { t };
if (analogs) {
*analogs = GameAPI::Analogs::sortAnalogs(*analogs, analog_names);
}
}
template<typename T, typename... Rest>
void sortAnalogs(std::vector<Analog> *analogs, T t, Rest... rest) {
const std::vector<std::string> analog_names { t, rest... };
if (analogs) {
*analogs = GameAPI::Analogs::sortAnalogs(*analogs, analog_names);
}
}
float getState(rawinput::Device *device, Analog &analog);
float getState(rawinput::RawInputManager *manager, Analog &analog);
float getState(std::unique_ptr<rawinput::RawInputManager> &manager, Analog &analog);
}
namespace Lights {
std::vector<Light> getLights(const std::string &game_name);
std::vector<Light> sortLights(
const std::vector<Light> &lights,
const std::vector<std::string> &light_names);
template<typename T>
void sortLights(std::vector<Light> *lights, T t) {
const std::vector<std::string> light_names { t };
if (lights) {
*lights = GameAPI::Lights::sortLights(*lights, light_names);
}
}
template<typename T, typename... Rest>
void sortLights(std::vector<Light> *lights, T t, Rest... rest) {
const std::vector<std::string> light_names { t, rest... };
if (lights) {
*lights = GameAPI::Lights::sortLights(*lights, light_names);
}
}
void writeLight(rawinput::Device *device, int index, float value);
void writeLight(rawinput::RawInputManager *manager, Light &light, float value);
void writeLight(std::unique_ptr<rawinput::RawInputManager> &manager, Light &light, float value);
float readLight(rawinput::Device *device, int index);
float readLight(rawinput::RawInputManager *manager, Light &light);
float readLight(std::unique_ptr<rawinput::RawInputManager> &manager, Light &light);
}
namespace Options {
std::vector<Option> getOptions(const std::string &game_name);
void sortOptions(std::vector<Option> &, const std::vector<OptionDefinition> &);
}
}
#include "button.h"
#include "analog.h"
#include "light.h"
+440
View File
@@ -0,0 +1,440 @@
#include "button.h"
#include "rawinput/rawinput.h"
#include "util/logging.h"
#include "util/utils.h"
const char *ButtonAnalogTypeStr[] = {
"None",
"Positive",
"Negative",
"Hat Up",
"Hat Upright",
"Hat Right",
"Hat Downright",
"Hat Down",
"Hat Downleft",
"Hat Left",
"Hat Upleft",
"Hat Neutral",
"MIDI Control Precision",
"MIDI Control Single",
"MIDI Control On/Off",
"MIDI Pitch Down",
"MIDI Pitch Up",
};
std::string Button::getVKeyString() {
switch (this->getVKey() % 256) {
case 0x01:
return "Left MB";
case 0x02:
return "Right MB";
case 0x04:
return "Middle MB";
case 0x05:
return "X1 MB";
case 0x06:
return "X2 MB";
case 0x08:
return "Backspace";
case 0x09:
return "Tab";
case 0x0C:
return "Clear";
case 0x0D:
return "Enter";
case 0x10:
return "Shift";
case 0x11:
return "Ctrl";
case 0x12:
if (this->getVKey() > 255)
return "AltGr";
else
return "Alt";
case 0x13:
return "Pause";
case 0x14:
return "Caps Lock";
case 0x1B:
return "Escape";
case 0x20:
return "Space";
case 0x21:
return "Page Up";
case 0x22:
return "Page Down";
case 0x23:
return "End";
case 0x24:
return "Home";
case 0x25:
return "Left";
case 0x26:
return "Up";
case 0x27:
return "Right";
case 0x28:
return "Down";
case 0x2C:
return "Prt Scr";
case 0x2D:
return "Insert";
case 0x2E:
return "Delete";
case 0x30:
return "0";
case 0x31:
return "1";
case 0x32:
return "2";
case 0x33:
return "3";
case 0x34:
return "4";
case 0x35:
return "5";
case 0x36:
return "6";
case 0x37:
return "7";
case 0x38:
return "8";
case 0x39:
return "9";
case 0x41:
return "A";
case 0x42:
return "B";
case 0x43:
return "C";
case 0x44:
return "D";
case 0x45:
return "E";
case 0x46:
return "F";
case 0x47:
return "G";
case 0x48:
return "H";
case 0x49:
return "I";
case 0x4A:
return "J";
case 0x4B:
return "K";
case 0x4C:
return "L";
case 0x4D:
return "M";
case 0x4E:
return "N";
case 0x4F:
return "O";
case 0x50:
return "P";
case 0x51:
return "Q";
case 0x52:
return "R";
case 0x53:
return "S";
case 0x54:
return "T";
case 0x55:
return "U";
case 0x56:
return "V";
case 0x57:
return "W";
case 0x58:
return "X";
case 0x59:
return "Y";
case 0x5A:
return "Z";
case 0x5B:
return "Left Windows";
case 0x5C:
return "Right Windows";
case 0x5D:
return "Apps";
case 0x60:
return "Num 0";
case 0x61:
return "Num 1";
case 0x62:
return "Num 2";
case 0x63:
return "Num 3";
case 0x64:
return "Num 4";
case 0x65:
return "Num 5";
case 0x66:
return "Num 6";
case 0x67:
return "Num 7";
case 0x68:
return "Num 8";
case 0x69:
return "Num 9";
case 0x6A:
return "*";
case 0x6B:
return "+";
case 0x6C:
return "Seperator";
case 0x6D:
return "-";
case 0x6E:
return ".";
case 0x6F:
return "/";
case 0x70:
return "F1";
case 0x71:
return "F2";
case 0x72:
return "F3";
case 0x73:
return "F4";
case 0x74:
return "F5";
case 0x75:
return "F6";
case 0x76:
return "F7";
case 0x77:
return "F8";
case 0x78:
return "F9";
case 0x79:
return "F10";
case 0x7A:
return "F11";
case 0x7B:
return "F12";
case 0x7C:
return "F13";
case 0x7D:
return "F14";
case 0x7E:
return "F15";
case 0x7F:
return "F16";
case 0x80:
return "F17";
case 0x81:
return "F18";
case 0x82:
return "F19";
case 0x83:
return "F20";
case 0x84:
return "F21";
case 0x85:
return "F22";
case 0x86:
return "F23";
case 0x87:
return "F24";
case 0x90:
return "Num Lock";
case 0x91:
return "Scroll Lock";
case 0xA0:
return "Left Shift";
case 0xA1:
return "Right Shift";
case 0xA2:
return "Left Control";
case 0xA3:
return "Right Control";
case 0xA4:
return "Left Menu";
case 0xA5:
return "Right Menu";
default:
// check win API
char keyName[128];
if (GetKeyNameText((LONG) (MapVirtualKey(vKey, MAPVK_VK_TO_VSC) << 16), keyName, 128))
return std::string(keyName);
return "Unknown";
}
}
std::string Button::getDisplayString(rawinput::RawInputManager* manager) {
// get VKey string
auto vKey = (uint16_t) this->getVKey();
std::string vKeyString = fmt::format("{:#x}", vKey);
// device must be existing
if (this->device_identifier.empty() && vKey == 0xFF) {
return "";
}
if (this->isNaive()) {
return this->getVKeyString() + " (Naive, " + vKeyString + ")";
} else {
auto device = manager->devices_get(this->device_identifier);
if (!device) {
return "Device missing (" + vKeyString + ")";
}
std::lock_guard<std::mutex> lock(*device->mutex);
switch (device->type) {
case rawinput::MOUSE: {
const char *btn = "Unknown";
static const char *MOUSE_NAMES[] = {
"Left Mouse",
"Right Mouse",
"Middle Mouse",
"Mouse 1",
"Mouse 2",
"Mouse 3",
"Mouse 4",
"Mouse 5",
};
if (vKey < sizeof(MOUSE_NAMES)) {
btn = MOUSE_NAMES[vKey];
}
return fmt::format("{} ({})", btn, device->desc);
}
case rawinput::KEYBOARD:
return this->getVKeyString() + " (" + device->desc + ")";
case rawinput::HID: {
auto hid = device->hidInfo;
switch (this->analog_type) {
case BAT_NONE:
if (vKey < hid->button_caps_names.size())
return hid->button_caps_names[vKey] + " (" + device->desc + ")";
else
return "Invalid button (" + device->desc + ")";
case BAT_NEGATIVE:
case BAT_POSITIVE: {
const char *sign = this->analog_type == BAT_NEGATIVE ? "-" : "+";
if (vKey < hid->value_caps_names.size()) {
return hid->value_caps_names[vKey] + sign + " (" + device->desc + ")";
} else {
return "Invalid analog (" + device->desc + ")";
}
}
case BAT_HS_UP:
return "Hat Up (" + device->desc + ")";
case BAT_HS_UPRIGHT:
return "Hat UpRight (" + device->desc + ")";
case BAT_HS_RIGHT:
return "Hat Right (" + device->desc + ")";
case BAT_HS_DOWNRIGHT:
return "Hat DownRight (" + device->desc + ")";
case BAT_HS_DOWN:
return "Hat Down (" + device->desc + ")";
case BAT_HS_DOWNLEFT:
return "Hat DownLeft (" + device->desc + ")";
case BAT_HS_LEFT:
return "Hat Left (" + device->desc + ")";
case BAT_HS_UPLEFT:
return "Hat UpLeft (" + device->desc + ")";
case BAT_HS_NEUTRAL:
return "Hat Neutral (" + device->desc + ")";
default:
return "Unknown analog type (" + device->desc + ")";
}
}
case rawinput::MIDI:
switch (this->analog_type) {
case BAT_NONE:
return "MIDI " + vKeyString + " (" + device->desc + ")";
case BAT_MIDI_CTRL_PRECISION:
return "MIDI PREC " + vKeyString + " (" + device->desc + ")";
case BAT_MIDI_CTRL_SINGLE:
return "MIDI CTRL " + vKeyString + " (" + device->desc + ")";
case BAT_MIDI_CTRL_ONOFF:
return "MIDI ONOFF " + vKeyString + " (" + device->desc + ")";
case BAT_MIDI_PITCH_DOWN:
return "MIDI Pitch Down (" + device->desc + ")";
case BAT_MIDI_PITCH_UP:
return "MIDI Pitch Up (" + device->desc + ")";
default:
return "MIDI Unknown " + vKeyString + " (" + device->desc + ")";
}
case rawinput::PIUIO_DEVICE:
return "PIUIO " + vKeyString;
case rawinput::DESTROYED:
return "Device unplugged (" + vKeyString + ")";
default:
return "Unknown device type (" + vKeyString + ")";
}
}
}
#define HAT_SWITCH_INCREMENT (1.f / 7)
void Button::getHatSwitchValues(float analog_state, ButtonAnalogType* buffer) {
// rawinput converts neutral hat switch values to a negative value
if (analog_state < 0.f) {
buffer[0] = BAT_HS_NEUTRAL;
buffer[1] = BAT_NONE;
buffer[2] = BAT_NONE;
return;
}
if (analog_state < 0 * HAT_SWITCH_INCREMENT + 0.001f) {
buffer[0] = BAT_HS_UP;
buffer[1] = BAT_NONE;
buffer[2] = BAT_NONE;
return;
}
if (analog_state < 1 * HAT_SWITCH_INCREMENT + 0.001f) {
buffer[0] = BAT_HS_UPRIGHT;
buffer[1] = BAT_HS_UP;
buffer[2] = BAT_HS_RIGHT;
return;
}
if (analog_state < 2 * HAT_SWITCH_INCREMENT + 0.001f) {
buffer[0] = BAT_HS_RIGHT;
buffer[1] = BAT_NONE;
buffer[2] = BAT_NONE;
return;
}
if (analog_state < 3 * HAT_SWITCH_INCREMENT + 0.001f) {
buffer[0] = BAT_HS_DOWNRIGHT;
buffer[1] = BAT_HS_RIGHT;
buffer[2] = BAT_HS_DOWN;
return;
}
if (analog_state < 4 * HAT_SWITCH_INCREMENT + 0.001f) {
buffer[0] = BAT_HS_DOWN;
buffer[1] = BAT_NONE;
buffer[2] = BAT_NONE;
return;
}
if (analog_state < 5 * HAT_SWITCH_INCREMENT + 0.001f) {
buffer[0] = BAT_HS_DOWNLEFT;
buffer[1] = BAT_HS_DOWN;
buffer[2] = BAT_HS_LEFT;
return;
}
if (analog_state < 6 * HAT_SWITCH_INCREMENT + 0.001f) {
buffer[0] = BAT_HS_LEFT;
buffer[1] = BAT_NONE;
buffer[2] = BAT_NONE;
return;
}
if (analog_state < 7 * HAT_SWITCH_INCREMENT + 0.001f) {
buffer[0] = BAT_HS_UPLEFT;
buffer[1] = BAT_HS_LEFT;
buffer[2] = BAT_HS_UP;
return;
}
buffer[0] = BAT_HS_NEUTRAL;
buffer[1] = BAT_NONE;
buffer[2] = BAT_NONE;
return;
}
+176
View File
@@ -0,0 +1,176 @@
#pragma once
#include <string>
#include <utility>
#include <vector>
#include "api.h"
namespace rawinput {
class RawInputManager;
}
enum ButtonAnalogType {
BAT_NONE = 0,
BAT_POSITIVE = 1,
BAT_NEGATIVE = 2,
BAT_HS_UP = 3,
BAT_HS_UPRIGHT = 4,
BAT_HS_RIGHT = 5,
BAT_HS_DOWNRIGHT = 6,
BAT_HS_DOWN = 7,
BAT_HS_DOWNLEFT = 8,
BAT_HS_LEFT = 9,
BAT_HS_UPLEFT = 10,
BAT_HS_NEUTRAL = 11,
BAT_MIDI_CTRL_PRECISION = 12,
BAT_MIDI_CTRL_SINGLE = 13,
BAT_MIDI_CTRL_ONOFF = 14,
BAT_MIDI_PITCH_DOWN = 15,
BAT_MIDI_PITCH_UP = 16,
};
extern const char *ButtonAnalogTypeStr[];
class Button {
private:
std::vector<Button> alternatives;
std::string name;
std::string device_identifier = "";
unsigned short vKey = 0xFF;
ButtonAnalogType analog_type = BAT_NONE;
double debounce_up = 0.0;
double debounce_down = 0.0;
bool invert = false;
GameAPI::Buttons::State last_state = GameAPI::Buttons::BUTTON_NOT_PRESSED;
float last_velocity = 0.f;
std::string getVKeyString();
public:
// overrides
bool override_enabled = false;
GameAPI::Buttons::State override_state = GameAPI::Buttons::BUTTON_NOT_PRESSED;
float override_velocity = 0.f;
explicit Button(std::string name) : name(std::move(name)) {};
inline std::vector<Button> &getAlternatives() {
return this->alternatives;
}
inline bool isSet() {
if (this->override_enabled) {
return true;
}
if (this->vKey != 0xFF) {
return true;
}
for (auto &alternative : this->alternatives) {
if (alternative.vKey != 0xFF) {
return true;
}
}
return false;
}
inline void clearBindings() {
vKey = 0xFF;
alternatives.clear();
device_identifier = "";
analog_type = BAT_NONE;
}
std::string getDisplayString(rawinput::RawInputManager* manager);
inline bool isNaive() const {
return this->device_identifier.empty();
}
inline const std::string &getName() const {
return this->name;
}
inline const std::string &getDeviceIdentifier() const {
return this->device_identifier;
}
inline void setDeviceIdentifier(std::string new_device_identifier) {
this->device_identifier = std::move(new_device_identifier);
}
inline unsigned short getVKey() const {
return this->vKey;
}
inline void setVKey(unsigned short vKey) {
this->vKey = vKey;
}
inline ButtonAnalogType getAnalogType() const {
return this->analog_type;
}
inline void setAnalogType(ButtonAnalogType analog_type) {
this->analog_type = analog_type;
}
inline double getDebounceUp() const {
return this->debounce_up;
}
inline void setDebounceUp(double debounce_time_up) {
this->debounce_up = debounce_time_up;
}
inline double getDebounceDown() const {
return this->debounce_down;
}
inline void setDebounceDown(double debounce_time_down) {
this->debounce_down = debounce_time_down;
}
inline bool getInvert() const {
return this->invert;
}
inline void setInvert(bool invert) {
this->invert = invert;
}
inline GameAPI::Buttons::State getLastState() {
return this->last_state;
}
inline void setLastState(GameAPI::Buttons::State last_state) {
this->last_state = last_state;
}
inline float getLastVelocity() {
return this->last_velocity;
}
inline void setLastVelocity(float last_velocity) {
this->last_velocity = last_velocity;
}
/*
* Map hat switch float value from [0-1] to directions.
* Buffer must be sized 3 or bigger.
* Order of detection is:
* 1. Multi Direction Binding (example: BAT_HS_UPRIGHT)
* 2. Lower number binding (in order up, right, down, left)
* 3. Higher number binding
* Empty fields will be 0 (BAT_NONE)
*
* Example:
* Xbox360 Controller reports value 2 => mapped to float [0-1] it's 0.25f
* Resulting buffer is: {BAT_HS_UPRIGHT, BAT_HS_UP, BAT_HS_RIGHT}
*/
static void getHatSwitchValues(float analog_state, ButtonAnalogType* buffer);
};
+1160
View File
File diff suppressed because it is too large Load Diff
+60
View File
@@ -0,0 +1,60 @@
#pragma once
#include <filesystem>
#include <fstream>
#include <iostream>
#include "external/tinyxml2/tinyxml2.h"
#include "game.h"
// settings
extern std::string CONFIG_PATH_OVERRIDE;
struct ConfigKeypadBindings {
std::string keypads[2];
std::filesystem::path card_paths[2];
};
class Config {
public:
static Config &getInstance();
bool getStatus();
bool createConfigFile();
bool addGame(Game &game);
bool updateBinding(const Game &game, const Button &button, int alternative);
bool updateBinding(const Game &game, const Analog &analog);
bool updateBinding(const Game &game, ConfigKeypadBindings &keypads);
bool updateBinding(const Game &game, const Light &light, int alternative);
bool updateBinding(const Game &game, const Option &option);
std::vector<Button> getButtons(const std::string &game);
std::vector<Button> getButtons(Game *);
std::vector<Analog> getAnalogs(const std::string &game);
std::vector<Analog> getAnalogs(Game *);
ConfigKeypadBindings getKeypadBindings(const std::string &game);
ConfigKeypadBindings getKeypadBindings(Game *);
std::vector<Light> getLights(const std::string &game);
std::vector<Light> getLights(Game *);
std::vector<Option> getOptions(const std::string &game);
std::vector<Option> getOptions(Game *);
private:
Config();
Config(const Config &);
~Config() = default;
const Config &operator=(const Config &);
tinyxml2::XMLDocument configFile;
bool status;
std::string configLocation;
bool firstFillConfigFile();
};
+39
View File
@@ -0,0 +1,39 @@
#include "configurator.h"
#include "overlay/overlay.h"
#include "script/manager.h"
namespace cfg {
// globals
bool CONFIGURATOR_STANDALONE = false;
ConfigType CONFIGURATOR_TYPE = ConfigType::Config;
Configurator::Configurator() {
CONFIGURATOR_STANDALONE = true;
}
Configurator::~Configurator() {
CONFIGURATOR_STANDALONE = false;
}
void Configurator::run() {
// create instance
overlay::ENABLED = true;
overlay::create_software(this->wnd.hWnd);
overlay::OVERLAY->set_active(true);
overlay::OVERLAY->hotkeys_enable = false;
ImGui::GetIO().MouseDrawCursor = false;
// scripts
script::manager_scan();
script::manager_config();
// run window
this->wnd.run();
// clean up
script::manager_shutdown();
}
}
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include "configurator_wnd.h"
namespace cfg {
enum class ConfigType {
Config,
KFControl,
};
// globals
extern bool CONFIGURATOR_STANDALONE;
extern ConfigType CONFIGURATOR_TYPE;
class Configurator {
private:
ConfiguratorWindow wnd;
public:
Configurator();
~Configurator();
void run();
};
}
+173
View File
@@ -0,0 +1,173 @@
#include "configurator_wnd.h"
#include <windows.h>
#include "build/defs.h"
#include "launcher/shutdown.h"
#include "overlay/overlay.h"
#include "util/logging.h"
#include "cfg/configurator.h"
#include "icon.h"
static const char *CLASS_NAME = "ConfiguratorWindow";
static std::string WINDOW_TITLE;
static int WINDOW_SIZE_X = 800;
static int WINDOW_SIZE_Y = 600;
static HICON WINDOW_ICON = LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(MAINICON));
cfg::ConfiguratorWindow::ConfiguratorWindow() {
// register the window class
WNDCLASS wc {};
wc.lpfnWndProc = ConfiguratorWindow::window_proc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = CLASS_NAME;
wc.hbrBackground = NULL;
wc.hIcon = WINDOW_ICON;
RegisterClass(&wc);
// determine window title
if (cfg::CONFIGURATOR_TYPE == cfg::ConfigType::Config) {
WINDOW_TITLE = "SpiceTools Config (" + to_string(VERSION_STRING_CFG) + ")";
WINDOW_SIZE_X = 800;
WINDOW_SIZE_Y = 600;
} else if (cfg::CONFIGURATOR_TYPE == cfg::ConfigType::KFControl) {
WINDOW_TITLE = "KFControl (" + to_string(VERSION_STRING_CFG) + ")";
WINDOW_SIZE_X = 400;
WINDOW_SIZE_Y = 316;
}
// open window
this->hWnd = CreateWindowEx(
0,
CLASS_NAME,
WINDOW_TITLE.c_str(),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
NULL,
GetModuleHandle(NULL),
(LPVOID) this);
}
cfg::ConfiguratorWindow::~ConfiguratorWindow() {
// close window
DestroyWindow(this->hWnd);
// unregister class
UnregisterClass(CLASS_NAME, GetModuleHandle(NULL));
}
void cfg::ConfiguratorWindow::run() {
// show window
SetWindowPos(this->hWnd, HWND_TOP, 0, 0, WINDOW_SIZE_X, WINDOW_SIZE_Y, 0);
ShowWindow(this->hWnd, SW_SHOWNORMAL);
UpdateWindow(this->hWnd);
// draw overlay in 60 FPS
SetTimer(this->hWnd, 1, 1000 / 60, nullptr);
// window loop
BOOL ret;
MSG msg;
while ((ret = GetMessage(&msg, nullptr, 0, 0)) != 0) {
if (ret == -1) {
break;
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
LRESULT CALLBACK cfg::ConfiguratorWindow::window_proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CHAR: {
// input characters if overlay is active
if (overlay::OVERLAY && overlay::OVERLAY->has_focus()) {
overlay::OVERLAY->input_char((unsigned int) wParam);
return true;
}
break;
}
case WM_CREATE: {
// set user data of window to class pointer
auto create_struct = reinterpret_cast<LPCREATESTRUCT>(lParam);
SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(create_struct->lpCreateParams));
break;
}
case WM_CLOSE:
case WM_DESTROY: {
// exit process
launcher::shutdown();
break;
}
case WM_TIMER: {
// update overlay
if (overlay::OVERLAY) {
overlay::OVERLAY->update();
overlay::OVERLAY->set_active(true);
overlay::OVERLAY->new_frame();
overlay::OVERLAY->render();
}
// repaint window
InvalidateRect(hWnd, nullptr, TRUE);
break;
}
case WM_ERASEBKGND: {
return 1;
}
case WM_PAINT: {
// render overlay
if (overlay::OVERLAY) {
// get pixel data
int width, height;
uint32_t *pixel_data = overlay::OVERLAY->sw_get_pixel_data(&width, &height);
if (width > 0 && height > 0) {
// create bitmap
HBITMAP bitmap = CreateBitmap(width, height, 1, 8 * sizeof(uint32_t), pixel_data);
// prepare paint
PAINTSTRUCT paint{};
HDC hdc = BeginPaint(hWnd, &paint);
HDC hdcMem = CreateCompatibleDC(hdc);
SetBkMode(hdc, TRANSPARENT);
// draw bitmap
SelectObject(hdcMem, bitmap);
BitBlt(hdc, paint.rcPaint.left, paint.rcPaint.top,
paint.rcPaint.right - paint.rcPaint.left,
paint.rcPaint.bottom - paint.rcPaint.top,
hdcMem, paint.rcPaint.left, paint.rcPaint.top, SRCCOPY);
// delete bitmap
DeleteObject(bitmap);
// clean up
DeleteDC(hdcMem);
EndPaint(hWnd, &paint);
} else {
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
} else {
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
break;
}
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include <windows.h>
namespace cfg {
class ConfiguratorWindow {
public:
HWND hWnd;
ConfiguratorWindow();
~ConfiguratorWindow();
void run();
static LRESULT CALLBACK window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
};
}
+25
View File
@@ -0,0 +1,25 @@
#include "game.h"
/////////////////////////
/// Private Functions ///
/////////////////////////
// add button items
void Game::addItem(Button button) {
this->buttons.push_back(std::move(button));
}
// add analog items
void Game::addItem(Analog analog) {
this->analogs.push_back(std::move(analog));
}
// add light items
void Game::addItem(Light light) {
this->lights.push_back(std::move(light));
}
// add option items
void Game::addItem(Option option) {
this->options.push_back(std::move(option));
}
+84
View File
@@ -0,0 +1,84 @@
#pragma once
#include <vector>
#include <typeinfo>
#include <iostream>
#include "button.h"
#include "analog.h"
#include "light.h"
#include "option.h"
#include "external/tinyxml2/tinyxml2.h"
class Game {
public:
explicit Game(std::string game_name) : game_name(std::move(game_name)) {};
/*
template<typename T, typename... Rest>
Game(std::string gameName) : gameName(std::move(gameName)) {};
*/
~Game() = default;
inline const std::string &getGameName() const {
return this->game_name;
};
inline std::vector<std::string> &getDLLNames() {
return this->dll_names;
}
inline void addDLLName(std::string dll_name) {
this->dll_names.push_back(std::move(dll_name));
}
template<typename T>
void addItems(T t) {
this->addItem(t);
}
template<typename T, typename... Rest>
void addItems(T t, Rest... rest) {
this->addItem(t);
this->addItems(rest...);
}
inline std::vector<Button> &getButtons() {
return this->buttons;
}
inline const std::vector<Button> &getButtons() const {
return this->buttons;
}
inline std::vector<Analog> &getAnalogs() {
return this->analogs;
}
inline const std::vector<Analog> &getAnalogs() const {
return this->analogs;
}
inline std::vector<Light> &getLights() {
return this->lights;
}
inline const std::vector<Light> &getLights() const {
return this->lights;
}
inline std::vector<Option> &getOptions() {
return this->options;
}
inline const std::vector<Option> &getOptions() const {
return this->options;
}
private:
std::string game_name;
std::vector<std::string> dll_names;
std::vector<Button> buttons;
std::vector<Analog> analogs;
std::vector<Light> lights;
std::vector<Option> options;
void addItem(Button button);
void addItem(Analog analog);
void addItem(Light light);
void addItem(Option option);
};
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#define MAINICON 20
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

+3
View File
@@ -0,0 +1,3 @@
#include "icon.h"
MAINICON ICON "icon.ico"
+73
View File
@@ -0,0 +1,73 @@
#include "light.h"
#include <utility>
#include "rawinput/piuio.h"
#include "rawinput/rawinput.h"
#include "rawinput/sextet.h"
#include "util/logging.h"
std::string Light::getDisplayString(rawinput::RawInputManager* manager) {
// get index string
std::string index_string = fmt::format("{:#x}", index);
// device must be existing
if (this->deviceIdentifier.empty()) {
return "";
}
// get device
auto device = manager->devices_get(this->deviceIdentifier);
if (!device) {
return "Device missing (" + index_string + ")";
}
// return string based on device type
switch (device->type) {
case rawinput::HID: {
auto hid = device->hidInfo;
unsigned int hid_index = index;
// check button output caps
if (hid_index < hid->button_output_caps_names.size()) {
return hid->button_output_caps_names[hid_index] +
" (" + index_string + " - " + device->desc + ")";
} else {
hid_index -= hid->button_output_caps_names.size();
}
// check value output caps
if (hid_index < hid->value_output_caps_names.size()) {
return hid->value_output_caps_names[hid_index] +
" (" + index_string + " - " + device->desc + ")";
}
// not found
return "Invalid Light (" + index_string + ")";
}
case rawinput::SEXTET_OUTPUT: {
// get light name of sextet device
if (index < rawinput::SextetDevice::LIGHT_COUNT) {
return rawinput::SextetDevice::LIGHT_NAMES[index] + " (" + index_string + ")";
}
// not found
return "Invalid Sextet Light (" + index_string + ")";
}
case rawinput::PIUIO_DEVICE: {
// get light name of PIUIO device
if (index < rawinput::PIUIO::PIUIO_MAX_NUM_OF_LIGHTS) {
return rawinput::PIUIO::LIGHT_NAMES[index] + " (" + index_string + ")";
}
return "Invalid PIUIO Light (" + index_string + ")";
}
case rawinput::DESTROYED:
return "Unplugged device (" + index_string + ")";
default:
return "Unknown Light (" + index_string + ")";
}
}
+68
View File
@@ -0,0 +1,68 @@
#pragma once
#include <string>
#include <vector>
namespace rawinput {
class RawInputManager;
}
class Light {
private:
std::vector<Light> alternatives;
std::string lightName;
std::string deviceIdentifier = "";
unsigned int index = 0;
public:
float last_state = 0.f;
// overrides
bool override_enabled = false;
float override_state = 0.f;
explicit Light(std::string lightName) : lightName(std::move(lightName)) {};
std::string getDisplayString(rawinput::RawInputManager* manager);
inline std::vector<Light> &getAlternatives() {
return this->alternatives;
}
inline bool isSet() const {
if (this->override_enabled) {
return true;
}
if (!this->deviceIdentifier.empty()) {
return true;
}
for (auto &alternative : this->alternatives) {
if (!alternative.deviceIdentifier.empty()) {
return true;
}
}
return false;
}
inline const std::string &getName() const {
return this->lightName;
}
inline const std::string &getDeviceIdentifier() const {
return this->deviceIdentifier;
}
inline void setDeviceIdentifier(std::string deviceIdentifier) {
this->deviceIdentifier = std::move(deviceIdentifier);
}
inline unsigned int getIndex() const {
return this->index;
}
inline void setIndex(unsigned int index) {
this->index = index;
}
};
+4
View File
@@ -0,0 +1,4 @@
#pragma once
#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
#define RT_MANIFEST 24
+29
View File
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="1.0.0.0"
processorArchitecture="*"
name="spicecfg.exe"
type="win32"
/>
<description>SpiceTools Config</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
+30
View File
@@ -0,0 +1,30 @@
#include "manifest.h"
#include "winuser.h"
#ifdef __GNUC__
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "manifest.manifest"
#endif
1 VERSIONINFO
FILEVERSION 1,0,0,0
PRODUCTVERSION 1,0,0,0
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "080904E4"
BEGIN
VALUE "CompanyName", ""
VALUE "FileDescription", "SpiceTools Configuration Utility"
VALUE "FileVersion", "1.0.0.0"
VALUE "InternalName", "spicecfg"
VALUE "LegalCopyright", ""
VALUE "OriginalFilename", "spicecfg.exe"
VALUE "ProductName", "SpiceTools Configuration"
VALUE "ProductVersion", "1.0.0.0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x809, 1252
END
END
+84
View File
@@ -0,0 +1,84 @@
#include "option.h"
#include "util/logging.h"
void Option::value_add(std::string new_value) {
// put in primary slot if possible
if (this->value.empty()) {
this->value = std::move(new_value);
return;
}
// add new alternative
this->alternatives.emplace_back(this->definition, std::move(new_value));
}
bool Option::has_alternatives() const {
return !this->alternatives.empty();
}
bool Option::value_bool() const {
if (this->definition.type != OptionType::Bool) {
log_fatal("option", "value_bool() called on {}/{}", this->definition.title, this->definition.type);
}
return !this->value.empty();
}
const std::string &Option::value_text() const {
if (this->definition.type != OptionType::Text && this->definition.type != OptionType::Enum) {
log_fatal("option", "value_text() called on {}/{}", this->definition.title, this->definition.type);
}
return this->value;
}
std::vector<std::string> Option::values() const {
std::vector<std::string> values;
if (!this->is_active()) {
return values;
}
values.push_back(this->value);
for (auto &alt : this->alternatives) {
if (alt.is_active()) {
values.push_back(alt.value);
}
}
return values;
}
std::vector<std::string> Option::values_text() const {
std::vector<std::string> values;
if (!this->is_active()) {
return values;
}
values.push_back(this->value_text());
for (auto &alt : this->alternatives) {
if (alt.is_active()) {
values.push_back(alt.value_text());
}
}
return values;
}
int Option::value_int() const {
if (this->definition.type != OptionType::Integer && this->definition.type != OptionType::Enum) {
log_fatal("option", "value_int() called on {}/{}", this->definition.title, this->definition.type);
return 0;
}
char *p;
auto res = strtol(this->value.c_str(), &p, 10);
if (*p) {
log_fatal("option", "tried to convert {} to integer ({})", this->value, this->definition.title);
return 0;
} else {
return res;
}
}
+58
View File
@@ -0,0 +1,58 @@
#pragma once
#include <string>
#include <utility>
#include <vector>
enum class OptionType {
Bool,
Text,
Integer,
Enum,
};
struct OptionDefinition {
std::string title;
std::string name;
std::string desc;
OptionType type;
bool hidden = false;
std::string setting_name = "";
std::string game_name = "";
std::string category = "Development";
bool sensitive = false;
std::vector<std::pair<std::string, std::string>> elements = {};
};
class Option {
private:
OptionDefinition definition;
public:
std::string value;
std::vector<Option> alternatives;
bool disabled = false;
explicit Option(OptionDefinition definition, std::string value = "") :
definition(std::move(definition)), value(std::move(value)) {};
inline const OptionDefinition &get_definition() const {
return this->definition;
}
inline void set_definition(OptionDefinition definition) {
this->definition = std::move(definition);
}
inline bool is_active() const {
return !this->value.empty();
}
void value_add(std::string new_value);
bool has_alternatives() const;
bool value_bool() const;
const std::string &value_text() const;
std::vector<std::string> values() const;
std::vector<std::string> values_text() const;
int value_int() const;
};
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#define IDR_CHANGELOG 130
#define IDR_LICENSES 131
#define IDR_PATCHES 132
#define IDR_README 133
+122
View File
@@ -0,0 +1,122 @@
#include "screen_resize.h"
#include "external/rapidjson/document.h"
#include "external/rapidjson/writer.h"
#include "util/utils.h"
#include "util/fileutils.h"
using namespace rapidjson;
namespace cfg {
// globals
std::unique_ptr<cfg::ScreenResize> SCREENRESIZE;
ScreenResize::ScreenResize() {
this->config_path = std::string(getenv("APPDATA")) + "\\spicetools_screen_resize.json";
if (fileutils::file_exists(this->config_path)) {
this->config_load();
}
}
ScreenResize::~ScreenResize() {
}
void ScreenResize::config_load() {
log_info("ScreenResize", "loading config");
std::string config = fileutils::text_read(this->config_path);
if (!config.empty()) {
// parse document
Document doc;
doc.Parse(config.c_str());
// check parse error
auto error = doc.GetParseError();
if (error) {
log_warning("ScreenResize", "config parse error: {}", error);
}
// verify root is a dict
if (doc.IsObject()) {
bool valid = true;
auto offset_x = doc.FindMember("offset_x");
if (offset_x == doc.MemberEnd() || !offset_x->value.IsInt()) {
log_warning("ScreenResize", "offset_x not found");
valid = false;
}
auto offset_y = doc.FindMember("offset_y");
if (offset_y == doc.MemberEnd() || !offset_y->value.IsInt()) {
log_warning("ScreenResize", "offset_y not found");
valid = false;
}
auto scale_x = doc.FindMember("scale_x");
if (scale_x == doc.MemberEnd() || !scale_x->value.IsDouble()) {
log_warning("ScreenResize", "scale_x not found");
valid = false;
}
auto scale_y = doc.FindMember("scale_y");
if (scale_y == doc.MemberEnd() || !scale_y->value.IsDouble()) {
log_warning("ScreenResize", "scale_y not found");
valid = false;
}
auto enable_screen_resize = doc.FindMember("enable_screen_resize");
if (enable_screen_resize == doc.MemberEnd() || !enable_screen_resize->value.IsBool()) {
log_warning("ScreenResize", "enable_screen_resize not found");
valid = false;
}
auto enable_linear_filter = doc.FindMember("enable_linear_filter");
if (enable_linear_filter == doc.MemberEnd() || !enable_linear_filter->value.IsBool()) {
log_warning("ScreenResize", "enable_linear_filter not found");
valid = false;
}
auto keep_aspect_ratio = doc.FindMember("keep_aspect_ratio");
if (keep_aspect_ratio == doc.MemberEnd() || !keep_aspect_ratio->value.IsBool()) {
log_warning("ScreenResize", "keep_aspect_ratio not found");
valid = false;
}
if (valid) {
this->offset_x = offset_x->value.GetInt();
this->offset_y = offset_y->value.GetInt();
this->scale_x = (float)scale_x->value.GetDouble();
this->scale_y = (float)scale_y->value.GetDouble();
this->enable_screen_resize = enable_screen_resize->value.GetBool();
this->enable_linear_filter = enable_linear_filter->value.GetBool();
this->keep_aspect_ratio = keep_aspect_ratio->value.GetBool();
}
} else {
log_warning("ScreenResize", "config not found");
}
}
}
void ScreenResize::config_save() {
log_info("ScreenResize", "saving config");
// create document
Document doc;
doc.SetObject();
auto &alloc = doc.GetAllocator();
doc.AddMember("offset_x", this->offset_x, alloc);
doc.AddMember("offset_y", this->offset_y, alloc);
doc.AddMember("scale_x", this->scale_x, alloc);
doc.AddMember("scale_y", this->scale_y, alloc);
doc.AddMember("enable_screen_resize", this->enable_screen_resize, alloc);
doc.AddMember("enable_linear_filter", this->enable_linear_filter, alloc);
doc.AddMember("keep_aspect_ratio", this->keep_aspect_ratio, alloc);
// build JSON
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
doc.Accept(writer);
// save to file
if (fileutils::text_write(this->config_path, buffer.GetString())) {
// this->config_dirty = false;
} else {
log_warning("ScreenResize", "unable to save config file to {}", this->config_path);
}
}
}
+31
View File
@@ -0,0 +1,31 @@
#pragma once
#include <memory>
#include <string>
namespace cfg {
class ScreenResize {
private:
std::string config_path;
// bool config_dirty = false;
public:
ScreenResize();
~ScreenResize();
int offset_x = 0;
int offset_y = 0;
float scale_x = 1.0;
float scale_y = 1.0;
bool enable_screen_resize = false;
bool enable_linear_filter = false;
bool keep_aspect_ratio = false;
void config_load();
void config_save();
};
// globals
extern std::unique_ptr<cfg::ScreenResize> SCREENRESIZE;
}
+70
View File
@@ -0,0 +1,70 @@
#include "spicecfg.h"
#include <memory>
#include <windows.h>
#include <shlwapi.h>
#include "launcher/launcher.h"
#include "launcher/logger.h"
#include "launcher/signal.h"
#include "launcher/options.h"
#include "util/crypt.h"
#include "util/libutils.h"
#include "rawinput/rawinput.h"
#include "config.h"
#include "configurator.h"
// for debugging, set to 1 to allocate console
#define ALLOC_CONSOLE 0
int spicecfg_run(const std::vector<std::string> &sextet_devices) {
// initialize config
auto &config = Config::getInstance();
if (!config.getStatus()) {
return EXIT_FAILURE;
}
// initialize input
RI_MGR = std::make_unique<rawinput::RawInputManager>();
RI_MGR->devices_print();
for (const auto &device : sextet_devices) {
RI_MGR->sextet_register(device);
}
// run configurator
cfg::CONFIGURATOR_STANDALONE = true;
cfg::Configurator configurator;
configurator.run();
// success
return 0;
}
#ifdef SPICETOOLS_SPICECFG_STANDALONE
#ifdef _MSC_VER
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) {
#else
int main(int argc, char *argv[]) {
#endif
// initialize console
if (ALLOC_CONSOLE) {
AllocConsole();
freopen("conin$", "r", stdin);
freopen("conout$", "w", stdout);
freopen("conout$", "w", stderr);
}
// run launcher with configurator option
cfg::CONFIGURATOR_STANDALONE = true;
#ifdef _MSC_VER
return main_implementation(__argc, __argv);
#else
return main_implementation(argc, argv);
#endif
}
#endif
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include <vector>
#include <string>
int spicecfg_run(const std::vector<std::string> &sextet_devices);