Initial re-upload of spice2x-24-08-24
This commit is contained in:
18
cfg/Win32D.rc
Normal file
18
cfg/Win32D.rc
Normal file
@@ -0,0 +1,18 @@
|
||||
#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"
|
||||
IDR_DSEGFONT RCDATA "../external/dseg/DSEG14Classic-Italic.ttf"
|
||||
|
||||
#endif
|
||||
255
cfg/analog.cpp
Normal file
255
cfg/analog.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
#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 ¤t = 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;
|
||||
}
|
||||
|
||||
float Analog::applyMultiplier(float value) {
|
||||
if (1 < this->multiplier) {
|
||||
// multiplier - just multiply the value and take the decimal part
|
||||
return normalizeAnalogValue(value * this->multiplier);
|
||||
} else if (this->multiplier < -1) {
|
||||
const unsigned short number_of_divisions = -this->multiplier;
|
||||
// divisor - need to take care of over/underflow
|
||||
if (0.75f < this->divisor_previous_value && value < 0.25f) {
|
||||
this->divisor_region = (this->divisor_region + 1) % number_of_divisions;
|
||||
} else if (this->divisor_previous_value < 0.25f && 0.75f < value) {
|
||||
if (1 <= this->divisor_region) {
|
||||
this->divisor_region -= 1;
|
||||
} else {
|
||||
this->divisor_region = number_of_divisions - 1;
|
||||
}
|
||||
}
|
||||
this->divisor_previous_value = value;
|
||||
return ((float)this->divisor_region + value) / (float)number_of_divisions;
|
||||
} else {
|
||||
// multiplier in [-1, 1] range is just treated as 1
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
float Analog::normalizeAnalogValue(float value) {
|
||||
// effectively the same as fmodf(value, 1.f)
|
||||
// for small values, this is MUCH faster than fmodf.
|
||||
float new_value = value;
|
||||
while (new_value > 1.f) {
|
||||
new_value -= 1.f;
|
||||
}
|
||||
while (new_value < 0.f) {
|
||||
new_value += 1.f;
|
||||
}
|
||||
return new_value;
|
||||
}
|
||||
|
||||
float Analog::applyDeadzone(float raw_value) {
|
||||
float value = raw_value;
|
||||
const auto deadzone = this->getDeadzone();
|
||||
if (deadzone > 0) {
|
||||
|
||||
// calculate values
|
||||
const auto delta = value - 0.5f;
|
||||
const auto dtlen = 1.f - deadzone;
|
||||
|
||||
// check mirror
|
||||
if (this->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
|
||||
const 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 (this->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 (this->getDeadzoneMirror()) {
|
||||
value = 1.f - value;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
211
cfg/analog.h
Normal file
211
cfg/analog.h
Normal file
@@ -0,0 +1,211 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
#include <queue>
|
||||
|
||||
#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;
|
||||
|
||||
// multiplier/divisor
|
||||
int multiplier = 1;
|
||||
float divisor_previous_value = 0.5f;
|
||||
unsigned short divisor_region = 0;
|
||||
|
||||
// relative input mode
|
||||
float absolute_value_for_rel_mode = 0.5f;
|
||||
bool relative_mode = false;
|
||||
|
||||
// circular buffer (delayed input)
|
||||
int delay_buffer_depth = 0;
|
||||
std::queue<float> delay_buffer;
|
||||
|
||||
float calculateAngularDifference(float old_rads, float new_rads);
|
||||
float normalizeAngle(float rads);
|
||||
float normalizeAnalogValue(float value);
|
||||
|
||||
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);
|
||||
float applyMultiplier(float raw_value);
|
||||
float applyDeadzone(float raw_value);
|
||||
|
||||
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;
|
||||
setMultiplier(1);
|
||||
setRelativeMode(false);
|
||||
setDelayBufferDepth(0);
|
||||
}
|
||||
|
||||
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 int getMultiplier() const {
|
||||
return this->multiplier;
|
||||
}
|
||||
|
||||
inline void setMultiplier(int multiplier) {
|
||||
this->multiplier = multiplier;
|
||||
this->divisor_region = 0;
|
||||
this->divisor_previous_value = 0.5f;
|
||||
}
|
||||
|
||||
inline float getLastState() const {
|
||||
return this->last_state;
|
||||
}
|
||||
|
||||
inline void setLastState(float last_state) {
|
||||
this->last_state = last_state;
|
||||
}
|
||||
|
||||
inline bool isRelativeMode() const {
|
||||
return this->relative_mode;
|
||||
}
|
||||
|
||||
inline void setRelativeMode(bool relative_mode) {
|
||||
this->relative_mode = relative_mode;
|
||||
this->absolute_value_for_rel_mode = 0.5f;
|
||||
}
|
||||
|
||||
inline float getAbsoluteValue(float relative_delta) {
|
||||
this->absolute_value_for_rel_mode =
|
||||
normalizeAnalogValue(this->absolute_value_for_rel_mode + relative_delta);
|
||||
return this->absolute_value_for_rel_mode;
|
||||
}
|
||||
|
||||
inline int getDelayBufferDepth() const {
|
||||
return this->delay_buffer_depth;
|
||||
}
|
||||
|
||||
inline void setDelayBufferDepth(int depth) {
|
||||
this->delay_buffer_depth = depth;
|
||||
}
|
||||
|
||||
inline std::queue<float> &getDelayBuffer() {
|
||||
return this->delay_buffer;
|
||||
}
|
||||
};
|
||||
926
cfg/api.cpp
Normal file
926
cfg/api.cpp
Normal file
@@ -0,0 +1,926 @@
|
||||
#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 ? ¤t_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];
|
||||
}
|
||||
|
||||
// deadzone
|
||||
if (analog.isDeadzoneSet()) {
|
||||
value = analog.applyDeadzone(value);
|
||||
}
|
||||
|
||||
if (analog.isRelativeMode()) {
|
||||
float relative_delta = value - 0.5f;
|
||||
// built-in scaling to make values reasonable
|
||||
relative_delta /= 80.f;
|
||||
|
||||
// integer multiplier/divisor
|
||||
const auto mult = analog.getMultiplier();
|
||||
if (mult < -1) {
|
||||
relative_delta /= -mult;
|
||||
} else if (1 < mult) {
|
||||
relative_delta *= mult;
|
||||
}
|
||||
|
||||
// sensitivity (ranges from 0.0 to 4.0)
|
||||
if (analog.isSensitivitySet()) {
|
||||
relative_delta *= analog.getSensitivity();
|
||||
}
|
||||
|
||||
// translate relative movement to absolute value
|
||||
value = analog.getAbsoluteValue(relative_delta);
|
||||
|
||||
} else {
|
||||
// integer multiplier
|
||||
value = analog.applyMultiplier(value);
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// delay
|
||||
if (0 < analog.getDelayBufferDepth()) {
|
||||
auto& queue = analog.getDelayBuffer();
|
||||
|
||||
// ensure the queue isn't too long; drop old values
|
||||
while (analog.getDelayBufferDepth() <= (int)queue.size()) {
|
||||
queue.pop();
|
||||
}
|
||||
|
||||
// always push new value
|
||||
queue.push(value);
|
||||
|
||||
// get a new value to return
|
||||
if ((int)queue.size() < analog.getDelayBufferDepth()) {
|
||||
// not enough in the queue, stall for now, shouldn't happen often
|
||||
value = analog.getLastState();
|
||||
} else {
|
||||
value = queue.front();
|
||||
queue.pop();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// deadzone
|
||||
if (analog.isDeadzoneSet()) {
|
||||
value = analog.applyDeadzone(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
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();
|
||||
device->output_pending = true;
|
||||
} 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);
|
||||
device->output_pending = true;
|
||||
} else {
|
||||
log_warning("api", "invalid piuio light index: {}", index);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case rawinput::SMX_STAGE: {
|
||||
if (index < rawinput::SmxStageDevice::TOTAL_LIGHT_COUNT) {
|
||||
device->smxstageInfo->SetLightByIndex(index, static_cast<uint8_t>(value*255.f));
|
||||
device->output_pending = true;
|
||||
} else {
|
||||
log_warning("api", "invalid smx stage 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
cfg/api.h
Normal file
158
cfg/api.h
Normal 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
cfg/button.cpp
Normal file
440
cfg/button.cpp
Normal 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
cfg/button.h
Normal file
176
cfg/button.h
Normal 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);
|
||||
};
|
||||
1215
cfg/config.cpp
Normal file
1215
cfg/config.cpp
Normal file
File diff suppressed because it is too large
Load Diff
62
cfg/config.h
Normal file
62
cfg/config.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#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::filesystem::path configLocation;
|
||||
std::filesystem::path configLocationTemp;
|
||||
|
||||
bool firstFillConfigFile();
|
||||
void saveConfigFile();
|
||||
};
|
||||
39
cfg/configurator.cpp
Normal file
39
cfg/configurator.cpp
Normal 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
cfg/configurator.h
Normal file
26
cfg/configurator.h
Normal 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();
|
||||
};
|
||||
}
|
||||
177
cfg/configurator_wnd.cpp
Normal file
177
cfg/configurator_wnd.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
#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 = "spice2x 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);
|
||||
|
||||
if (this->hWnd) {
|
||||
overlay::USE_WM_CHAR_FOR_IMGUI_CHAR_INPUT = true;
|
||||
}
|
||||
}
|
||||
|
||||
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
cfg/configurator_wnd.h
Normal file
19
cfg/configurator_wnd.h
Normal 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
cfg/game.cpp
Normal file
25
cfg/game.cpp
Normal 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
cfg/game.h
Normal file
84
cfg/game.h
Normal 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
cfg/icon.h
Normal file
3
cfg/icon.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#define MAINICON 20
|
||||
BIN
cfg/icon.ico
Normal file
BIN
cfg/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 139 KiB |
3
cfg/icon.rc
Normal file
3
cfg/icon.rc
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "icon.h"
|
||||
|
||||
MAINICON ICON "icon.ico"
|
||||
83
cfg/light.cpp
Normal file
83
cfg/light.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "light.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "rawinput/piuio.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "rawinput/sextet.h"
|
||||
#include "rawinput/smxstage.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::SMX_STAGE: {
|
||||
|
||||
// get light name of SMX Stage device
|
||||
if (index < rawinput::SmxStageDevice::TOTAL_LIGHT_COUNT) {
|
||||
return rawinput::SmxStageDevice::GetLightNameByIndex(index) + " (" + index_string + ")";
|
||||
}
|
||||
|
||||
return "Invalid SMX Stage Light (" + index_string + ")";
|
||||
}
|
||||
case rawinput::DESTROYED:
|
||||
return "Unplugged device (" + index_string + ")";
|
||||
default:
|
||||
return "Unknown Light (" + index_string + ")";
|
||||
}
|
||||
}
|
||||
68
cfg/light.h
Normal file
68
cfg/light.h
Normal 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
cfg/manifest.h
Normal file
4
cfg/manifest.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
|
||||
#define RT_MANIFEST 24
|
||||
29
cfg/manifest.manifest
Normal file
29
cfg/manifest.manifest
Normal 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>spice2x 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
cfg/manifest.rc
Normal file
30
cfg/manifest.rc
Normal 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
|
||||
112
cfg/option.cpp
Normal file
112
cfg/option.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
#include "option.h"
|
||||
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.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;
|
||||
}
|
||||
|
||||
uint32_t Option::value_uint32() const {
|
||||
if (this->definition.type != OptionType::Integer && this->definition.type != OptionType::Enum) {
|
||||
log_fatal("option", "invalid call: value_uint32() 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", "failed to convert {} to unsigned integer (option: {})", this->value, this->definition.title);
|
||||
return 0;
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t Option::value_hex64() const {
|
||||
if (this->definition.type != OptionType::Hex) {
|
||||
log_fatal("option", "invalid call: value_hex() called on {}/{}", this->definition.title, this->definition.type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t affinity = 0;
|
||||
try {
|
||||
affinity = std::stoull(this->value.c_str(), nullptr, 16);
|
||||
} catch (const std::exception &ex) {
|
||||
log_fatal("option", "failed to parse {} as hexadecimal (option: {})", this->value, this->definition.title);
|
||||
}
|
||||
return affinity;
|
||||
}
|
||||
|
||||
bool Option::search_match(const std::string &query_in_lower_case) {
|
||||
if (this->search_string.empty()) {
|
||||
const auto ¶m =
|
||||
this->definition.display_name.empty() ?
|
||||
this->definition.name : this->definition.display_name;
|
||||
|
||||
const auto s = this->definition.title + " -" + param;
|
||||
this->search_string = strtolower(s);
|
||||
}
|
||||
return this->search_string.find(query_in_lower_case) != std::string::npos;
|
||||
}
|
||||
72
cfg/option.h
Normal file
72
cfg/option.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
enum class OptionType {
|
||||
Bool,
|
||||
Text,
|
||||
Integer,
|
||||
Enum,
|
||||
Hex,
|
||||
};
|
||||
|
||||
struct OptionDefinition {
|
||||
std::string title;
|
||||
// unique identifier used for flag matching but also stored in config files
|
||||
// (should not be changed once published for compat)
|
||||
std::string name;
|
||||
// what's displayed in the UI/logs as the flag name
|
||||
std::string display_name = "";
|
||||
// slash-delimited list of strings that also work as flag
|
||||
std::string aliases = "";
|
||||
// what's displayed in the UI/logs as the tooltip
|
||||
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 = {};
|
||||
bool disabled = false;
|
||||
};
|
||||
|
||||
class Option {
|
||||
private:
|
||||
OptionDefinition definition;
|
||||
std::string search_string;
|
||||
|
||||
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;
|
||||
uint32_t value_uint32() const;
|
||||
uint64_t value_hex64() const;
|
||||
bool search_match(const std::string &query_in_lower_case);
|
||||
};
|
||||
7
cfg/resource.h
Normal file
7
cfg/resource.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#define IDR_CHANGELOG 130
|
||||
#define IDR_LICENSES 131
|
||||
#define IDR_PATCHES 132
|
||||
#define IDR_README 133
|
||||
#define IDR_DSEGFONT 134
|
||||
219
cfg/screen_resize.cpp
Normal file
219
cfg/screen_resize.cpp
Normal file
@@ -0,0 +1,219 @@
|
||||
#include "screen_resize.h"
|
||||
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "external/rapidjson/pointer.h"
|
||||
#include "external/rapidjson/prettywriter.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
|
||||
namespace cfg {
|
||||
|
||||
// globals
|
||||
std::unique_ptr<cfg::ScreenResize> SCREENRESIZE;
|
||||
std::optional<std::string> SCREEN_RESIZE_CFG_PATH_OVERRIDE;
|
||||
|
||||
ScreenResize::ScreenResize() {
|
||||
if (SCREEN_RESIZE_CFG_PATH_OVERRIDE.has_value()) {
|
||||
this->config_path = SCREEN_RESIZE_CFG_PATH_OVERRIDE.value();
|
||||
} else {
|
||||
this->config_path = std::filesystem::path(_wgetenv(L"APPDATA")) / L"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: {}", this->config_path.string());
|
||||
|
||||
std::string config = fileutils::text_read(this->config_path);
|
||||
if (config.empty()) {
|
||||
log_info("ScreenResize", "config is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
// parse document
|
||||
rapidjson::Document doc;
|
||||
doc.Parse(config.c_str());
|
||||
|
||||
// check parse error
|
||||
auto error = doc.GetParseError();
|
||||
if (error) {
|
||||
log_warning("ScreenResize", "config parse error: {}", error);
|
||||
return;
|
||||
}
|
||||
|
||||
// verify root is a dict
|
||||
if (!doc.IsObject()) {
|
||||
log_warning("ScreenResize", "config not found");
|
||||
return;
|
||||
}
|
||||
|
||||
bool use_game_setting = false;
|
||||
|
||||
std::string root("/");
|
||||
// try to find game-specific setting, if one exists
|
||||
{
|
||||
const auto game = rapidjson::Pointer("/sp2x_games/" + eamuse_get_game()).Get(doc);
|
||||
if (game && game->IsObject()) {
|
||||
use_game_setting = true;
|
||||
root = "/sp2x_games/" + eamuse_get_game() + "/";
|
||||
}
|
||||
}
|
||||
|
||||
log_misc(
|
||||
"ScreenResize",
|
||||
"Loading fullscreen image settings. Game = {}, is_global = {}, JSON path: {}",
|
||||
eamuse_get_game(),
|
||||
use_game_setting,
|
||||
root);
|
||||
load_int_value(doc, root + "offset_x", this->offset_x);
|
||||
load_int_value(doc, root + "offset_y", this->offset_y);
|
||||
load_float_value(doc, root + "scale_x", this->scale_x);
|
||||
load_float_value(doc, root + "scale_y", this->scale_y);
|
||||
load_bool_value(doc, root + "enable_screen_resize", this->enable_screen_resize);
|
||||
load_bool_value(doc, root + "enable_linear_filter", this->enable_linear_filter);
|
||||
load_bool_value(doc, root + "keep_aspect_ratio", this->keep_aspect_ratio);
|
||||
load_bool_value(doc, root + "centered", this->centered);
|
||||
|
||||
// windowed settings are always under game settings
|
||||
root = "/sp2x_games/" + eamuse_get_game() + "/";
|
||||
log_misc(
|
||||
"ScreenResize",
|
||||
"Loading window settings. Game = {}, JSON path: {}",
|
||||
eamuse_get_game(),
|
||||
root);
|
||||
load_bool_value(doc, root + "w_always_on_top", this->window_always_on_top);
|
||||
load_bool_value(doc, root + "w_enable_resize", this->enable_window_resize);
|
||||
load_bool_value(doc, root + "w_keep_aspect_ratio", this->client_keep_aspect_ratio);
|
||||
load_int_value(doc, root + "w_border_type", this->window_decoration);
|
||||
load_uint32_value(doc, root + "w_width", this->client_width);
|
||||
load_uint32_value(doc, root + "w_height", this->client_height);
|
||||
load_int_value(doc, root + "w_offset_x", this->window_offset_x);
|
||||
load_int_value(doc, root + "w_offset_y", this->window_offset_y);
|
||||
}
|
||||
|
||||
bool ScreenResize::load_bool_value(rapidjson::Document& doc, std::string path, bool& value) {
|
||||
const auto v = rapidjson::Pointer(path).Get(doc);
|
||||
if (!v) {
|
||||
log_misc("ScreenResize", "{} not found", path);
|
||||
return false;
|
||||
}
|
||||
if (!v->IsBool()) {
|
||||
log_warning("ScreenResize", "{} is invalid type", path);
|
||||
return false;
|
||||
}
|
||||
value = v->GetBool();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScreenResize::load_int_value(rapidjson::Document& doc, std::string path, int& value) {
|
||||
const auto v = rapidjson::Pointer(path).Get(doc);
|
||||
if (!v) {
|
||||
log_misc("ScreenResize", "{} not found", path);
|
||||
return false;
|
||||
}
|
||||
if (!v->IsInt()) {
|
||||
log_warning("ScreenResize", "{} is invalid type", path);
|
||||
return false;
|
||||
}
|
||||
value = v->GetInt();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScreenResize::load_uint32_value(rapidjson::Document& doc, std::string path, uint32_t& value) {
|
||||
const auto v = rapidjson::Pointer(path).Get(doc);
|
||||
if (!v) {
|
||||
log_misc("ScreenResize", "{} not found", path);
|
||||
return false;
|
||||
}
|
||||
if (!v->IsUint()) {
|
||||
log_warning("ScreenResize", "{} is invalid type", path);
|
||||
return false;
|
||||
}
|
||||
value = v->GetUint();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScreenResize::load_float_value(rapidjson::Document& doc, std::string path, float& value) {
|
||||
const auto v = rapidjson::Pointer(path).Get(doc);
|
||||
if (!v) {
|
||||
log_misc("ScreenResize", "{} not found", path);
|
||||
return false;
|
||||
}
|
||||
if (v->IsInt()) {
|
||||
value = v->GetInt();
|
||||
return true;
|
||||
}
|
||||
if (v->IsDouble()) {
|
||||
value = v->GetDouble();
|
||||
return true;
|
||||
}
|
||||
if (v->IsFloat()) {
|
||||
value = v->GetFloat();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScreenResize::config_save() {
|
||||
log_info("ScreenResize", "saving config: {}", this->config_path.string());
|
||||
|
||||
rapidjson::Document doc;
|
||||
std::string config = fileutils::text_read(this->config_path);
|
||||
if (!config.empty()) {
|
||||
doc.Parse(config.c_str());
|
||||
log_misc("ScreenResize", "existing config file found");
|
||||
}
|
||||
if (!doc.IsObject()) {
|
||||
log_misc("ScreenResize", "clearing out config file");
|
||||
doc.SetObject();
|
||||
}
|
||||
|
||||
// always save under per-game settings
|
||||
std::string root("/sp2x_games/" + eamuse_get_game() + "/");
|
||||
|
||||
log_misc(
|
||||
"ScreenResize",
|
||||
"Game = {}, JSON path = {}",
|
||||
eamuse_get_game(),
|
||||
root);
|
||||
|
||||
// full screen image settings
|
||||
rapidjson::Pointer(root + "offset_x").Set(doc, this->offset_x);
|
||||
rapidjson::Pointer(root + "offset_y").Set(doc, this->offset_y);
|
||||
rapidjson::Pointer(root + "scale_x").Set(doc, this->scale_x);
|
||||
rapidjson::Pointer(root + "scale_y").Set(doc, this->scale_y);
|
||||
rapidjson::Pointer(root + "enable_screen_resize").Set(doc, this->enable_screen_resize);
|
||||
rapidjson::Pointer(root + "enable_linear_filter").Set(doc, this->enable_linear_filter);
|
||||
rapidjson::Pointer(root + "keep_aspect_ratio").Set(doc, this->keep_aspect_ratio);
|
||||
rapidjson::Pointer(root + "centered").Set(doc, this->centered);
|
||||
|
||||
// windowed mode settings
|
||||
rapidjson::Pointer(root + "w_always_on_top").Set(doc, this->window_always_on_top);
|
||||
rapidjson::Pointer(root + "w_enable_resize").Set(doc, this->enable_window_resize);
|
||||
rapidjson::Pointer(root + "w_keep_aspect_ratio").Set(doc, this->client_keep_aspect_ratio);
|
||||
rapidjson::Pointer(root + "w_border_type").Set(doc, this->window_decoration);
|
||||
rapidjson::Pointer(root + "w_width").Set(doc, this->client_width);
|
||||
rapidjson::Pointer(root + "w_height").Set(doc, this->client_height);
|
||||
rapidjson::Pointer(root + "w_offset_x").Set(doc, this->window_offset_x);
|
||||
rapidjson::Pointer(root + "w_offset_y").Set(doc, this->window_offset_y);
|
||||
|
||||
// build JSON
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::PrettyWriter<rapidjson::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.string());
|
||||
}
|
||||
}
|
||||
}
|
||||
70
cfg/screen_resize.h
Normal file
70
cfg/screen_resize.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <filesystem>
|
||||
#include "external/rapidjson/document.h"
|
||||
|
||||
namespace cfg {
|
||||
|
||||
enum WindowDecorationMode {
|
||||
Default = 0,
|
||||
Borderless = 1,
|
||||
ResizableFrame = 2
|
||||
};
|
||||
|
||||
extern std::optional<std::string> SCREEN_RESIZE_CFG_PATH_OVERRIDE;
|
||||
|
||||
class ScreenResize {
|
||||
private:
|
||||
std::filesystem::path config_path;
|
||||
// bool config_dirty = false;
|
||||
|
||||
bool load_bool_value(rapidjson::Document& doc, std::string path, bool& value);
|
||||
bool load_int_value(rapidjson::Document& doc, std::string path, int& value);
|
||||
bool load_uint32_value(rapidjson::Document& doc, std::string path, uint32_t& value);
|
||||
bool load_float_value(rapidjson::Document& doc, std::string path, float& value);
|
||||
|
||||
public:
|
||||
ScreenResize();
|
||||
~ScreenResize();
|
||||
|
||||
// full screen (directx) image settings
|
||||
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 = true;
|
||||
bool keep_aspect_ratio = true;
|
||||
bool centered = true;
|
||||
|
||||
// windowed mode sizing
|
||||
// Windows terminology:
|
||||
// window = rectangle including the frame
|
||||
// client = just the content area without frames.
|
||||
bool window_always_on_top = false;
|
||||
bool client_keep_aspect_ratio = true;
|
||||
bool enable_window_resize = false;
|
||||
int window_decoration = 0; // enum type WindowDecorationMode
|
||||
uint32_t client_width = 0;
|
||||
uint32_t client_height = 0;
|
||||
int32_t window_offset_x = 0;
|
||||
int32_t window_offset_y = 0;
|
||||
|
||||
// these are not saved by config, but used by window management
|
||||
uint32_t init_client_width = 0;
|
||||
uint32_t init_client_height = 0;
|
||||
float init_client_aspect_ratio = 1.f;
|
||||
uint32_t init_window_style = 0;
|
||||
uint32_t window_deco_width = 0;
|
||||
uint32_t window_deco_height = 0;
|
||||
|
||||
void config_load();
|
||||
void config_save();
|
||||
};
|
||||
|
||||
// globals
|
||||
extern std::unique_ptr<cfg::ScreenResize> SCREENRESIZE;
|
||||
}
|
||||
70
cfg/spicecfg.cpp
Normal file
70
cfg/spicecfg.cpp
Normal 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
cfg/spicecfg.h
Normal file
6
cfg/spicecfg.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
int spicecfg_run(const std::vector<std::string> &sextet_devices);
|
||||
Reference in New Issue
Block a user