spicetools/cfg/api.cpp

927 lines
29 KiB
C++

#include "api.h"
#include "rawinput/rawinput.h"
#include "rawinput/piuio.h"
#include "util/time.h"
#include "util/utils.h"
#include "config.h"
using namespace GameAPI;
std::vector<Button> GameAPI::Buttons::getButtons(const std::string &game_name) {
return Config::getInstance().getButtons(game_name);
}
std::vector<Button> GameAPI::Buttons::getButtons(Game *game) {
return Config::getInstance().getButtons(game);
}
std::vector<Button> GameAPI::Buttons::sortButtons(
const std::vector<Button> &buttons,
const std::vector<std::string> &button_names,
const std::vector<unsigned short> *vkey_defaults)
{
std::vector<Button> sorted;
bool button_found;
int index = 0;
for (auto &name : button_names) {
button_found = false;
for (auto &bt : buttons) {
if (name == bt.getName()) {
button_found = true;
sorted.push_back(bt);
break;
}
}
if (!button_found) {
auto &button = sorted.emplace_back(name);
if (vkey_defaults) {
button.setVKey(vkey_defaults->at(index));
}
}
++index;
}
return sorted;
}
GameAPI::Buttons::State GameAPI::Buttons::getState(rawinput::RawInputManager *manager, Button &_button, bool check_alts) {
// check override
if (_button.override_enabled) {
return _button.override_state;
}
// for iterating button alternatives
auto current_button = &_button;
auto alternatives = check_alts ? &current_button->getAlternatives() : nullptr;
unsigned int button_count = 0;
while (true) {
// naive behavior
if (current_button->isNaive()) {
// read
auto vkey = current_button->getVKey();
GameAPI::Buttons::State state;
if (vkey == 0xFF) {
state = BUTTON_NOT_PRESSED;
} else {
state = (GetAsyncKeyState(current_button->getVKey()) & 0x8000) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
}
// invert
if (current_button->getInvert()) {
if (state == BUTTON_PRESSED) {
state = BUTTON_NOT_PRESSED;
} else {
state = BUTTON_PRESSED;
}
}
// return state
if (state != BUTTON_NOT_PRESSED) {
return state;
}
// get next button
button_count++;
if (!alternatives || alternatives->empty() || button_count - 1 >= alternatives->size()) {
return BUTTON_NOT_PRESSED;
} else {
current_button = &alternatives->at(button_count - 1);
continue;
}
}
// get device
auto &devid = current_button->getDeviceIdentifier();
auto device = manager->devices_get(devid, false); // TODO: fix to update only
// get state if device was marked as updated
GameAPI::Buttons::State state = current_button->getLastState();
double *last_up = nullptr;
double *last_down = nullptr;
if (device) {
// lock device
device->mutex->lock();
// get vkey
auto vKey = current_button->getVKey();
// update state based on device type
switch (device->type) {
case rawinput::MOUSE: {
if (vKey < sizeof(device->mouseInfo->key_states)) {
auto mouse = device->mouseInfo;
state = mouse->key_states[vKey] ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
last_up = &mouse->key_up[vKey];
last_down = &mouse->key_down[vKey];
}
break;
}
case rawinput::KEYBOARD: {
if (vKey < sizeof(device->keyboardInfo->key_states)) {
auto kb = device->keyboardInfo;
state = kb->key_states[vKey] ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
last_up = &kb->key_up[vKey];
last_down = &kb->key_down[vKey];
}
break;
}
case rawinput::HID: {
auto hid = device->hidInfo;
auto bat = current_button->getAnalogType();
switch (bat) {
case BAT_NONE: {
auto button_states_it = hid->button_states.begin();
auto button_up_it = hid->button_up.begin();
auto button_down_it = hid->button_down.begin();
while (button_states_it != hid->button_states.end()) {
auto size = button_states_it->size();
if (vKey < size) {
state = (*button_states_it)[vKey] ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
last_up = &(*button_up_it)[vKey];
last_down = &(*button_down_it)[vKey];
break;
} else {
vKey -= size;
++button_states_it;
++button_up_it;
++button_down_it;
}
}
break;
}
case BAT_NEGATIVE:
case BAT_POSITIVE: {
auto value_states = &hid->value_states;
if (vKey < value_states->size()) {
auto value = value_states->at(vKey);
if (current_button->getAnalogType() == BAT_POSITIVE) {
state = value > 0.6f ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
} else {
state = value < 0.4f ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
}
} else {
state = BUTTON_NOT_PRESSED;
}
break;
}
case BAT_HS_UP:
case BAT_HS_UPRIGHT:
case BAT_HS_RIGHT:
case BAT_HS_DOWNRIGHT:
case BAT_HS_DOWN:
case BAT_HS_DOWNLEFT:
case BAT_HS_LEFT:
case BAT_HS_UPLEFT:
case BAT_HS_NEUTRAL: {
auto &value_states = hid->value_states;
if (vKey < value_states.size()) {
auto value = value_states.at(vKey);
// get hat switch values
ButtonAnalogType buffer[3];
Button::getHatSwitchValues(value, buffer);
// check if one of the values match our analog type
state = BUTTON_NOT_PRESSED;
for (ButtonAnalogType &buffer_bat : buffer) {
if (buffer_bat == bat) {
state = BUTTON_PRESSED;
break;
}
}
} else
state = BUTTON_NOT_PRESSED;
break;
}
default:
state = BUTTON_NOT_PRESSED;
break;
}
break;
}
case rawinput::MIDI: {
auto bat = current_button->getAnalogType();
auto midi = device->midiInfo;
switch (bat) {
case BAT_NONE: {
if (vKey < 16 * 128) {
// check for event
auto midi_event = midi->states_events[vKey];
if (midi_event) {
// choose state based on event
state = (midi_event % 2) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
// update event
if (!midi->states[vKey] || midi_event > 1)
midi->states_events[vKey]--;
} else
state = BUTTON_NOT_PRESSED;
}
break;
}
case BAT_MIDI_CTRL_PRECISION: {
if (vKey < 16 * 32)
state = midi->controls_precision[vKey] > 0 ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
else
state = BUTTON_NOT_PRESSED;
break;
}
case BAT_MIDI_CTRL_SINGLE: {
if (vKey < 16 * 44)
state = midi->controls_single[vKey] > 0 ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
else
state = BUTTON_NOT_PRESSED;
break;
}
case BAT_MIDI_CTRL_ONOFF: {
if (vKey < 16 * 6)
state = midi->controls_onoff[vKey] ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
else
state = BUTTON_NOT_PRESSED;
break;
}
case BAT_MIDI_PITCH_DOWN:
state = midi->pitch_bend < 0x2000 ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
break;
case BAT_MIDI_PITCH_UP:
state = midi->pitch_bend > 0x2000 ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
break;
default: {
state = BUTTON_NOT_PRESSED;
break;
}
}
break;
}
case rawinput::PIUIO_DEVICE: {
state = device->piuioDev->IsPressed(vKey) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
}
default:
break;
}
// unlock device
device->mutex->unlock();
}
// debounce
if (state == BUTTON_NOT_PRESSED) {
if (last_up) {
auto debounce_up = current_button->getDebounceUp();
if (debounce_up > 0.0 && get_performance_seconds() - *last_up < debounce_up) {
state = BUTTON_PRESSED;
}
}
} else {
if (last_down) {
auto debounce_down = current_button->getDebounceDown();
if (debounce_down > 0.0 && get_performance_seconds() - *last_down < debounce_down) {
state = BUTTON_NOT_PRESSED;
}
}
}
// set last state
current_button->setLastState(state);
// invert
if (current_button->getInvert()) {
if (state == BUTTON_PRESSED) {
state = BUTTON_NOT_PRESSED;
} else {
state = BUTTON_PRESSED;
}
}
// early quit
if (state == BUTTON_PRESSED) {
return state;
}
// get next button
button_count++;
if (!alternatives || alternatives->empty() || button_count - 1 >= alternatives->size()) {
return BUTTON_NOT_PRESSED;
} else {
current_button = &alternatives->at(button_count - 1);
}
}
}
Buttons::State Buttons::getState(std::unique_ptr<rawinput::RawInputManager> &manager, Button &button, bool check_alts) {
if (manager) {
return getState(manager.get(), button, check_alts);
} else {
return button.getLastState();
}
}
static float getVelocityHelper(rawinput::RawInputManager *manager, Button &button) {
// check override
if (button.override_enabled) {
return button.override_velocity;
}
// naive behavior
if (button.isNaive()) {
if (button.getInvert()) {
return (GetAsyncKeyState(button.getVKey()) & 0x8000) ? 0.f : 1.f;
} else {
return (GetAsyncKeyState(button.getVKey()) & 0x8000) ? 1.f : 0.f;
}
}
// get button state
Buttons::State button_state = Buttons::getState(manager, button, false);
// check if button isn't being pressed
if (button_state != Buttons::BUTTON_PRESSED) {
return 0.f;
}
// get device
auto &devid = button.getDeviceIdentifier();
auto device = manager->devices_get(devid, false);
// return last velocity if device wasn't found
if (!device) {
return button.getLastVelocity();
}
// prepare
float velocity = 1.f;
auto vKey = button.getVKey();
// lock device
device->mutex->lock();
// determine velocity based on device type
switch (device->type) {
case rawinput::MIDI: {
// read control
if (vKey < 16 * 128) {
velocity = (float) device->midiInfo->velocity[vKey] / 127.f;
} else {
velocity = 0.f;
}
// invert
if (button.getInvert()) {
velocity = 1.f - velocity;
}
break;
}
default:
break;
}
// unlock device
device->mutex->unlock();
// set last velocity
button.setLastVelocity(velocity);
// return determined velocity
return velocity;
}
float GameAPI::Buttons::getVelocity(rawinput::RawInputManager *manager, Button &button) {
// get button velocity
auto velocity = getVelocityHelper(manager, button);
// check alternatives
for (auto &alternative : button.getAlternatives()) {
auto alt_velocity = getVelocityHelper(manager, alternative);
if (alt_velocity > velocity) {
velocity = alt_velocity;
}
}
// return highest velocity detected
return velocity;
}
float Buttons::getVelocity(std::unique_ptr<rawinput::RawInputManager> &manager, Button &button) {
if (manager) {
return getVelocity(manager.get(), button);
} else {
return button.getLastVelocity();
}
}
float GameAPI::Analogs::getState(rawinput::Device *device, Analog &analog) {
float value = 0.5f;
if (!device) {
return value;
}
auto index = analog.getIndex();
auto inverted = analog.getInvert();
device->mutex->lock();
// get value from device
switch (device->type) {
case rawinput::MOUSE: {
// get mouse position
auto mouse = device->mouseInfo;
long pos;
switch (index) {
case rawinput::MOUSEPOS_X:
pos = mouse->pos_x;
break;
case rawinput::MOUSEPOS_Y:
pos = mouse->pos_y;
break;
case rawinput::MOUSEPOS_WHEEL:
pos = mouse->pos_wheel;
break;
default:
pos = 0;
break;
}
// apply sensitivity
auto val = (int) roundf(pos * analog.getSensitivity());
if (val < 0) {
inverted = !inverted;
}
// modulo & normalize to [0.0, 1.0]
if (index != rawinput::MOUSEPOS_WHEEL) {
val = std::abs(val) % 257;
value = val / 256.f;
} else {
val = std::abs(val) % 65;
value = val / 64.f;
}
// invert
if (inverted) {
value = 1.f - value;
}
break;
}
case rawinput::HID: {
// get value
if (inverted) {
value = 1.f - device->hidInfo->value_states[index];
} else {
value = device->hidInfo->value_states[index];
}
// 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);
}