This commit is contained in:
bicarus-dev
2026-04-18 23:07:10 -07:00
22 changed files with 412 additions and 118 deletions
+122 -20
View File
@@ -35,24 +35,33 @@ struct ICCA_STATUS_LA9 {
static_assert(sizeof(struct ICCA_STATUS) == 24, "ICCA_STATUS must be 24 bytes");
enum ICCA_WORKFLOW {
STEP,
SLEEP,
START,
INIT,
READY,
GET_USERID,
ACTIVE,
EJECT,
EJECT_CHECK,
END,
CLOSE_EJECT,
CLOSE_E_CHK,
CLOSE_END,
ERR_GETUID = -2
STEP = 0,
SLEEP = 1,
START = 2,
INIT = 3,
READY = 4,
GET_USERID = 5,
ACTIVE = 6,
EJECT = 7,
EJECT_CHECK = 8,
END = 9,
CLOSE_EJECT = 10,
CLOSE_E_CHK = 11,
CLOSE_END = 12,
ERR_GETUID = -2,
ERR_REMOVED = -11
};
// used by RA as the status_code
enum ICCA_STATUS_CODE {
EMPTY = 1,
ENGAGED = 2
};
struct ICCA_UNIT {
struct ICCA_STATUS status {};
enum ICCA_WORKFLOW state = STEP;
enum ICCA_WORKFLOW state_jdz = SLEEP;
bool card_cmd_pressed = false;
bool card_in = false;
double card_in_time = 0.0;
@@ -75,6 +84,11 @@ static inline int icca_get_active_count() {
}
static inline int icca_get_unit_id(int unit_id) {
// on RA the unit_id is zero-indexed
if (avs::game::is_model("JDZ")) {
return unit_id;
}
if (icca_get_active_count() < 2)
return 1;
else {
@@ -122,9 +136,15 @@ static inline void update_card(int unit_id) {
IS_LAST_CARD_FELICA = is_card_uid_felica(unit->status.uid);
unit->state = acio::ICCA_COMPAT_ACTIVE ? START : ACTIVE;
unit->state_jdz = ACTIVE;
unit->status.error = 0;
unit->status.front_sensor = 1;
unit->status.rear_sensor = 1;
} else {
unit->state = ERR_GETUID;
unit->state_jdz = ERR_GETUID;
unit->status.front_sensor = 1;
unit->status.rear_sensor = 1;
memset(unit->status.uid, 0, 8);
}
unit->card_in = true;
@@ -132,10 +152,25 @@ static inline void update_card(int unit_id) {
} else if (unit->state == EJECT_CHECK) {
unit->state = SLEEP;
unit->card_in = false;
unit->status.front_sensor = 0;
unit->status.rear_sensor = 0;
} else if (unit->state_jdz == READY) {
unit->state = GET_USERID;
unit->state_jdz = GET_USERID;
unit->status.error = 0;
unit->status.front_sensor = 1;
unit->status.rear_sensor = 1;
unit->card_in = true;
unit->card_in_time = get_performance_seconds();
}
} else {
unit->state = acio::ICCA_COMPAT_ACTIVE ? START : ACTIVE;
if (unit->state_jdz == READY) {
unit->state_jdz = GET_USERID;
}
unit->status.error = 0;
unit->status.front_sensor = 1;
unit->status.rear_sensor = 1;
unit->card_in = true;
unit->card_in_time = get_performance_seconds();
}
@@ -144,6 +179,12 @@ static inline void update_card(int unit_id) {
unit->state = CLOSE_EJECT;
if (fabs(get_performance_seconds() - unit->card_in_time) > CARD_TIMEOUT) {
unit->card_in = false;
memset(unit->status.uid, 0, 8);
if (unit->state_jdz == ACTIVE || unit->state_jdz == GET_USERID) {
unit->state_jdz = ERR_REMOVED;
}
unit->status.front_sensor = 0;
unit->status.rear_sensor = 0;
}
}
@@ -302,7 +343,11 @@ static char __cdecl ac_io_icca_get_status(void *a1, void *a2) {
// copy state to output buffer
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
unit->status.status_code = unit->state;
if (avs::game::is_model("JDZ")) {
unit->status.status_code = unit->card_in ? ENGAGED : EMPTY;
} else {
unit->status.status_code = unit->state;
}
memcpy(status, &unit->status, sizeof(struct ICCA_STATUS));
// funny workaround
@@ -387,6 +432,11 @@ static char __cdecl ac_io_icca_req_uid(int unit_id) {
static int __cdecl ac_io_icca_req_uid_isfinished(int unit_id, DWORD *read_state) {
unit_id = icca_get_unit_id(unit_id);
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
if (avs::game::is_model("JDZ")) {
// hack for RA - only ever seen it as this
*read_state = 8;
return 1;
}
if (unit->card_in) {
if (fabs(get_performance_seconds() - unit->card_in_time) < CARD_TIMEOUT) {
unit->state = END;
@@ -403,16 +453,61 @@ static int __cdecl ac_io_icca_send_keep_alive_packet(int a1, int a2, int a3) {
return 0;
}
static int __cdecl ac_io_icca_workflow_jdz(int workflow, int unit_id) {
unit_id = icca_get_unit_id(unit_id);
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
switch (workflow) {
// RA uses "STEP" as more of a polling thing it seems
case STEP:
switch (unit->state_jdz) {
case CLOSE_EJECT:
unit->state_jdz = CLOSE_E_CHK;
break;
case CLOSE_E_CHK:
unit->state_jdz = CLOSE_END;
break;
case CLOSE_END:
unit->state_jdz = SLEEP;
break;
default:
break;
}
break;
case SLEEP:
unit->state = SLEEP;
break;
case START:
unit->state_jdz = INIT;
break;
case INIT:
case READY:
unit->state_jdz = READY;
break;
case CLOSE_EJECT:
unit->state_jdz = CLOSE_EJECT;
return CLOSE_E_CHK;
case CLOSE_END:
unit->state_jdz = SLEEP;
break;
default:
// should probably log a warning here
break;
}
return unit->state_jdz;
}
static int __cdecl ac_io_icca_workflow(int workflow, int unit_id) {
// RA uses a different workflow process for the slotted readers; treat it differently
if (avs::game::is_model("JDZ")) {
return ac_io_icca_workflow_jdz(workflow, unit_id);
}
unit_id = icca_get_unit_id(unit_id);
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
switch (workflow) {
case STEP:
if (avs::game::is_model("JDZ"))
unit->state = SLEEP;
else
unit->state = STEP;
unit->state = STEP;
break;
case SLEEP:
unit->state = SLEEP;
@@ -441,7 +536,6 @@ static int __cdecl ac_io_icca_workflow(int workflow, int unit_id) {
default:
break;
}
return unit->state;
}
@@ -467,6 +561,14 @@ acio::ICCAModule::ICCAModule(HMODULE module, acio::HookMode hookMode) : ACIOModu
void acio::ICCAModule::attach() {
ACIOModule::attach();
// workaround for RA; ac_io_icca_cardunit_init is never called so treat like both are initialized
if (avs::game::is_model("JDZ")) {
ICCA_UNITS[0].initialized = true;
ICCA_UNITS[1].initialized = true;
CARD_TIMEOUT = 10.0; // keep the card in the slot for longer
}
// hooks
ACIO_MODULE_HOOK(ac_io_icca_cardunit_init);
ACIO_MODULE_HOOK(ac_io_icca_cardunit_init_isfinished);
+1
View File
@@ -60,6 +60,7 @@ namespace cfg {
// window = rectangle including the frame
// client = just the content area without frames.
bool window_always_on_top = false;
bool window_disable_round_corners = false;
bool client_keep_aspect_ratio = true;
bool enable_window_resize = false;
int window_decoration = 0; // enum type WindowDecorationMode
+1 -2
View File
@@ -915,8 +915,7 @@ namespace games::iidx {
log_warning(
"iidx",
"Failed to apply ASIO compatibility fix for iidx32+. "
"Unless patches are applied, your ASIO device ({}) may hang or fail to work",
ASIO_DRIVER->c_str());
"Unless patches are applied, your ASIO device may hang or fail to work");
} else {
log_info(
"iidx",
+1 -1
View File
@@ -475,7 +475,7 @@ namespace games::popn {
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);
const auto rgb = tapeledutils::pick_color_from_led_tape(map, (uint8_t *)i_pData, data_size);
// program the lights into API
auto &lights = get_lights();
+12
View File
@@ -136,6 +136,18 @@ std::vector<Light> &games::popn::get_lights() {
{"Pika", "IC Card R"},
{"Pika", "IC Card G"},
{"Pika", "IC Card B"},
{"Pika", "Speakers R"},
{"Pika", "Speakers G"},
{"Pika", "Speakers B"},
{"Pika", "Monitor R"},
{"Pika", "Monitor G"},
{"Pika", "Monitor B"},
{"Pika", "Control Panel R"},
{"Pika", "Control Panel G"},
{"Pika", "Control Panel B"},
{"Pika", "Cabinet R"},
{"Pika", "Cabinet G"},
{"Pika", "Cabinet B"},
});
}
+12
View File
@@ -121,6 +121,18 @@ namespace games::popn {
IC_Card_R,
IC_Card_G,
IC_Card_B,
Speaker_R,
Speaker_G,
Speaker_B,
Monitor_R,
Monitor_G,
Monitor_B,
ControlPanel_R,
ControlPanel_G,
ControlPanel_B,
Cabinet_R,
Cabinet_G,
Cabinet_B,
} popn_lights_t;
}
+56 -6
View File
@@ -19,11 +19,13 @@
#include "util/sysutils.h"
#include "io.h"
#include "util/deferlog.h"
#include "misc/nativetouchhook.h"
#include "misc/wintouchemu.h"
namespace games::popn {
bool SHOW_PIKA_MONITOR_WARNING = false;
bool NATIVE_TOUCH = false;
#if SPICE64 && !SPICE_XP
@@ -36,39 +38,83 @@ namespace games::popn {
// 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 4 = speaker, size 40, values 0-0x7F - 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] button lights: these are button lights and handled specially
0,
0,
0,
0,
0,
"Invalid"
},
{
// [1] red pop-kun
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,
0xFF,
"Red Pop-Kun"
},
{
// [2] blue 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,
0xFF,
"Blue Pop-Kun"
},
{
14, // 14*3; game reports 44 but make it 42; these are lights above the banner
// [3] top LED
14, // 14*3; game reports 44 but make it 42
Lights::popn_lights_t::TopLED_R,
Lights::popn_lights_t::TopLED_G,
Lights::popn_lights_t::TopLED_B,
0xFF,
"Top"
},
{
// [4] speakers
13, // 13*3; game reports 40 but make it 39
Lights::popn_lights_t::Speaker_G,
Lights::popn_lights_t::Speaker_R,
Lights::popn_lights_t::Speaker_B,
0x7F,
"Speakers"
},
{
// [5] monitor
6,
Lights::popn_lights_t::Monitor_G,
Lights::popn_lights_t::Monitor_R,
Lights::popn_lights_t::Monitor_B,
0x7F,
"Monitor"
},
{
// [6] control panel
9, // 9*3; game reports 28
Lights::popn_lights_t::ControlPanel_G,
Lights::popn_lights_t::ControlPanel_R,
Lights::popn_lights_t::ControlPanel_B,
0x7F,
"Control Panel"
},
{
// [7] Cabinet
7, // 7*3; game reports 22
Lights::popn_lights_t::Cabinet_G,
Lights::popn_lights_t::Cabinet_R,
Lights::popn_lights_t::Cabinet_B,
0x7F,
"Cabinet"
},
};
@@ -670,9 +716,13 @@ namespace games::popn {
// set third column to 0 and it will work with BIO2
if (!GRAPHICS_WINDOWED) {
wintouchemu::FORCE = true;
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
wintouchemu::hook_title_ends("", "Main Screen", avs::game::DLL_INSTANCE);
if (NATIVE_TOUCH) {
nativetouchhook::hook(avs::game::DLL_INSTANCE);
} else {
wintouchemu::FORCE = true;
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
wintouchemu::hook_title_ends("", "Main Screen", avs::game::DLL_INSTANCE);
}
}
sysutils::hook_EnumDisplayDevicesA();
+2 -1
View File
@@ -17,11 +17,12 @@ namespace games::popn {
}
#if SPICE64
constexpr int POPN_TAPELED_TOTAL = 4;
constexpr int POPN_TAPELED_TOTAL = 8;
extern tapeledutils::tape_led TAPELED_MAPPING[POPN_TAPELED_TOTAL];
#endif
extern bool SHOW_PIKA_MONITOR_WARNING;
extern bool NATIVE_TOUCH;
class POPNGame : public games::Game {
public:
+11
View File
@@ -476,6 +476,14 @@ static HWND WINAPI CreateWindowExW_hook(DWORD dwExStyle, LPCWSTR lpClassName, LP
}
}
// FTT
if (avs::game::is_model("MMD")) {
// set window name
if (!lpWindowName) {
lpWindowName = L"Future TomTom";
}
}
if (GRAPHICS_WINDOWED) {
graphics_window_check_bounds_before_creation(x, y, nWidth, nHeight);
}
@@ -924,6 +932,9 @@ void graphics_hook_subscreen_window(HWND hWnd) {
if (GRAPHICS_WSUB_ALWAYS_ON_TOP) {
graphics_update_z_order(hWnd, true);
}
if (GRAPHICS_WINDOW_DISABLE_ROUNDED_CORNERS) {
graphics_set_corner_preference(hWnd, true);
}
}
void graphics_screens_register(int screen) {
+5
View File
@@ -7,6 +7,9 @@
#include <windows.h>
#include <d3d9.h>
#if !SPICE_XP
#include <dwmapi.h>
#endif
#include "external/toojpeg/toojpeg.h"
@@ -50,6 +53,7 @@ extern std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_WINDOW_SIZE;
extern std::optional<std::string> GRAPHICS_WINDOW_POS;
extern bool GRAPHICS_WINDOW_ALWAYS_ON_TOP;
extern bool GRAPHICS_WINDOW_BACKBUFFER_SCALE;
extern bool GRAPHICS_WINDOW_DISABLE_ROUNDED_CORNERS;
extern std::optional<HWND> GRAPHICS_HOOKED_WINDOW;
extern bool GRAPHICS_IIDX_WSUB;
@@ -103,6 +107,7 @@ void graphics_windowed_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
void graphics_capture_initial_window(HWND hWnd);
void graphics_update_window_style(HWND hWnd, bool force_update=false);
void graphics_update_z_order(HWND hWnd, bool always_on_top);
void graphics_set_corner_preference(HWND hWnd, bool disable);
void graphics_move_resize_window(HWND hWnd);
bool graphics_window_options_breaks_game();
bool graphics_window_decoration_change_crashes_game();
@@ -24,6 +24,7 @@ std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_WINDOW_SIZE;
std::optional<std::string> GRAPHICS_WINDOW_POS;
bool GRAPHICS_WINDOW_ALWAYS_ON_TOP = false;
bool GRAPHICS_WINDOW_BACKBUFFER_SCALE = false;
bool GRAPHICS_WINDOW_DISABLE_ROUNDED_CORNERS = false;
std::optional<HWND> GRAPHICS_HOOKED_WINDOW;
// IIDX Windowed Subscreen - starts out as false, enabled by IIDX module on pre-attach as needed
@@ -132,6 +133,10 @@ void graphics_capture_initial_window(HWND hWnd) {
log_info("graphics-windowed", "[{}] change window z-order - always on top", fmt::ptr(hWnd));
graphics_update_z_order(hWnd, true);
}
if (cfg::SCREENRESIZE->window_disable_round_corners) {
log_info("graphics-windowed", "[{}] disabling rounded corners", fmt::ptr(hWnd));
graphics_set_corner_preference(hWnd, true);
}
// ensure spicetouch coordinates are initialized
update_spicetouch_window_dimensions(hWnd);
@@ -181,6 +186,10 @@ void graphics_load_windowed_parameters() {
cfg::SCREENRESIZE->window_always_on_top = true;
}
if (GRAPHICS_WINDOW_DISABLE_ROUNDED_CORNERS) {
cfg::SCREENRESIZE->window_disable_round_corners = true;
}
log_debug("graphics-windowed", "graphics_load_windowed_parameters returned");
}
@@ -469,6 +478,27 @@ void graphics_update_z_order(HWND hWnd, bool always_on_top) {
log_debug("graphics-windowed", "graphics_update_z_order returned");
}
void graphics_set_corner_preference(HWND hWnd, bool disable) {
if (!GRAPHICS_WINDOWED) {
return;
}
#if !SPICE_XP
log_debug("graphics-windowed", "graphics_set_corner_preference called");
DWM_WINDOW_CORNER_PREFERENCE corner_preference = disable ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT;
DwmSetWindowAttribute(
hWnd,
DWMWA_WINDOW_CORNER_PREFERENCE,
&corner_preference, sizeof(corner_preference));
log_debug("graphics-windowed", "graphics_set_corner_preference returned");
#else
// don't link against DwmSetWindowAttribute on XP
log_debug("graphics-windowed", "graphics_set_corner_preference called; ignoring");
#endif
}
void graphics_move_resize_window(HWND hWnd) {
if (!GRAPHICS_WINDOWED) {
return;
@@ -502,8 +532,8 @@ bool graphics_window_decoration_change_crashes_game() {
static std::once_flag flag;
static bool result = false;
std::call_once(flag, []() {
// ddr crashes when frame style changes
if (avs::game::is_model("MDX")) {
// ddr and ftt crash when frame style changes
if (avs::game::is_model("MDX") || avs::game::is_model("MMD")) {
result = true;
}
+5 -3
View File
@@ -597,6 +597,9 @@ int main_implementation(int argc, char *argv[]) {
if (options[launcher::Options::PopnSubMonitorOverride].is_active()) {
sysutils::SECOND_MONITOR_OVERRIDE = options[launcher::Options::PopnSubMonitorOverride].value_text();
}
if (options[launcher::Options::PopnNativeTouch].value_bool()) {
games::popn::NATIVE_TOUCH = true;
}
if (options[launcher::Options::LoadMetalGearArcadeModule].value_bool()) {
attach_mga = true;
}
@@ -1117,6 +1120,7 @@ int main_implementation(int argc, char *argv[]) {
}
GRAPHICS_WINDOW_ALWAYS_ON_TOP = options[launcher::Options::spice2x_WindowAlwaysOnTop].value_bool();
GRAPHICS_WINDOW_BACKBUFFER_SCALE = options[launcher::Options::WindowForceScaling].value_bool();
GRAPHICS_WINDOW_DISABLE_ROUNDED_CORNERS = options[launcher::Options::WindowDisableRoundedCorners].value_bool();
// IIDX/SDVX Windowed Subscreen
if (options[launcher::Options::spice2x_IIDXWindowedSubscreenSize].is_active()) {
@@ -1831,9 +1835,7 @@ int main_implementation(int argc, char *argv[]) {
attach_ftt = true;
// the game is windowed by default unless we set the env
if (GRAPHICS_WINDOWED) {
GRAPHICS_WINDOWED = false;
} else {
if (!GRAPHICS_WINDOWED) {
SetEnvironmentVariable("DAMAC_VIEWER_FULLSCREEN", "0");
}
+47 -26
View File
@@ -32,6 +32,7 @@ static const std::vector<std::string> CATEGORY_ORDER_BASIC = {
static const std::vector<std::string> CATEGORY_ORDER_ADVANCED = {
"Game Options (Advanced)",
"Game Options (Peripherals)",
"Overlay",
"Network (Advanced)",
"Performance",
@@ -513,7 +514,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.desc = "Flip the camera order.",
.type = OptionType::Bool,
.game_name = "Beatmania IIDX",
.category = "Game Options (Advanced)",
.category = "Game Options (Peripherals)",
},
{
// IIDXDisableCameras
@@ -523,7 +524,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.type = OptionType::Bool,
.hidden = true,
.game_name = "Beatmania IIDX",
.category = "Game Options",
.category = "Game Options (Peripherals)",
},
{
// IIDXCabCamAccess
@@ -533,7 +534,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
"Only turn this on if you have OFFICIAL arcade cameras connected to the correct USB ports. Default: auto.",
.type = OptionType::Enum,
.game_name = "Beatmania IIDX",
.category = "Game Options (Advanced)",
.category = "Game Options (Peripherals)",
.elements = {
{"auto", ""},
{"off", ""},
@@ -549,7 +550,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.desc = "Experimental camera support for IIDX 27+ via texture hooking. Requires camera(s) that support YUY2 or NV12 encoding.",
.type = OptionType::Bool,
.game_name = "Beatmania IIDX",
.category = "Game Options (Advanced)",
.category = "Game Options (Peripherals)",
},
{
// IIDXCamHookRatio
@@ -561,7 +562,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.type = OptionType::Enum,
.hidden = true,
.game_name = "Beatmania IIDX",
.category = "Game Options (Advanced)",
.category = "Game Options (Peripherals)",
.elements = {
{"43", "4:3"},
{"169", "16:9"},
@@ -579,7 +580,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.type = OptionType::Text,
.setting_name = "0x5817a0,0x6fffbd8,0xbbae40,0xe0,0x30",
.game_name = "Beatmania IIDX",
.category = "Game Options (Advanced)",
.category = "Game Options (Peripherals)",
},
{
// IIDXCamHookTopId
@@ -594,7 +595,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.type = OptionType::Text,
.setting_name = "vid_1234&pid_5678",
.game_name = "Beatmania IIDX",
.category = "Game Options (Advanced)",
.category = "Game Options (Peripherals)",
},
{
// IIDXCamHookFrontId
@@ -609,7 +610,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.type = OptionType::Text,
.setting_name = "vid_90ab&pid_cdef",
.game_name = "Beatmania IIDX",
.category = "Game Options (Advanced)",
.category = "Game Options (Peripherals)",
},
{
.title = "IIDX Sound Output Device",
@@ -772,11 +773,11 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options (Advanced)",
},
{
.title = "SDVX Force 720p",
.title = "SDVX Vivid Wave Force 720p Window (DEPRECATED - use -windowsize instead)",
.name = "sdvx720",
.desc = "Force Sound Voltex 720p display mode, used by older games. "
"For newer games, use window resize function instead.",
.desc = "Old & deprecated option for launching Vivid Wave in 720p when using windowed mode.",
.type = OptionType::Bool,
.hidden = true,
.game_name = "Sound Voltex",
.category = "Game Options (Advanced)",
},
@@ -786,7 +787,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.desc = "Enable Sound Voltex printer emulation.",
.type = OptionType::Bool,
.game_name = "Sound Voltex",
.category = "Game Options (Advanced)",
.category = "Game Options (Peripherals)",
},
{
// SDVXPrinterOutputPath
@@ -795,7 +796,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.desc = "Path to folder where images will be stored.",
.type = OptionType::Text,
.game_name = "Sound Voltex",
.category = "Game Options (Advanced)",
.category = "Game Options (Peripherals)",
.picker = OptionPickerType::DirectoryPath,
},
{
@@ -804,7 +805,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.desc = "Clean up saved images in the output directory on startup.",
.type = OptionType::Bool,
.game_name = "Sound Voltex",
.category = "Game Options (Advanced)",
.category = "Game Options (Peripherals)",
},
{
.title = "SDVX Printer Output Overwrite",
@@ -812,7 +813,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.desc = "Always overwrite the same file in output directory.",
.type = OptionType::Bool,
.game_name = "Sound Voltex",
.category = "Game Options (Advanced)",
.category = "Game Options (Peripherals)",
},
{
// SDVXPrinterOutputFormat
@@ -822,7 +823,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.type = OptionType::Text,
.setting_name = "(png/bmp/tga/jpg)",
.game_name = "Sound Voltex",
.category = "Game Options (Advanced)",
.category = "Game Options (Peripherals)",
},
{
.title = "SDVX Printer JPG Quality",
@@ -831,7 +832,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.type = OptionType::Integer,
.setting_name = "(0-100)",
.game_name = "Sound Voltex",
.category = "Game Options (Advanced)",
.category = "Game Options (Peripherals)",
},
{
.title = "SDVX Disable Cameras (DEPRECATED - no longer needed)",
@@ -842,7 +843,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.type = OptionType::Bool,
.hidden = true,
.game_name = "Sound Voltex",
.category = "Game Options",
.category = "Game Options (Peripherals)",
},
{
.title = "SDVX FS Subscreen Native Touch Handling",
@@ -1008,6 +1009,18 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Monitor",
.picker = OptionPickerType::Monitor,
},
{
// PopnNativeTouch
.title = "Pop'n Music PikaPika Native Touch Handling",
.name = "popnnativetouch",
.desc = "Disables touch hooks and lets the game access a touch screen directly. "
"Requires a touch screen to be connected as a secondary monitor. "
"Touch input must be routed to the primary screen via Windows Tablet PC settings. "
"Enable this when you get duplicate touch inputs from an actual touch screen.",
.type = OptionType::Bool,
.game_name = "Pop'n Music",
.category = "Game Options (Advanced)",
},
{
.title = "Force Load HELLO! Pop'n Music Module",
.name = "hpm",
@@ -2218,6 +2231,14 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.type = OptionType::Bool,
.category = "Graphics (Windowed)",
},
{
// WindowDisableRoundedCorners
.title = "Disable Round Window Corners",
.name = "windownoroundcorners",
.desc = "Windows 11 and above only: Disables rounded corners on the game window(s).",
.type = OptionType::Bool,
.category = "Graphics (Windowed)",
},
{
// spice2x_IIDXWindowedSubscreenSize
.title = "IIDX Windowed Subscreen Size",
@@ -2347,7 +2368,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
"Note: this is NOT for the motion camera - you still need a real RealSense camera for that!",
.type = OptionType::Bool,
.game_name = "DANCERUSH",
.category = "Game Options",
.category = "Game Options (Peripherals)",
},
{
// spice2x_IIDXNativeTouch
@@ -2681,7 +2702,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.desc = "Allow game to access camera; camera must be compatible with game.",
.type = OptionType::Bool,
.game_name = "LovePlus",
.category = "Game Options",
.category = "Game Options (Peripherals)",
},
{
// LovePlusPrinterOutputPath
@@ -2690,7 +2711,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.desc = "Path to folder where images will be stored.",
.type = OptionType::Text,
.game_name = "LovePlus",
.category = "Game Options (Advanced)",
.category = "Game Options (Peripherals)",
.picker = OptionPickerType::DirectoryPath,
},
{
@@ -2699,7 +2720,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.desc = "Clean up saved images in the output directory on startup.",
.type = OptionType::Bool,
.game_name = "LovePlus",
.category = "Game Options (Advanced)",
.category = "Game Options (Peripherals)",
},
{
.title = "LovePlus Printer Output Overwrite",
@@ -2707,7 +2728,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.desc = "Always overwrite the same file in output directory.",
.type = OptionType::Bool,
.game_name = "LovePlus",
.category = "Game Options (Advanced)",
.category = "Game Options (Peripherals)",
},
{
// LovePlusPrinterOutputFormat
@@ -2717,7 +2738,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.type = OptionType::Text,
.setting_name = "(png/bmp/tga/jpg)",
.game_name = "LovePlus",
.category = "Game Options (Advanced)",
.category = "Game Options (Peripherals)",
},
{
.title = "LovePlus Printer JPG Quality",
@@ -2726,7 +2747,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.type = OptionType::Integer,
.setting_name = "(0-100)",
.game_name = "LovePlus",
.category = "Game Options (Advanced)",
.category = "Game Options (Peripherals)",
},
{
// OptionConflictResolution
@@ -2747,7 +2768,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
"option to bypass camera error during boot, allowing you to try out the game.",
.type = OptionType::Bool,
.game_name = "Otoca D'or",
.category = "Game Options",
.category = "Game Options (Peripherals)",
}
};
+2
View File
@@ -101,6 +101,7 @@ namespace launcher {
PopnMusicForceSDMode,
PopnNoSub,
PopnSubMonitorOverride,
PopnNativeTouch,
LoadHelloPopnMusicModule,
LoadGitaDoraModule,
GitaDoraTwoChannelAudio,
@@ -232,6 +233,7 @@ namespace launcher {
spice2x_WindowPosition,
spice2x_WindowAlwaysOnTop,
WindowForceScaling,
WindowDisableRoundedCorners,
spice2x_IIDXWindowedSubscreenSize,
spice2x_IIDXWindowedSubscreenPosition,
IIDXWindowedSubscreenBorderless,
+3 -4
View File
@@ -86,13 +86,12 @@ namespace launcher {
if (LAUNCHER_ARGC > 0) {
// build cmd line
std::stringstream cmd_line;
cmd_line << "START \"\" ";
std::string cmd_line = "START \"\" ";
for (int i = 0; i < LAUNCHER_ARGC; i++)
cmd_line << " \"" << LAUNCHER_ARGV[i] << "\"";
cmd_line += " \"" + std::string(LAUNCHER_ARGV[i]) + "\"";
// run command
system(cmd_line.str().c_str());
system(cmd_line.c_str());
}
}
+31 -12
View File
@@ -17,6 +17,7 @@
#include "external/imgui/misc/cpp/imgui_stdlib.h"
#include "games/io.h"
#include "games/sdvx/sdvx.h"
#include "games/popn/popn.h"
#include "avs/core.h"
#include "avs/ea3.h"
#include "avs/game.h"
@@ -283,18 +284,8 @@ namespace overlay::windows {
// keypad buttons
ImGui::TextUnformatted("");
if (this->games_selected_name == "Beatmania IIDX") {
ImGui::Indent(INDENT);
ImGui::TextColored(
ImVec4(1, 0.5f, 0.5f, 1.f),
"WARNING: Lightning Model (TDJ) I/O will ignore the keypad!");
ImGui::TextWrapped(
"Use Toggle Sub Screen button to show the overlay and use your mouse, "
"connect using SpiceCompanion app, or connect a touch screen to enter "
"the PIN.");
ImGui::Unindent(INDENT);
ImGui::TextUnformatted("");
}
this->build_keypad_warning();
auto keypad_buttons = games::get_buttons_keypads(this->games_selected_name);
auto keypad_count = eamuse_get_game_keypads_name();
if (keypad_count == 1) {
@@ -602,6 +593,34 @@ namespace overlay::windows {
}
}
void Config::build_keypad_warning() {
// not checking the -iidxtdj option here, we could do that in the future
const bool is_tdj = this->games_selected_name == "Beatmania IIDX";
// not using games::popn::is_pikapika_model() here since that always returns false on 32-bit
const bool is_popn = this->games_selected_name == "Pop'n Music" && avs::game::SPEC[0] == 'D';
if (!is_tdj && !is_popn) {
return;
}
ImGui::Indent(INDENT);
if (is_tdj) {
ImGui::TextColored(
ImVec4(1, 0.5f, 0.5f, 1.f),
"WARNING: Lightning Model (TDJ) I/O will ignore keypad number input!");
} else if (is_popn) {
ImGui::TextColored(
ImVec4(1, 0.5f, 0.5f, 1.f),
"WARNING: PikaPika Pop-Kun model will ignore keypad number input!");
}
ImGui::TextWrapped(
"Use Toggle Sub Screen button (Overlay tab) to show the overlay and use your mouse, "
"connect using SpiceCompanion app, or connect a touch screen to enter "
"the PIN.");
ImGui::Unindent(INDENT);
ImGui::TextUnformatted("");
}
void Config::build_buttons(const std::string &name, std::vector<Button> *buttons, int min, int max) {
ImGui::AlignTextToFramePadding();
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "%s Buttons", name.c_str());
+5 -3
View File
@@ -193,13 +193,15 @@ namespace overlay::windows {
std::vector<Option> *options, const std::string &category, const std::string *filter=nullptr);
void build_about();
void build_launcher();
void launch_shell(LPCSTR app, LPCSTR file=nullptr);
void build_keypad_warning();
void launch_shell(LPCSTR app, LPCSTR file=nullptr);
void build_menu(int *game_selected);
void shutdown_system(bool force, bool reboot_instead);
void set_alternating_row_colors(const int row_index);
bool validate_ea_card(char card_number[16]);
public:
+8 -10
View File
@@ -628,13 +628,12 @@ namespace overlay::windows {
ImGui::Text("Wheel: %ld", mouse->pos_wheel);
// keys
std::stringstream keys;
keys << "[";
std::string keys = "[";
for (auto key : mouse->key_states) {
keys << (key ? "1," : "0,");
keys += (key ? "1," : "0,");
}
keys << "]";
ImGui::Text("Keys: %s", keys.str().c_str());
keys += "]";
ImGui::Text("Keys: %s", keys.c_str());
break;
}
@@ -643,15 +642,14 @@ namespace overlay::windows {
ImGui::Text("Type: Keyboard");
// keys
std::stringstream keys;
keys << "[";
std::string keys = "[";
for (size_t i = 0; i < std::size(keyboard->key_states); i++) {
if (keyboard->key_states[i]) {
keys << i << ",";
keys += std::to_string(i) + ",";
}
}
keys << "]";
ImGui::Text("Keys: %s", keys.str().c_str());
keys += "]";
ImGui::Text("Keys: %s", keys.c_str());
break;
}
+8 -11
View File
@@ -192,8 +192,10 @@ namespace rawinput::touch {
contact_count = std::max(contact_count, (size_t) hid->value_states_raw[index]);
}
// https://learn.microsoft.com/en-us/windows-hardware/design/component-guidelines/touchscreen-packet-reporting-modes
// hybrid mode devices will report a contact count of 0 for subsequent reports that are
// part of the same initial frame
// most laptops and touchscreens are like this - they have the contact count set to 5 but can actually report up to 10 fingers
if (contact_count > 0) {
if (contact_count > touch_report_count) {
touch.remaining_contact_count = contact_count - touch_report_count;
@@ -216,6 +218,12 @@ namespace rawinput::touch {
touch_points.reserve(touch.elements_x.size());
for (size_t i = 0; i < touch.elements_x.size(); i++) {
// if there are no more touch events to handle, exit out early
if (touch_report_count == 0) {
break;
}
touch_report_count--;
// build touch point
HIDTouchPoint hid_tp{};
auto pos_x = hid->value_states[touch.elements_x[i]];
@@ -247,17 +255,6 @@ namespace rawinput::touch {
hid_tp.y = 1.f - hid_tp.y;
}
// check if this touch point should be considered valid
//
// If "Contact count" reports there are no touch reports remaining and the X and Y
// coordinates of this touch point are zero, then this report element should be
// skipped.
if (touch_report_count == 0 && hid_tp.x == 0.f && hid_tp.y == 0.f) {
continue;
} else if (touch_report_count > 0) {
touch_report_count--;
}
// generate ID (hopefully unique)
hid_tp.id = (DWORD) hid->value_states_raw[touch.elements_contact_identifier[i]];
hid_tp.id += (DWORD) (0xFFFFFF + device->id * 512);
+14 -12
View File
@@ -219,20 +219,21 @@ intptr_t replace_pattern(HMODULE module, const std::string &signature,
strreplace(pattern_str, "??", "00");
auto pattern_bin = std::make_unique<uint8_t[]>(signature.length() / 2);
if (!hex2bin(pattern_str.c_str(), pattern_bin.get())) {
return false;
return 0;
}
// build signature mask
std::ostringstream signature_mask;
std::string signature_mask;
signature_mask.reserve(signature.size() / 2);
for (size_t i = 0; i < signature.length(); i += 2) {
if (signature[i] == '?') {
if (signature[i + 1] == '?') {
signature_mask << '?';
signature_mask += '?';
} else {
return false;
return 0;
}
} else {
signature_mask << 'X';
signature_mask += 'X';
}
}
@@ -241,20 +242,21 @@ intptr_t replace_pattern(HMODULE module, const std::string &signature,
strreplace(replace_data_str, "??", "00");
auto replace_data_bin = std::make_unique<uint8_t[]>(replacement.length() / 2);
if (!hex2bin(replace_data_str.c_str(), replace_data_bin.get())) {
return false;
return 0;
}
// build replace mask
std::ostringstream replace_mask;
std::string replace_mask;
replace_mask.reserve(replacement.size() / 2);
for (size_t i = 0; i < replacement.length(); i += 2) {
if (replacement[i] == '?') {
if (replacement[i + 1] == '?') {
replace_mask << '?';
replace_mask += '?';
} else {
return false;
return 0;
}
} else {
replace_mask << 'X';
replace_mask += 'X';
}
}
@@ -262,11 +264,11 @@ intptr_t replace_pattern(HMODULE module, const std::string &signature,
return replace_pattern(
module,
pattern_bin.get(),
signature_mask.str().c_str(),
signature_mask.c_str(),
offset,
usage,
replace_data_bin.get(),
replace_mask.str().c_str()
replace_mask.c_str()
);
}
+12 -3
View File
@@ -9,7 +9,7 @@ namespace tapeledutils {
}
// for bi2x-style byte array of all colors and LEDs at once
rgb_float3_t pick_color_from_led_tape(uint8_t *data, size_t data_size) {
static rgb_float3_t pick_color_from_led_tape_internal(uint8_t *data, size_t data_size, uint8_t divisor) {
rgb_float3_t result = {0.f, 0.f, 0.f};
if (TAPE_LED_ALGORITHM == TAPE_LED_USE_AVERAGE) {
@@ -25,7 +25,7 @@ namespace tapeledutils {
}
// normalize
const float avg_mult = 1.f / (data_size * 255);
const float avg_mult = 1.f / (data_size * divisor);
result.r = avg_ri * avg_mult;
result.g = avg_gi * avg_mult;
result.b = avg_bi * avg_mult;
@@ -50,7 +50,7 @@ namespace tapeledutils {
}
// normalize
const float single_mult = 1.f / 255;
const float single_mult = 1.f / divisor;
result.r = color[0] * single_mult;
result.g = color[1] * single_mult;
result.b = color[2] * single_mult;
@@ -58,6 +58,15 @@ namespace tapeledutils {
return result;
}
rgb_float3_t pick_color_from_led_tape(tape_led &light, uint8_t *data, size_t data_size) {
const auto max_value = (light.max_value > 0) ? light.max_value : 0xFF;
return pick_color_from_led_tape_internal(data, data_size, max_value);
}
rgb_float3_t pick_color_from_led_tape(uint8_t *data, size_t data_size) {
return pick_color_from_led_tape_internal(data, data_size, 0xFF);
}
// for bi2a-style that calls for each individual LED
size_t get_led_index_using_avg_algo(size_t data_size) {
size_t index_to_use;
+22 -2
View File
@@ -25,13 +25,33 @@ namespace tapeledutils {
struct tape_led {
std::vector<rgb_float3_t> data;
int index_r, index_g, index_b; // Averaged RGB light output indexes
uint8_t max_value;
std::string lightName;
tape_led(size_t data_size, int index_r, int index_g, int index_b, std::string lightName)
: data(std::vector<rgb_float3_t>(data_size)), index_r(index_r), index_g(index_g), index_b(index_b), lightName(std::move(lightName)) {}
tape_led(
size_t data_size,
int index_r, int index_g, int index_b,
std::string lightName)
: data(
std::vector<rgb_float3_t>(data_size)),
index_r(index_r), index_g(index_g), index_b(index_b),
max_value(0),
lightName(std::move(lightName)) {}
tape_led(
size_t data_size,
int index_r, int index_g, int index_b,
uint8_t max_value,
std::string lightName)
: data(
std::vector<rgb_float3_t>(data_size)),
index_r(index_r), index_g(index_g), index_b(index_b),
max_value(max_value),
lightName(std::move(lightName)) {}
};
bool is_enabled();
rgb_float3_t pick_color_from_led_tape(tape_led &light, uint8_t *data, size_t data_size);
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);
}