diff --git a/src/spice2x/README.md b/src/spice2x/README.md index e7c754d..f224037 100644 --- a/src/spice2x/README.md +++ b/src/spice2x/README.md @@ -247,6 +247,25 @@ which also means that your hex edits are applicable directly. - sets the contents of the 16 segment display and disables writes from game - ticker_reset() - re-enables writes from game +- tapeled_get(name: str, ...) + - returns a list containing a dict of the current tape LED states. The dict keys are: + - `Stage Left` + - `Stage Right` + - `Cabinet Left` + - `Cabinet Right` + - `Control Panel Under` + - `Ceiling Left` + - `Title Left` + - `Title Right` + - `Ceiling Right` + - `Touch Panel Left` + - `Touch Panel Right` + - `Side Panel Left Inner` + - `Side Panel Left Outer` + - `Side Panel Left` + - `Side Panel Right Outer` + - `Side Panel Right Inner` + - `Side Panel Right` #### LCD - info() diff --git a/src/spice2x/api/modules/iidx.cpp b/src/spice2x/api/modules/iidx.cpp index 43860b2..b65423f 100644 --- a/src/spice2x/api/modules/iidx.cpp +++ b/src/spice2x/api/modules/iidx.cpp @@ -1,13 +1,9 @@ #include "iidx.h" #include -#include -#include "games/iidx/iidx.h" -#include "external/rapidjson/document.h" using namespace std::placeholders; using namespace rapidjson; - namespace api::modules { // settings @@ -17,6 +13,11 @@ namespace api::modules { 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); + } } /** @@ -69,4 +70,51 @@ namespace api::modules { // 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 ¶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()) { + 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()); + } } diff --git a/src/spice2x/api/modules/iidx.h b/src/spice2x/api/modules/iidx.h index 50fea1e..41ae20e 100644 --- a/src/spice2x/api/modules/iidx.h +++ b/src/spice2x/api/modules/iidx.h @@ -2,6 +2,7 @@ #include "api/module.h" #include "api/request.h" +#include "games/iidx/iidx.h" namespace api::modules { @@ -10,10 +11,16 @@ namespace api::modules { IIDX(); private: + // state + robin_hood::unordered_map> lights_by_names; // function definitions void ticker_get(Request &req, Response &res); void ticker_set(Request &req, Response &res); void ticker_reset(Request &req, Response &res); + void tapeled_get(Request &req, Response &res); + + // helper + void copy_tapeled_data(Response &res, rapidjson::Value &response_object, const tapeledutils::tape_led &mapping); }; } diff --git a/src/spice2x/api/resources/python/spiceapi/iidx.py b/src/spice2x/api/resources/python/spiceapi/iidx.py index b2736e1..79050a5 100644 --- a/src/spice2x/api/resources/python/spiceapi/iidx.py +++ b/src/spice2x/api/resources/python/spiceapi/iidx.py @@ -16,3 +16,13 @@ def iidx_ticker_set(con: Connection, text: str): def iidx_ticker_reset(con: Connection): req = Request("iidx", "ticker_reset") con.request(req) + + +def iidx_tapeled_get(con: Connection, *light_names): + req = Request("iidx", "tapeled_get") + + for light_name in light_names: + req.add_param(light_name) + + res = con.request(req) + return res.get_data() diff --git a/src/spice2x/games/iidx/bi2x_hook.cpp b/src/spice2x/games/iidx/bi2x_hook.cpp index 4cbd577..3fdae2e 100644 --- a/src/spice2x/games/iidx/bi2x_hook.cpp +++ b/src/spice2x/games/iidx/bi2x_hook.cpp @@ -409,50 +409,27 @@ namespace games::iidx { * 16 - side panel right - 183 bytes - 61 colors * * 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[] = { - { 19, Lights::StageLeftAvgR, Lights::StageLeftAvgG, Lights::StageLeftAvgB }, - { 19, Lights::StageRightAvgR, Lights::StageRightAvgG, Lights::StageRightAvgB }, - { 45, Lights::CabinetLeftAvgR, Lights::CabinetLeftAvgG, Lights::CabinetLeftAvgB }, - { 45, Lights::CabinetRightAvgR, Lights::CabinetRightAvgG, Lights::CabinetRightAvgB }, - { 21, Lights::ControlPanelUnderAvgR, Lights::ControlPanelUnderAvgG, Lights::ControlPanelUnderAvgB }, - { 54, Lights::CeilingLeftAvgR, Lights::CeilingLeftAvgG, Lights::CeilingLeftAvgB }, - { 11, Lights::TitleLeftAvgR, Lights::TitleLeftAvgG, Lights::TitleLeftAvgB }, - { 11, Lights::TitleRightAvgR, Lights::TitleRightAvgG, Lights::TitleRightAvgB }, - { 54, Lights::CeilingRightAvgR, Lights::CeilingRightAvgG, Lights::CeilingRightAvgB }, - { 17, Lights::TouchPanelLeftAvgR, Lights::TouchPanelLeftAvgG, Lights::TouchPanelLeftAvgB }, - { 17, Lights::TouchPanelRightAvgR, Lights::TouchPanelRightAvgG, Lights::TouchPanelRightAvgB }, - { 68, Lights::SidePanelLeftInnerAvgR, Lights::SidePanelLeftInnerAvgG, Lights::SidePanelLeftInnerAvgB }, - { 68, Lights::SidePanelLeftOuterAvgR, Lights::SidePanelLeftOuterAvgG, Lights::SidePanelLeftOuterAvgB }, - { 61, Lights::SidePanelLeftAvgR, Lights::SidePanelLeftAvgG, Lights::SidePanelLeftAvgB }, - { 68, Lights::SidePanelRightOuterAvgR, Lights::SidePanelRightOuterAvgG, Lights::SidePanelRightOuterAvgB }, - { 68, Lights::SidePanelRightInnerAvgR, Lights::SidePanelRightInnerAvgG, Lights::SidePanelRightInnerAvgB }, - { 61, Lights::SidePanelRightAvgR, Lights::SidePanelRightAvgG, Lights::SidePanelRightAvgB }, - }; - // check index bounds - if (tapeledutils::is_enabled() && index < std::size(mapping)) { - auto &map = mapping[index]; + if (tapeledutils::is_enabled() && index < std::size(TAPELED_MAPPING)) { + auto &map = TAPELED_MAPPING[index]; + const auto data_size = map.data.capacity(); // 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 auto &lights = get_lights(); 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_b], rgb.b); + + for (unsigned int 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) { diff --git a/src/spice2x/games/iidx/iidx.cpp b/src/spice2x/games/iidx/iidx.cpp index 32cc1f5..e91f942 100644 --- a/src/spice2x/games/iidx/iidx.cpp +++ b/src/spice2x/games/iidx/iidx.cpp @@ -28,6 +28,8 @@ #include "util/sigscan.h" #include "util/utils.h" +#include "external/robin_hood.h" + #include "bi2a.h" #include "bi2x_hook.h" #include "ezusb.h" @@ -74,6 +76,26 @@ namespace games::iidx { bool IIDXIO_LED_TICKER_READONLY = false; std::mutex IIDX_LED_TICKER_LOCK; + tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL] = { + { 19, Lights::StageLeftAvgR, Lights::StageLeftAvgG, Lights::StageLeftAvgB, "Stage Left" }, + { 19, Lights::StageRightAvgR, Lights::StageRightAvgG, Lights::StageRightAvgB, "Stage Right" }, + { 45, Lights::CabinetLeftAvgR, Lights::CabinetLeftAvgG, Lights::CabinetLeftAvgB, "Cabinet Left" }, + { 45, Lights::CabinetRightAvgR, Lights::CabinetRightAvgG, Lights::CabinetRightAvgB, "Cabinet Right" }, + { 21, Lights::ControlPanelUnderAvgR, Lights::ControlPanelUnderAvgG, Lights::ControlPanelUnderAvgB, "Control Panel Under" }, + { 54, Lights::CeilingLeftAvgR, Lights::CeilingLeftAvgG, Lights::CeilingLeftAvgB, "Ceiling Left" }, + { 11, Lights::TitleLeftAvgR, Lights::TitleLeftAvgG, Lights::TitleLeftAvgB, "Title Left" }, + { 11, Lights::TitleRightAvgR, Lights::TitleRightAvgG, Lights::TitleRightAvgB, "Title Right" }, + { 54, Lights::CeilingRightAvgR, Lights::CeilingRightAvgG, Lights::CeilingRightAvgB, "Ceiling Right" }, + { 17, Lights::TouchPanelLeftAvgR, Lights::TouchPanelLeftAvgG, Lights::TouchPanelLeftAvgB, "Touch Panel Left" }, + { 17, Lights::TouchPanelRightAvgR, Lights::TouchPanelRightAvgG, Lights::TouchPanelRightAvgB, "Touch Panel Right" }, + { 68, Lights::SidePanelLeftInnerAvgR, Lights::SidePanelLeftInnerAvgG, Lights::SidePanelLeftInnerAvgB, "Side Panel Left Inner" }, + { 68, Lights::SidePanelLeftOuterAvgR, Lights::SidePanelLeftOuterAvgG, Lights::SidePanelLeftOuterAvgB, "Side Panel Left Outer" }, + { 61, Lights::SidePanelLeftAvgR, Lights::SidePanelLeftAvgG, Lights::SidePanelLeftAvgB, "Side Panel Left" }, + { 68, Lights::SidePanelRightOuterAvgR, Lights::SidePanelRightOuterAvgG, Lights::SidePanelRightOuterAvgB, "Side Panel Right Outer" }, + { 68, Lights::SidePanelRightInnerAvgR, Lights::SidePanelRightInnerAvgG, Lights::SidePanelRightInnerAvgB, "Side Panel Right Inner" }, + { 61, Lights::SidePanelRightAvgR, Lights::SidePanelRightAvgG, Lights::SidePanelRightAvgB, "Side Panel Right" }, + }; + static LONG WINAPI RegOpenKeyA_hook(HKEY hKey, LPCSTR lpSubKey, PHKEY phkResult) { if (lpSubKey != nullptr && phkResult != nullptr) { if (hKey == HKEY_LOCAL_MACHINE && diff --git a/src/spice2x/games/iidx/iidx.h b/src/spice2x/games/iidx/iidx.h index f1b10b1..0e92368 100644 --- a/src/spice2x/games/iidx/iidx.h +++ b/src/spice2x/games/iidx/iidx.h @@ -2,9 +2,13 @@ #include #include +#include #include "games/game.h" +#include "external/robin_hood.h" +#include "util/tapeled.h" + namespace games::iidx { // settings @@ -30,6 +34,10 @@ namespace games::iidx { extern bool IIDXIO_LED_TICKER_READONLY; extern std::mutex IIDX_LED_TICKER_LOCK; + constexpr int IIDX_TAPELED_TOTAL = 17; + // data mapping + extern tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL]; + class IIDXGame : public games::Game { public: IIDXGame(); diff --git a/src/spice2x/util/tapeled.h b/src/spice2x/util/tapeled.h index be18eca..6687bca 100644 --- a/src/spice2x/util/tapeled.h +++ b/src/spice2x/util/tapeled.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include namespace tapeledutils { @@ -20,6 +22,15 @@ namespace tapeledutils { float b; } rgb_float3_t; + struct tape_led { + std::vector data; + int index_r, index_g, index_b; // Averaged RGB light output indexes + std::string lightName; + + tape_led(size_t data_size, int index_r, int index_g, int index_b, std::string lightName) + : data(std::vector(data_size)), index_r(index_r), index_g(index_g), index_b(index_b), lightName(std::move(lightName)) {} + }; + bool is_enabled(); rgb_float3_t pick_color_from_led_tape(uint8_t *data, size_t data_size); size_t get_led_index_using_avg_algo(size_t data_size);