diff --git a/src/spice2x/games/popn/bi3a_hook.cpp b/src/spice2x/games/popn/bi3a_hook.cpp index a9e99e7..18acaa0 100644 --- a/src/spice2x/games/popn/bi3a_hook.cpp +++ b/src/spice2x/games/popn/bi3a_hook.cpp @@ -1,6 +1,6 @@ #include "bi3a_hook.h" -#if SPICE64 +#if SPICE64 && !SPICE_XP #include #include @@ -9,6 +9,7 @@ #include "rawinput/rawinput.h" #include "misc/eamuse.h" #include "games/io.h" +#include "popn.h" #include "io.h" #include "util/tapeled.h" #include @@ -54,19 +55,22 @@ namespace games::popn { union AIO_IOB5_BI3A__DEVSTATUS { uint8_t RawBytes[648]; struct { - uint8_t Reserved0[44]; // 0-43 - uint8_t TestButton; // 44, io code offset rcx+0xe4 - uint8_t ServiceButton; // 45, io code offset rcx+0xe5 - uint8_t CoinMech; // 46, io code offset rcx+0xe6 - uint8_t Reserved1[19]; // 47-65 - uint8_t JOYL_SW; // 66 - uint8_t Reserved2[4]; // 67-70 - uint8_t JOYR_SW; // 71 - uint8_t Headphones; // 72 - uint8_t Recorder; // 73, unused - uint8_t Reserved3[295]; // 74-368 - uint8_t Buttons_0_8; // 369 - uint8_t Button_9; // 370 + uint8_t Reserved0[27]; // 0-26 + uint8_t Coin0; // 27 + uint8_t Coin1; // 28 + uint8_t Reserved1[15]; // 29-43 + uint8_t TestButton; // 44, io code offset rcx+0xe4 + uint8_t ServiceButton; // 45, io code offset rcx+0xe5 + uint8_t CoinMech; // 46, io code offset rcx+0xe6 + uint8_t Reserved2[19]; // 47-65 + uint8_t JOYL_SW; // 66 + uint8_t Reserved3[4]; // 67-70 + uint8_t JOYR_SW; // 71 + uint8_t Headphones; // 72 + uint8_t Recorder; // 73, unused + uint8_t Reserved4[295]; // 74-368 + uint8_t Buttons_0_8; // 369 + uint8_t Button_9; // 370 } Input; }; @@ -244,7 +248,7 @@ namespace games::popn { if (i_pSci == aioSciComm) { aioNmgrIob5 = new AIO_NMGR_IOB5; - log_info("bi2x_hook", "aioNMgrIob5_Create called, returning custom AIO_NMGR_IOB5: {}", fmt::ptr(aioNmgrIob5)); + log_info("bi3a_hook", "aioNMgrIob5_Create called, returning custom AIO_NMGR_IOB5: {}", fmt::ptr(aioNmgrIob5)); return aioNmgrIob5; } else { return aioNMgrIob5_Create_orig(i_pSci, i_bfMode); @@ -253,11 +257,11 @@ namespace games::popn { static AIO_IOB5_BI3A *__fastcall aioIob5Bi3a_Create(AIO_NMGR_IOB5 *i_pNodeMgr, uint8_t i, void *p) { - log_info("bi2x_hook", "aioIob5Bi3a_Create called with i_pNodeMgr={}, i={}, p={}", fmt::ptr(i_pNodeMgr), i, fmt::ptr(p)); + log_info("bi3a_hook", "aioIob5Bi3a_Create called with i_pNodeMgr={}, i={}, p={}", fmt::ptr(i_pNodeMgr), i, fmt::ptr(p)); if (i_pNodeMgr == aioNmgrIob5) { aioIob5Bi3a = new AIO_IOB5_BI3A; - log_info("bi2x_hook", "aioIob5Bi3a_Create: returning custom AIO_IOB5_BI3A: {}", fmt::ptr(aioIob5Bi3a)); + log_info("bi3a_hook", "aioIob5Bi3a_Create: returning custom AIO_IOB5_BI3A: {}", fmt::ptr(aioIob5Bi3a)); return aioIob5Bi3a; } else { return aioIob5Bi3a_Create_orig(i_pNodeMgr, i, p); @@ -326,7 +330,8 @@ namespace games::popn { o_DevStatus->Input.Headphones = 1; } - // TODO: coin + // coin + o_DevStatus->Input.Coin0 += eamuse_coin_get_stock(); } static void __fastcall aioIob5Bi3a_SetOutputData(AIO_IOB5_BI3A *i_pNodeCtl, uint32_t i_CnPin, uint8_t i_Data) { @@ -344,6 +349,15 @@ namespace games::popn { std::optional light; switch (i_CnPin) { + case 7: + light = Lights::IC_Card_R; + break; + case 8: + light = Lights::IC_Card_G; + break; + case 9: + light = Lights::IC_Card_B; + break; case 10: light = Lights::WooferLED_R; break; @@ -376,9 +390,10 @@ namespace games::popn { struct PopnLight { int data_index; Lights::popn_lights_t light; + uint8_t size; PopnLight( - int data_index, Lights::popn_lights_t light) : - data_index(data_index), light(light) {} + int data_index, Lights::popn_lights_t light, uint8_t size) : + data_index(data_index), light(light), size(size) {} }; static void __fastcall aioIob5Bi3a_SetTapeLedDataPart( @@ -403,15 +418,14 @@ namespace games::popn { log_info( "bi3a_hook", - "aioIob5Bi3a_SetTapeLedDataPart Pin={}, Size={}, data@{} = {}", - i_CnPin, number_of_leds, fmt::ptr(i_pData), data); + "aioIob5Bi3a_SetTapeLedDataPart Pin={}, Size={}, Type={}, data@{} = {}", + i_CnPin, number_of_leds, (uint8_t)i_LedType, fmt::ptr(i_pData), data); } auto &lights = get_lights(); - - // special handling converting to non-RGB lights - // take the max(R, G, B) and use it to write the light value if (i_CnPin == 0 && number_of_leds == 9 * 3) { + // special handling converting to non-RGB lights + // take the max(R, G, B) and use it to write the light value uint8_t light_value[9] = { 0 }; for (uint32_t i = 0; i < number_of_leds; i += 3) { light_value[i / 3] = @@ -430,6 +444,8 @@ namespace games::popn { Lights::popn_lights_t::Button9 }; + static_assert(std::size(legacy_button_lights) == 9); + for (size_t light = 0; light < 9; light++) { // on the new cab, buttons are colorless plastic and they rely on RGB to be lit at all times, even when "off" // when translating to legacy on/off lights, treat anything above ~60% brightness as fully on, @@ -439,16 +455,50 @@ namespace games::popn { lights.at(legacy_button_lights[light]), light_value[light] > 150 ? 1.f : 0.f); } + + // color buttons + constexpr Lights::popn_lights_t button_lights[] = { + Lights::popn_lights_t::PikaButton1_R, Lights::popn_lights_t::PikaButton1_G, Lights::popn_lights_t::PikaButton1_B, + Lights::popn_lights_t::PikaButton2_R, Lights::popn_lights_t::PikaButton2_G, Lights::popn_lights_t::PikaButton2_B, + Lights::popn_lights_t::PikaButton3_R, Lights::popn_lights_t::PikaButton3_G, Lights::popn_lights_t::PikaButton3_B, + Lights::popn_lights_t::PikaButton4_R, Lights::popn_lights_t::PikaButton4_G, Lights::popn_lights_t::PikaButton4_B, + Lights::popn_lights_t::PikaButton5_R, Lights::popn_lights_t::PikaButton5_G, Lights::popn_lights_t::PikaButton5_B, + Lights::popn_lights_t::PikaButton6_R, Lights::popn_lights_t::PikaButton6_G, Lights::popn_lights_t::PikaButton6_B, + Lights::popn_lights_t::PikaButton7_R, Lights::popn_lights_t::PikaButton7_G, Lights::popn_lights_t::PikaButton7_B, + Lights::popn_lights_t::PikaButton8_R, Lights::popn_lights_t::PikaButton8_G, Lights::popn_lights_t::PikaButton8_B, + Lights::popn_lights_t::PikaButton9_R, Lights::popn_lights_t::PikaButton9_G, Lights::popn_lights_t::PikaButton9_B + }; + + static_assert(std::size(button_lights) == 9 * 3); + + for (size_t light = 0; light < number_of_leds; light++) { + GameAPI::Lights::writeLight( + RI_MGR, + lights.at(button_lights[light]), + ((uint8_t *)i_pData)[light] / 255.f); + } } - // pin 0 = buttons 1-3, size 9 (but it's actually triple that in reality - game passes in wrong length), values 0-0xFF - // pin 1 = left pop (red pop kun), size 16, values 0-0xFF - // pin 2 = right pop (blue pop kun), size 16, values 0-0xFF - // pin 3 = title, size 44, values 0-0xFF - // pin 4 = speaker, size 40, values 0-0xFF - U region only - // pin 5 = monitor, size 18, values 0-0x7F - U region only - // pin 6 = control panel, size 28, values 0-0x7F - U region only - // pin 7 = cabinet, size 22, values 0-0x7F - U region only + if (tapeledutils::is_enabled() && i_CnPin > 0 && i_CnPin < std::size(TAPELED_MAPPING)) { + auto &map = TAPELED_MAPPING[i_CnPin]; + const auto data_size = std::min(map.data.capacity(), (size_t)number_of_leds / 3); + + // pick a color to use + const auto rgb = tapeledutils::pick_color_from_led_tape((uint8_t *)i_pData, 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); + + // tape LED output over API not implemented + // for (size_t i = 0; i < data_size; i++) { + // map.data[i].r = ((uint8_t *)i_pData)[i * 3]; + // map.data[i].g = ((uint8_t *)i_pData)[i * 3 + 1]; + // map.data[i].b = ((uint8_t *)i_pData)[i * 3 + 2]; + // } + } } void bi3a_hook_init() { diff --git a/src/spice2x/games/popn/bi3a_hook.h b/src/spice2x/games/popn/bi3a_hook.h index 38be875..b0f7fdc 100644 --- a/src/spice2x/games/popn/bi3a_hook.h +++ b/src/spice2x/games/popn/bi3a_hook.h @@ -1,6 +1,6 @@ #pragma once -#if SPICE64 +#if SPICE64 && !SPICE_XP namespace games::popn { void bi3a_hook_init(); diff --git a/src/spice2x/games/popn/io.cpp b/src/spice2x/games/popn/io.cpp index 236284b..c4c156d 100644 --- a/src/spice2x/games/popn/io.cpp +++ b/src/spice2x/games/popn/io.cpp @@ -43,65 +43,100 @@ std::vector &games::popn::get_lights() { if (lights.empty()) { lights = GameAPI::Lights::getLights("Pop'n Music"); - GameAPI::Lights::sortLights( - &lights, - "Button 1", - "Button 2", - "Button 3", - "Button 4", - "Button 5", - "Button 6", - "Button 7", - "Button 8", - "Button 9", - "Top LED 1", - "Top LED 2", - "Top LED 3", - "Top LED 4", - "Top LED 5", - "Top LED 6", - "Top LED 7", - "Top LED 8", - "Top LED 9", - "Top LED 10", - "Top LED 11", - "Top LED 12", - "Top LED 13", - "Top LED 14", - "Top LED 15", - "Top LED 16", - "Top LED 17", - "Top LED 18", - "Top LED 19", - "Top LED 20", - "Top LED 21", - "Top LED 22", - "Top LED 23", - "Top LED 24", - "Top LED 25", - "Top LED 26", - "Top LED 27", - "Top LED 28", - "Top LED 29", - "Top LED 30", - "Top LED 31", - "Top LED 32", - "Top LED R", - "Top LED G", - "Top LED B", - "Hi Lamp 1", - "Hi Lamp 2", - "Hi Lamp 3", - "Hi Lamp 4", - "Hi Lamp 5", - "Left Lamp 1", - "Left Lamp 2", - "Right Lamp 1", - "Right Lamp 2", - "Woofer LED R", - "Woofer LED G", - "Woofer LED B" - ); + GameAPI::Lights::sortLightsWithCategory(&lights, { + {"Common", "Button 1"}, + {"Common", "Button 2"}, + {"Common", "Button 3"}, + {"Common", "Button 4"}, + {"Common", "Button 5"}, + {"Common", "Button 6"}, + {"Common", "Button 7"}, + {"Common", "Button 8"}, + {"Common", "Button 9"}, + {"SD", "Hi Lamp 1"}, + {"SD", "Hi Lamp 2"}, + {"SD", "Hi Lamp 3"}, + {"SD", "Hi Lamp 4"}, + {"SD", "Hi Lamp 5"}, + {"SD", "Left Lamp 1"}, + {"SD", "Left Lamp 2"}, + {"SD", "Right Lamp 1"}, + {"SD", "Right Lamp 2"}, + {"HD", "Top LED 1"}, + {"HD", "Top LED 2"}, + {"HD", "Top LED 3"}, + {"HD", "Top LED 4"}, + {"HD", "Top LED 5"}, + {"HD", "Top LED 6"}, + {"HD", "Top LED 7"}, + {"HD", "Top LED 8"}, + {"HD", "Top LED 9"}, + {"HD", "Top LED 10"}, + {"HD", "Top LED 11"}, + {"HD", "Top LED 12"}, + {"HD", "Top LED 13"}, + {"HD", "Top LED 14"}, + {"HD", "Top LED 15"}, + {"HD", "Top LED 16"}, + {"HD", "Top LED 17"}, + {"HD", "Top LED 18"}, + {"HD", "Top LED 19"}, + {"HD", "Top LED 20"}, + {"HD", "Top LED 21"}, + {"HD", "Top LED 22"}, + {"HD", "Top LED 23"}, + {"HD", "Top LED 24"}, + {"HD", "Top LED 25"}, + {"HD", "Top LED 26"}, + {"HD", "Top LED 27"}, + {"HD", "Top LED 28"}, + {"HD", "Top LED 29"}, + {"HD", "Top LED 30"}, + {"HD", "Top LED 31"}, + {"HD", "Top LED 32"}, + {"HD/Pika", "Top LED R"}, + {"HD/Pika", "Top LED G"}, + {"HD/Pika", "Top LED B"}, + {"HD/Pika", "Woofer LED R"}, + {"HD/Pika", "Woofer LED G"}, + {"HD/Pika", "Woofer LED B"}, + {"Pika", "Button 1 R"}, + {"Pika", "Button 1 G"}, + {"Pika", "Button 1 B"}, + {"Pika", "Button 2 R"}, + {"Pika", "Button 2 G"}, + {"Pika", "Button 2 B"}, + {"Pika", "Button 3 R"}, + {"Pika", "Button 3 G"}, + {"Pika", "Button 3 B"}, + {"Pika", "Button 4 R"}, + {"Pika", "Button 4 G"}, + {"Pika", "Button 4 B"}, + {"Pika", "Button 5 R"}, + {"Pika", "Button 5 G"}, + {"Pika", "Button 5 B"}, + {"Pika", "Button 6 R"}, + {"Pika", "Button 6 G"}, + {"Pika", "Button 6 B"}, + {"Pika", "Button 7 R"}, + {"Pika", "Button 7 G"}, + {"Pika", "Button 7 B"}, + {"Pika", "Button 8 R"}, + {"Pika", "Button 8 G"}, + {"Pika", "Button 8 B"}, + {"Pika", "Button 9 R"}, + {"Pika", "Button 9 G"}, + {"Pika", "Button 9 B"}, + {"Pika", "Red Pop-Kun R"}, + {"Pika", "Red Pop-Kun G"}, + {"Pika", "Red Pop-Kun B"}, + {"Pika", "Blue Pop-Kun R"}, + {"Pika", "Blue Pop-Kun G"}, + {"Pika", "Blue Pop-Kun B"}, + {"Pika", "IC Card R"}, + {"Pika", "IC Card G"}, + {"Pika", "IC Card B"}, + }); } return lights; diff --git a/src/spice2x/games/popn/io.h b/src/spice2x/games/popn/io.h index 84f4559..e2f1393 100644 --- a/src/spice2x/games/popn/io.h +++ b/src/spice2x/games/popn/io.h @@ -38,6 +38,15 @@ namespace games::popn { Button7, Button8, Button9, + HiLamp1, + HiLamp2, + HiLamp3, + HiLamp4, + HiLamp5, + LeftLamp1, + LeftLamp2, + RightLamp1, + RightLamp2, TopLED1, TopLED2, TopLED3, @@ -73,18 +82,45 @@ namespace games::popn { TopLED_R, TopLED_G, TopLED_B, - HiLamp1, - HiLamp2, - HiLamp3, - HiLamp4, - HiLamp5, - LeftLamp1, - LeftLamp2, - RightLamp1, - RightLamp2, WooferLED_R, WooferLED_G, WooferLED_B, + PikaButton1_R, + PikaButton1_G, + PikaButton1_B, + PikaButton2_R, + PikaButton2_G, + PikaButton2_B, + PikaButton3_R, + PikaButton3_G, + PikaButton3_B, + PikaButton4_R, + PikaButton4_G, + PikaButton4_B, + PikaButton5_R, + PikaButton5_G, + PikaButton5_B, + PikaButton6_R, + PikaButton6_G, + PikaButton6_B, + PikaButton7_R, + PikaButton7_G, + PikaButton7_B, + PikaButton8_R, + PikaButton8_G, + PikaButton8_B, + PikaButton9_R, + PikaButton9_G, + PikaButton9_B, + RedPopKun_R, + RedPopKun_G, + RedPopKun_B, + BluePopKun_R, + BluePopKun_G, + BluePopKun_B, + IC_Card_R, + IC_Card_G, + IC_Card_B, } popn_lights_t; } diff --git a/src/spice2x/games/popn/popn.cpp b/src/spice2x/games/popn/popn.cpp index 006d49b..ac3f0f1 100644 --- a/src/spice2x/games/popn/popn.cpp +++ b/src/spice2x/games/popn/popn.cpp @@ -32,6 +32,46 @@ namespace games::popn { constexpr LONG FAKE_MONITOR_WIDTH = 1280; constexpr LONG FAKE_MONITOR_HEIGHT = 800; + // pin 0 = buttons 1-3, size 9 (but it's actually triple that in reality - game passes in wrong length), values 0-0xFF + // pin 1 = left pop (red pop kun), size 16, values 0-0xFF + // pin 2 = right pop (blue pop kun), size 16, values 0-0xFF + // pin 3 = title, size 44, values 0-0xFF + // pin 4 = speaker, size 40, values 0-0xFF - U region only + // pin 5 = monitor, size 18, values 0-0x7F - U region only + // pin 6 = control panel, size 28, values 0-0x7F - U region only + // pin 7 = cabinet, size 22, values 0-0x7F - U region only + tapeledutils::tape_led TAPELED_MAPPING[POPN_TAPELED_TOTAL] = { + { + 0, // these are button lights and handled specially + 0, + 0, + 0, + "Invalid" + }, + { + 5, // 5*3; game reports 16 but make it 15 + Lights::popn_lights_t::RedPopKun_R, + Lights::popn_lights_t::RedPopKun_G, + Lights::popn_lights_t::RedPopKun_B, + "Red Pop-Kun" + }, + { + 5, // 5*3; game reports 16 but make it 15 + Lights::popn_lights_t::BluePopKun_R, + Lights::popn_lights_t::BluePopKun_G, + Lights::popn_lights_t::BluePopKun_B, + "Blue Pop-Kun" + }, + { + 14, // 14*3; game reports 44 but make it 42; these are lights above the banner + Lights::popn_lights_t::TopLED_R, + Lights::popn_lights_t::TopLED_G, + Lights::popn_lights_t::TopLED_B, + "Top" + }, + }; + + static decltype(GetDisplayConfigBufferSizes) *GetDisplayConfigBufferSizes_orig = nullptr; static decltype(QueryDisplayConfig) *QueryDisplayConfig_orig = nullptr; static decltype(DisplayConfigGetDeviceInfo) *DisplayConfigGetDeviceInfo_orig = nullptr; diff --git a/src/spice2x/games/popn/popn.h b/src/spice2x/games/popn/popn.h index e11918e..caebe6e 100644 --- a/src/spice2x/games/popn/popn.h +++ b/src/spice2x/games/popn/popn.h @@ -2,6 +2,7 @@ #include "avs/game.h" #include "games/game.h" +#include "util/tapeled.h" namespace games::popn { @@ -15,6 +16,11 @@ namespace games::popn { } +#if SPICE64 + constexpr int POPN_TAPELED_TOTAL = 4; + extern tapeledutils::tape_led TAPELED_MAPPING[POPN_TAPELED_TOTAL]; +#endif + extern bool SHOW_PIKA_MONITOR_WARNING; class POPNGame : public games::Game {