mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-07 00:50:41 -07:00
api: sdvx tape led (#857)
## Link to GitHub Issue or related Pull Request, if one exists Fixes #852 ## Description of change Add SDVX valk cab tape LED output over API ## Testing tested with custom python script over api
This commit is contained in:
@@ -332,6 +332,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
api/modules/control.cpp
|
api/modules/control.cpp
|
||||||
api/modules/touch.cpp
|
api/modules/touch.cpp
|
||||||
api/modules/iidx.cpp
|
api/modules/iidx.cpp
|
||||||
|
api/modules/sdvx.cpp
|
||||||
api/serial.cpp
|
api/serial.cpp
|
||||||
api/modules/drs.cpp
|
api/modules/drs.cpp
|
||||||
api/modules/lcd.cpp
|
api/modules/lcd.cpp
|
||||||
|
|||||||
@@ -267,6 +267,20 @@ which also means that your hex edits are applicable directly.
|
|||||||
- `Side Panel Right Inner`
|
- `Side Panel Right Inner`
|
||||||
- `Side Panel Right`
|
- `Side Panel Right`
|
||||||
|
|
||||||
|
#### SDVX
|
||||||
|
- tapeled_get(name: str, ...)
|
||||||
|
- returns a list containing a dict of the current tape LED states. The dict keys are:
|
||||||
|
- `Title`
|
||||||
|
- `Upper Left Speaker`
|
||||||
|
- `Upper Right Speaker`
|
||||||
|
- `Left Wing`
|
||||||
|
- `Right Wing`
|
||||||
|
- `Control Panel`
|
||||||
|
- `Lower Left Speaker`
|
||||||
|
- `Lower Right Speaker`
|
||||||
|
- `Woofer`
|
||||||
|
- `V Unit`
|
||||||
|
|
||||||
#### LCD
|
#### LCD
|
||||||
- info()
|
- info()
|
||||||
- returns information about the serial LCD controller some games use
|
- returns information about the serial LCD controller some games use
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace api {
|
||||||
|
|
||||||
|
extern std::atomic_uint32_t CLIENT_COUNT;
|
||||||
|
|
||||||
|
inline bool has_clients() {
|
||||||
|
return CLIENT_COUNT.load(std::memory_order_relaxed) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "client.h"
|
||||||
#include "cfg/configurator.h"
|
#include "cfg/configurator.h"
|
||||||
#include "external/rapidjson/document.h"
|
#include "external/rapidjson/document.h"
|
||||||
#include "util/crypt.h"
|
#include "util/crypt.h"
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
#include "modules/lcd.h"
|
#include "modules/lcd.h"
|
||||||
#include "modules/lights.h"
|
#include "modules/lights.h"
|
||||||
#include "modules/memory.h"
|
#include "modules/memory.h"
|
||||||
|
#include "modules/sdvx.h"
|
||||||
#include "modules/touch.h"
|
#include "modules/touch.h"
|
||||||
#include "modules/resize.h"
|
#include "modules/resize.h"
|
||||||
#include "request.h"
|
#include "request.h"
|
||||||
@@ -36,6 +38,8 @@
|
|||||||
using namespace rapidjson;
|
using namespace rapidjson;
|
||||||
using namespace api;
|
using namespace api;
|
||||||
|
|
||||||
|
std::atomic_uint32_t api::CLIENT_COUNT = 0;
|
||||||
|
|
||||||
Controller::Controller(unsigned short port, std::string password, bool pretty)
|
Controller::Controller(unsigned short port, std::string password, bool pretty)
|
||||||
: port(port), password(std::move(password)), pretty(pretty)
|
: port(port), password(std::move(password)), pretty(pretty)
|
||||||
{
|
{
|
||||||
@@ -411,8 +415,11 @@ void Controller::init_state(api::ClientState *state) {
|
|||||||
state->modules.push_back(new modules::LCD());
|
state->modules.push_back(new modules::LCD());
|
||||||
state->modules.push_back(new modules::Lights());
|
state->modules.push_back(new modules::Lights());
|
||||||
state->modules.push_back(new modules::Memory());
|
state->modules.push_back(new modules::Memory());
|
||||||
|
state->modules.push_back(new modules::SDVX());
|
||||||
state->modules.push_back(new modules::Touch());
|
state->modules.push_back(new modules::Touch());
|
||||||
state->modules.push_back(new modules::Resize());
|
state->modules.push_back(new modules::Resize());
|
||||||
|
|
||||||
|
CLIENT_COUNT.fetch_add(1, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::free_state(api::ClientState *state) {
|
void Controller::free_state(api::ClientState *state) {
|
||||||
@@ -424,6 +431,8 @@ void Controller::free_state(api::ClientState *state) {
|
|||||||
|
|
||||||
// free cipher
|
// free cipher
|
||||||
delete state->cipher;
|
delete state->cipher;
|
||||||
|
|
||||||
|
CLIENT_COUNT.fetch_sub(1, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::free_socket() {
|
void Controller::free_socket() {
|
||||||
|
|||||||
@@ -106,14 +106,16 @@ namespace api::modules {
|
|||||||
void IIDX::copy_tapeled_data(Response &res, Value &response_object, const tapeledutils::tape_led &mapping) {
|
void IIDX::copy_tapeled_data(Response &res, Value &response_object, const tapeledutils::tape_led &mapping) {
|
||||||
// Create an array for the light state
|
// Create an array for the light state
|
||||||
Value light_state(kArrayType);
|
Value light_state(kArrayType);
|
||||||
light_state.Reserve(mapping.data.capacity() * 3, res.doc()->GetAllocator());
|
light_state.Reserve(
|
||||||
|
static_cast<SizeType>(mapping.data.size() * 3),
|
||||||
|
res.doc()->GetAllocator());
|
||||||
for (const auto [r, g, b] : mapping.data) {
|
for (const auto [r, g, b] : mapping.data) {
|
||||||
light_state.PushBack(r, res.doc()->GetAllocator());
|
light_state.PushBack(r, res.doc()->GetAllocator());
|
||||||
light_state.PushBack(g, res.doc()->GetAllocator());
|
light_state.PushBack(g, res.doc()->GetAllocator());
|
||||||
light_state.PushBack(b, res.doc()->GetAllocator());
|
light_state.PushBack(b, res.doc()->GetAllocator());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can't use StringRef here, turns some strings partially into null bytes for some reason
|
// can't use StringRef here, turns some strings partially into null bytes for some reason
|
||||||
Value light_name(mapping.lightName.c_str(), res.doc()->GetAllocator());
|
Value light_name(mapping.lightName.c_str(), res.doc()->GetAllocator());
|
||||||
response_object.AddMember(light_name, light_state, res.doc()->GetAllocator());
|
response_object.AddMember(light_name, light_state, res.doc()->GetAllocator());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
#include "sdvx.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
using namespace std::placeholders;
|
||||||
|
using namespace rapidjson;
|
||||||
|
|
||||||
|
namespace api::modules {
|
||||||
|
|
||||||
|
SDVX::SDVX() : Module("sdvx") {
|
||||||
|
functions["tapeled_get"] = std::bind(&SDVX::tapeled_get, this, _1, _2);
|
||||||
|
|
||||||
|
for (auto &light : games::sdvx::TAPELED_MAPPING) {
|
||||||
|
lights_by_names.emplace(light.lightName, light);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tapeled_get()
|
||||||
|
* tapeled_get(name: str, ...)
|
||||||
|
*/
|
||||||
|
void SDVX::tapeled_get(Request &req, Response &res) {
|
||||||
|
Value response_object(kObjectType);
|
||||||
|
|
||||||
|
// all tape leds
|
||||||
|
if (req.params.Size() == 0) {
|
||||||
|
// iterate through each device and dump its lights data into the response
|
||||||
|
for (const auto &mapping : games::sdvx::TAPELED_MAPPING) {
|
||||||
|
copy_tapeled_data(res, response_object, mapping);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// specified light names
|
||||||
|
for (Value ¶m : req.params.GetArray()) {
|
||||||
|
// check params
|
||||||
|
if (!param.IsString()) {
|
||||||
|
error_type(res, "name", "string");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto name = param.GetString();
|
||||||
|
if (const auto &it = lights_by_names.find(name); it != lights_by_names.end()) {
|
||||||
|
copy_tapeled_data(res, response_object, it->second.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.add_data(response_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDVX::copy_tapeled_data(Response &res, Value &response_object,
|
||||||
|
const tapeledutils::tape_led &mapping)
|
||||||
|
{
|
||||||
|
Value light_state(kArrayType);
|
||||||
|
light_state.Reserve(
|
||||||
|
static_cast<SizeType>(mapping.data.size() * 3),
|
||||||
|
res.doc()->GetAllocator());
|
||||||
|
for (const auto [r, g, b] : mapping.data) {
|
||||||
|
light_state.PushBack(r, res.doc()->GetAllocator());
|
||||||
|
light_state.PushBack(g, res.doc()->GetAllocator());
|
||||||
|
light_state.PushBack(b, res.doc()->GetAllocator());
|
||||||
|
}
|
||||||
|
|
||||||
|
// can't use StringRef here, turns some strings partially into null bytes for some reason
|
||||||
|
Value light_name(mapping.lightName.c_str(), res.doc()->GetAllocator());
|
||||||
|
response_object.AddMember(light_name, light_state, res.doc()->GetAllocator());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "api/module.h"
|
||||||
|
#include "api/request.h"
|
||||||
|
#include "external/robin_hood.h"
|
||||||
|
#include "games/sdvx/sdvx.h"
|
||||||
|
|
||||||
|
namespace api::modules {
|
||||||
|
|
||||||
|
class SDVX : public Module {
|
||||||
|
public:
|
||||||
|
SDVX();
|
||||||
|
|
||||||
|
private:
|
||||||
|
robin_hood::unordered_map<std::string, std::reference_wrapper<tapeledutils::tape_led>> lights_by_names;
|
||||||
|
|
||||||
|
void tapeled_get(Request &req, Response &res);
|
||||||
|
void copy_tapeled_data(Response &res, rapidjson::Value &response_object,
|
||||||
|
const tapeledutils::tape_led &mapping);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ from .coin import *
|
|||||||
from .control import *
|
from .control import *
|
||||||
from .exceptions import *
|
from .exceptions import *
|
||||||
from .iidx import *
|
from .iidx import *
|
||||||
|
from .sdvx import *
|
||||||
from .info import *
|
from .info import *
|
||||||
from .keypads import *
|
from .keypads import *
|
||||||
from .lights import *
|
from .lights import *
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
from .connection import Connection
|
||||||
|
from .request import Request
|
||||||
|
|
||||||
|
|
||||||
|
def sdvx_tapeled_get(con: Connection, *light_names):
|
||||||
|
req = Request("sdvx", "tapeled_get")
|
||||||
|
|
||||||
|
for light_name in light_names:
|
||||||
|
req.add_param(light_name)
|
||||||
|
|
||||||
|
res = con.request(req)
|
||||||
|
return res.get_data()
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
#if SPICE64
|
#if SPICE64
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include "api/client.h"
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
@@ -443,10 +444,12 @@ namespace games::iidx {
|
|||||||
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_g], rgb.g);
|
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_g], rgb.g);
|
||||||
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_b], rgb.b);
|
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_b], rgb.b);
|
||||||
|
|
||||||
for (unsigned int i = 0; i < data_size; ++i) {
|
if (api::has_clients()) {
|
||||||
map.data[i].r = data[i * 3];
|
for (size_t i = 0; i < data_size; ++i) {
|
||||||
map.data[i].g = data[i * 3 + 1];
|
map.data[i].r = data[i * 3];
|
||||||
map.data[i].b = data[i * 3 + 2];
|
map.data[i].g = data[i * 3 + 1];
|
||||||
|
map.data[i].b = data[i * 3 + 2];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#if SPICE64
|
#if SPICE64
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include "api/client.h"
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
@@ -349,43 +350,29 @@ namespace games::sdvx {
|
|||||||
* 9 - v unit - 258 bytes - 86 colors
|
* 9 - v unit - 258 bytes - 86 colors
|
||||||
*
|
*
|
||||||
* data is stored in RGB order, 3 bytes per color
|
* data is stored in RGB order, 3 bytes per color
|
||||||
*
|
|
||||||
* TODO: expose this data via API
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// data mapping
|
|
||||||
static struct TapeLedMapping {
|
|
||||||
size_t data_size;
|
|
||||||
int index_r, index_g, index_b;
|
|
||||||
|
|
||||||
TapeLedMapping(size_t data_size, int index_r, int index_g, int index_b)
|
|
||||||
: data_size(data_size), index_r(index_r), index_g(index_g), index_b(index_b) {}
|
|
||||||
|
|
||||||
} mapping[] = {
|
|
||||||
{ 74, Lights::TITLE_AVG_R, Lights::TITLE_AVG_G, Lights::TITLE_AVG_B },
|
|
||||||
{ 12, Lights::UPPER_LEFT_SPEAKER_AVG_R, Lights::UPPER_LEFT_SPEAKER_AVG_G, Lights::UPPER_LEFT_SPEAKER_AVG_B },
|
|
||||||
{ 12, Lights::UPPER_RIGHT_SPEAKER_AVG_R, Lights::UPPER_RIGHT_SPEAKER_AVG_G, Lights::UPPER_RIGHT_SPEAKER_AVG_B },
|
|
||||||
{ 56, Lights::LEFT_WING_AVG_R, Lights::LEFT_WING_AVG_G, Lights::LEFT_WING_AVG_B },
|
|
||||||
{ 56, Lights::RIGHT_WING_AVG_R, Lights::RIGHT_WING_AVG_G, Lights::RIGHT_WING_AVG_B },
|
|
||||||
{ 94, Lights::CONTROL_PANEL_AVG_R, Lights::CONTROL_PANEL_AVG_G, Lights::CONTROL_PANEL_AVG_B },
|
|
||||||
{ 12, Lights::LOWER_LEFT_SPEAKER_AVG_R, Lights::LOWER_LEFT_SPEAKER_AVG_G, Lights::LOWER_LEFT_SPEAKER_AVG_B },
|
|
||||||
{ 12, Lights::LOWER_RIGHT_SPEAKER_AVG_R, Lights::LOWER_RIGHT_SPEAKER_AVG_G, Lights::LOWER_RIGHT_SPEAKER_AVG_B },
|
|
||||||
{ 14, Lights::WOOFER_AVG_R, Lights::WOOFER_AVG_G, Lights::WOOFER_AVG_B },
|
|
||||||
{ 86, Lights::V_UNIT_AVG_R, Lights::V_UNIT_AVG_G, Lights::V_UNIT_AVG_B },
|
|
||||||
};
|
|
||||||
|
|
||||||
// check index bounds
|
// check index bounds
|
||||||
if (tapeledutils::is_enabled() && index < std::size(mapping)) {
|
if (tapeledutils::is_enabled() && index < std::size(TAPELED_MAPPING)) {
|
||||||
auto &map = mapping[index];
|
auto &map = TAPELED_MAPPING[index];
|
||||||
|
const auto data_size = map.data.size();
|
||||||
|
|
||||||
// pick a color to use
|
// pick a color to use
|
||||||
const auto rgb = tapeledutils::pick_color_from_led_tape(data, map.data_size);
|
const auto rgb = tapeledutils::pick_color_from_led_tape(data, data_size);
|
||||||
|
|
||||||
// program the lights into API
|
// program the lights into API
|
||||||
auto &lights = get_lights();
|
auto &lights = get_lights();
|
||||||
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_r], rgb.r);
|
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_r], rgb.r);
|
||||||
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_g], rgb.g);
|
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_g], rgb.g);
|
||||||
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_b], rgb.b);
|
GameAPI::Lights::writeLight(RI_MGR, lights[map.index_b], rgb.b);
|
||||||
|
|
||||||
|
if (api::has_clients()) {
|
||||||
|
for (size_t i = 0; i < data_size; ++i) {
|
||||||
|
map.data[i].r = data[i * 3];
|
||||||
|
map.data[i].g = data[i * 3 + 1];
|
||||||
|
map.data[i].b = data[i * 3 + 2];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (This != custom_node) {
|
if (This != custom_node) {
|
||||||
|
|||||||
@@ -61,6 +61,19 @@ namespace games::sdvx {
|
|||||||
static HKEY real_asio_reg_handle = nullptr;
|
static HKEY real_asio_reg_handle = nullptr;
|
||||||
static HKEY real_asio_device_reg_handle = nullptr;
|
static HKEY real_asio_device_reg_handle = nullptr;
|
||||||
|
|
||||||
|
tapeledutils::tape_led TAPELED_MAPPING[SDVX_TAPELED_TOTAL] = {
|
||||||
|
{ 74, Lights::TITLE_AVG_R, Lights::TITLE_AVG_G, Lights::TITLE_AVG_B, "Title" },
|
||||||
|
{ 12, Lights::UPPER_LEFT_SPEAKER_AVG_R, Lights::UPPER_LEFT_SPEAKER_AVG_G, Lights::UPPER_LEFT_SPEAKER_AVG_B, "Upper Left Speaker" },
|
||||||
|
{ 12, Lights::UPPER_RIGHT_SPEAKER_AVG_R, Lights::UPPER_RIGHT_SPEAKER_AVG_G, Lights::UPPER_RIGHT_SPEAKER_AVG_B, "Upper Right Speaker" },
|
||||||
|
{ 56, Lights::LEFT_WING_AVG_R, Lights::LEFT_WING_AVG_G, Lights::LEFT_WING_AVG_B, "Left Wing" },
|
||||||
|
{ 56, Lights::RIGHT_WING_AVG_R, Lights::RIGHT_WING_AVG_G, Lights::RIGHT_WING_AVG_B, "Right Wing" },
|
||||||
|
{ 94, Lights::CONTROL_PANEL_AVG_R, Lights::CONTROL_PANEL_AVG_G, Lights::CONTROL_PANEL_AVG_B, "Control Panel" },
|
||||||
|
{ 12, Lights::LOWER_LEFT_SPEAKER_AVG_R, Lights::LOWER_LEFT_SPEAKER_AVG_G, Lights::LOWER_LEFT_SPEAKER_AVG_B, "Lower Left Speaker" },
|
||||||
|
{ 12, Lights::LOWER_RIGHT_SPEAKER_AVG_R, Lights::LOWER_RIGHT_SPEAKER_AVG_G, Lights::LOWER_RIGHT_SPEAKER_AVG_B, "Lower Right Speaker" },
|
||||||
|
{ 14, Lights::WOOFER_AVG_R, Lights::WOOFER_AVG_G, Lights::WOOFER_AVG_B, "Woofer" },
|
||||||
|
{ 86, Lights::V_UNIT_AVG_R, Lights::V_UNIT_AVG_G, Lights::V_UNIT_AVG_B, "V Unit" },
|
||||||
|
};
|
||||||
|
|
||||||
static LONG WINAPI RegOpenKeyA_hook(HKEY hKey, LPCSTR lpSubKey, PHKEY phkResult) {
|
static LONG WINAPI RegOpenKeyA_hook(HKEY hKey, LPCSTR lpSubKey, PHKEY phkResult) {
|
||||||
if (lpSubKey != nullptr &&
|
if (lpSubKey != nullptr &&
|
||||||
phkResult != nullptr &&
|
phkResult != nullptr &&
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
#include "games/game.h"
|
#include "games/game.h"
|
||||||
|
#include "util/tapeled.h"
|
||||||
|
|
||||||
namespace games::sdvx {
|
namespace games::sdvx {
|
||||||
|
|
||||||
@@ -25,6 +26,9 @@ namespace games::sdvx {
|
|||||||
// states
|
// states
|
||||||
extern bool SHOW_VM_MONITOR_WARNING;
|
extern bool SHOW_VM_MONITOR_WARNING;
|
||||||
|
|
||||||
|
constexpr int SDVX_TAPELED_TOTAL = 10;
|
||||||
|
extern tapeledutils::tape_led TAPELED_MAPPING[SDVX_TAPELED_TOTAL];
|
||||||
|
|
||||||
static inline bool is_valkyrie_model() {
|
static inline bool is_valkyrie_model() {
|
||||||
return (
|
return (
|
||||||
avs::game::is_model("KFC") &&
|
avs::game::is_model("KFC") &&
|
||||||
|
|||||||
Reference in New Issue
Block a user