Files
spice2x.github.io/src/spice2x/api/modules/iidx.cpp
T
ASleepyCat 7c15452c1e Add support for outputting IIDX tape LED data via spiceapi (#315)
## Link to GitHub Issue, if one exists
#41 (partially)

## Description of change
This PR adds support for outputting IIDX tape LED data via spiceapi
(`iidx_tapeled_get()`). It returns the data as a `dict`, with the key
being the LED name and the value being the list of RGB values. You can
optionally pass in a variadic list of names to filter the returned data.

The new API function is only implemented in Python since I'm not able to
test the wrappers in other languages.

An example script using the new API is available in this Beef Board PR:
https://github.com/HWXLR8/beef-board/pull/133.

Demo use case: https://streamable.com/pq5e1e

## Testing
Tested in IIDX 32 with custom Beef Board firmware along with a Python
script to read and send the tape LED data over USB.
2025-05-12 21:27:20 -07:00

121 lines
4.1 KiB
C++

#include "iidx.h"
#include <functional>
using namespace std::placeholders;
using namespace rapidjson;
namespace api::modules {
// settings
static const size_t TICKER_SIZE = 9;
IIDX::IIDX() : Module("iidx") {
functions["ticker_get"] = std::bind(&IIDX::ticker_get, this, _1, _2);
functions["ticker_set"] = std::bind(&IIDX::ticker_set, this, _1, _2);
functions["ticker_reset"] = std::bind(&IIDX::ticker_reset, this, _1, _2);
functions["tapeled_get"] = std::bind(&IIDX::tapeled_get, this, _1, _2);
for (auto &light : games::iidx::TAPELED_MAPPING) {
this->lights_by_names.emplace(light.lightName, light);
}
}
/**
* ticker_get()
*/
void IIDX::ticker_get(api::Request &req, Response &res) {
// get led ticker
games::iidx::IIDX_LED_TICKER_LOCK.lock();
Value led_ticker(StringRef(games::iidx::IIDXIO_LED_TICKER, TICKER_SIZE), res.doc()->GetAllocator());
games::iidx::IIDX_LED_TICKER_LOCK.unlock();
// add to response
res.add_data(led_ticker);
}
/**
* ticker_set(text: str)
*/
void IIDX::ticker_set(api::Request &req, api::Response &res) {
// check param
if (req.params.Size() < 1)
return error_params_insufficient(res);
if (!req.params[0].IsString())
return error_type(res, "text", "str");
// get param
auto text = req.params[0].GetString();
auto text_len = req.params[0].GetStringLength();
// lock
std::lock_guard<std::mutex> ticker_lock(games::iidx::IIDX_LED_TICKER_LOCK);
// set to read only
games::iidx::IIDXIO_LED_TICKER_READONLY = true;
// set led ticker
memset(games::iidx::IIDXIO_LED_TICKER, ' ', TICKER_SIZE);
for (size_t i = 0; i < TICKER_SIZE && i < text_len; i++) {
games::iidx::IIDXIO_LED_TICKER[i] = text[i];
}
}
void IIDX::ticker_reset(api::Request &req, api::Response &res) {
// lock
std::lock_guard<std::mutex> ticker_lock(games::iidx::IIDX_LED_TICKER_LOCK);
// disable read only
games::iidx::IIDXIO_LED_TICKER_READONLY = false;
}
/**
* tapeled_get()
* tapeled_get(name: str, ...)
*/
void IIDX::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::iidx::TAPELED_MAPPING) {
copy_tapeled_data(res, response_object, mapping);
}
} else {
// specified light names
for (Value &param : 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()) {
const auto mapping = it->second.get();
copy_tapeled_data(res, response_object, mapping);
}
}
}
res.add_data(response_object);
}
void IIDX::copy_tapeled_data(Response &res, Value &response_object, const tapeledutils::tape_led &mapping) {
// Create an array for the light state
Value light_state(kArrayType);
light_state.Reserve(mapping.data.capacity() * 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());
}
}