mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-02 06:40:42 -07:00
Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 06374ef78a | |||
| bdde1ec6b8 | |||
| d2176fe4f0 | |||
| fb9eef904a | |||
| 48903998a5 | |||
| f2dca0265f | |||
| 1147ed9858 | |||
| 19d93f4ffd | |||
| 15bfffa1d3 | |||
| 18a9a47c33 | |||
| 6838e69ebe | |||
| 26b3b64794 | |||
| aadd2bdd59 | |||
| d0e24e31ca | |||
| 4fb7f20c7b | |||
| 6ddae70c5a | |||
| b38160d6c2 | |||
| 231c998cc0 | |||
| 127d31a85c | |||
| a8419bbaa0 | |||
| 0fc430ceb3 | |||
| e59a50b64c | |||
| 2042b629e9 | |||
| 440b9ed749 | |||
| 9936088286 | |||
| 4d8a6c1952 | |||
| af69cf9251 | |||
| 1d5550b1e8 | |||
| ec3f6639b7 | |||
| 727b99effd | |||
| 8afff3aaca | |||
| b281517429 | |||
| 54ec9f272e | |||
| a159691097 | |||
| ca70f7b19c | |||
| 7b4227d2a9 | |||
| f89efa2558 | |||
| 6f4824b648 | |||
| 45debeb5c9 | |||
| a01f452eba | |||
| e452734bc1 | |||
| d1779b93fa | |||
| fe9aac7e29 |
@@ -383,6 +383,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
games/shared/twtouch.cpp
|
games/shared/twtouch.cpp
|
||||||
games/popn/popn.cpp
|
games/popn/popn.cpp
|
||||||
games/popn/io.cpp
|
games/popn/io.cpp
|
||||||
|
games/popn/bi3a_hook.cpp
|
||||||
games/bbc/bbc.cpp
|
games/bbc/bbc.cpp
|
||||||
games/bbc/io.cpp
|
games/bbc/io.cpp
|
||||||
games/hpm/hpm.cpp
|
games/hpm/hpm.cpp
|
||||||
@@ -582,6 +583,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
overlay/windows/log.cpp
|
overlay/windows/log.cpp
|
||||||
overlay/windows/midi.cpp
|
overlay/windows/midi.cpp
|
||||||
overlay/windows/patch_manager.cpp
|
overlay/windows/patch_manager.cpp
|
||||||
|
overlay/windows/popn_sub.cpp
|
||||||
overlay/windows/wnd_manager.cpp
|
overlay/windows/wnd_manager.cpp
|
||||||
|
|
||||||
# rawinput
|
# rawinput
|
||||||
@@ -596,6 +598,8 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
rawinput/smxstage.h
|
rawinput/smxstage.h
|
||||||
rawinput/smxdedicab.cpp
|
rawinput/smxdedicab.cpp
|
||||||
rawinput/smxdedicab.h
|
rawinput/smxdedicab.h
|
||||||
|
rawinput/xinput.cpp
|
||||||
|
rawinput/xinput.h
|
||||||
|
|
||||||
# reader
|
# reader
|
||||||
reader/reader.cpp
|
reader/reader.cpp
|
||||||
|
|||||||
+120
-18
@@ -35,24 +35,33 @@ struct ICCA_STATUS_LA9 {
|
|||||||
static_assert(sizeof(struct ICCA_STATUS) == 24, "ICCA_STATUS must be 24 bytes");
|
static_assert(sizeof(struct ICCA_STATUS) == 24, "ICCA_STATUS must be 24 bytes");
|
||||||
|
|
||||||
enum ICCA_WORKFLOW {
|
enum ICCA_WORKFLOW {
|
||||||
STEP,
|
STEP = 0,
|
||||||
SLEEP,
|
SLEEP = 1,
|
||||||
START,
|
START = 2,
|
||||||
INIT,
|
INIT = 3,
|
||||||
READY,
|
READY = 4,
|
||||||
GET_USERID,
|
GET_USERID = 5,
|
||||||
ACTIVE,
|
ACTIVE = 6,
|
||||||
EJECT,
|
EJECT = 7,
|
||||||
EJECT_CHECK,
|
EJECT_CHECK = 8,
|
||||||
END,
|
END = 9,
|
||||||
CLOSE_EJECT,
|
CLOSE_EJECT = 10,
|
||||||
CLOSE_E_CHK,
|
CLOSE_E_CHK = 11,
|
||||||
CLOSE_END,
|
CLOSE_END = 12,
|
||||||
ERR_GETUID = -2
|
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_UNIT {
|
||||||
struct ICCA_STATUS status {};
|
struct ICCA_STATUS status {};
|
||||||
enum ICCA_WORKFLOW state = STEP;
|
enum ICCA_WORKFLOW state = STEP;
|
||||||
|
enum ICCA_WORKFLOW state_jdz = SLEEP;
|
||||||
bool card_cmd_pressed = false;
|
bool card_cmd_pressed = false;
|
||||||
bool card_in = false;
|
bool card_in = false;
|
||||||
double card_in_time = 0.0;
|
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) {
|
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)
|
if (icca_get_active_count() < 2)
|
||||||
return 1;
|
return 1;
|
||||||
else {
|
else {
|
||||||
@@ -122,9 +136,15 @@ static inline void update_card(int unit_id) {
|
|||||||
IS_LAST_CARD_FELICA = is_card_uid_felica(unit->status.uid);
|
IS_LAST_CARD_FELICA = is_card_uid_felica(unit->status.uid);
|
||||||
|
|
||||||
unit->state = acio::ICCA_COMPAT_ACTIVE ? START : ACTIVE;
|
unit->state = acio::ICCA_COMPAT_ACTIVE ? START : ACTIVE;
|
||||||
|
unit->state_jdz = ACTIVE;
|
||||||
unit->status.error = 0;
|
unit->status.error = 0;
|
||||||
|
unit->status.front_sensor = 1;
|
||||||
|
unit->status.rear_sensor = 1;
|
||||||
} else {
|
} else {
|
||||||
unit->state = ERR_GETUID;
|
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);
|
memset(unit->status.uid, 0, 8);
|
||||||
}
|
}
|
||||||
unit->card_in = true;
|
unit->card_in = true;
|
||||||
@@ -132,10 +152,25 @@ static inline void update_card(int unit_id) {
|
|||||||
} else if (unit->state == EJECT_CHECK) {
|
} else if (unit->state == EJECT_CHECK) {
|
||||||
unit->state = SLEEP;
|
unit->state = SLEEP;
|
||||||
unit->card_in = false;
|
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 {
|
} else {
|
||||||
unit->state = acio::ICCA_COMPAT_ACTIVE ? START : ACTIVE;
|
unit->state = acio::ICCA_COMPAT_ACTIVE ? START : ACTIVE;
|
||||||
|
if (unit->state_jdz == READY) {
|
||||||
|
unit->state_jdz = GET_USERID;
|
||||||
|
}
|
||||||
unit->status.error = 0;
|
unit->status.error = 0;
|
||||||
|
unit->status.front_sensor = 1;
|
||||||
|
unit->status.rear_sensor = 1;
|
||||||
unit->card_in = true;
|
unit->card_in = true;
|
||||||
unit->card_in_time = get_performance_seconds();
|
unit->card_in_time = get_performance_seconds();
|
||||||
}
|
}
|
||||||
@@ -144,6 +179,12 @@ static inline void update_card(int unit_id) {
|
|||||||
unit->state = CLOSE_EJECT;
|
unit->state = CLOSE_EJECT;
|
||||||
if (fabs(get_performance_seconds() - unit->card_in_time) > CARD_TIMEOUT) {
|
if (fabs(get_performance_seconds() - unit->card_in_time) > CARD_TIMEOUT) {
|
||||||
unit->card_in = false;
|
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
|
// copy state to output buffer
|
||||||
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||||
|
if (avs::game::is_model("JDZ")) {
|
||||||
|
unit->status.status_code = unit->card_in ? ENGAGED : EMPTY;
|
||||||
|
} else {
|
||||||
unit->status.status_code = unit->state;
|
unit->status.status_code = unit->state;
|
||||||
|
}
|
||||||
memcpy(status, &unit->status, sizeof(struct ICCA_STATUS));
|
memcpy(status, &unit->status, sizeof(struct ICCA_STATUS));
|
||||||
|
|
||||||
// funny workaround
|
// 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) {
|
static int __cdecl ac_io_icca_req_uid_isfinished(int unit_id, DWORD *read_state) {
|
||||||
unit_id = icca_get_unit_id(unit_id);
|
unit_id = icca_get_unit_id(unit_id);
|
||||||
ICCA_UNIT *unit = &ICCA_UNITS[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 (unit->card_in) {
|
||||||
if (fabs(get_performance_seconds() - unit->card_in_time) < CARD_TIMEOUT) {
|
if (fabs(get_performance_seconds() - unit->card_in_time) < CARD_TIMEOUT) {
|
||||||
unit->state = END;
|
unit->state = END;
|
||||||
@@ -403,15 +453,60 @@ static int __cdecl ac_io_icca_send_keep_alive_packet(int a1, int a2, int a3) {
|
|||||||
return 0;
|
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) {
|
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);
|
unit_id = icca_get_unit_id(unit_id);
|
||||||
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||||
switch (workflow) {
|
switch (workflow) {
|
||||||
case STEP:
|
case STEP:
|
||||||
if (avs::game::is_model("JDZ"))
|
|
||||||
unit->state = SLEEP;
|
|
||||||
else
|
|
||||||
unit->state = STEP;
|
unit->state = STEP;
|
||||||
break;
|
break;
|
||||||
case SLEEP:
|
case SLEEP:
|
||||||
@@ -441,7 +536,6 @@ static int __cdecl ac_io_icca_workflow(int workflow, int unit_id) {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return unit->state;
|
return unit->state;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,6 +561,14 @@ acio::ICCAModule::ICCAModule(HMODULE module, acio::HookMode hookMode) : ACIOModu
|
|||||||
void acio::ICCAModule::attach() {
|
void acio::ICCAModule::attach() {
|
||||||
ACIOModule::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
|
// hooks
|
||||||
ACIO_MODULE_HOOK(ac_io_icca_cardunit_init);
|
ACIO_MODULE_HOOK(ac_io_icca_cardunit_init);
|
||||||
ACIO_MODULE_HOOK(ac_io_icca_cardunit_init_isfinished);
|
ACIO_MODULE_HOOK(ac_io_icca_cardunit_init_isfinished);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "external/robin_hood.h"
|
#include "external/robin_hood.h"
|
||||||
|
#include "games/popn/popn.h"
|
||||||
#include "launcher/logger.h"
|
#include "launcher/logger.h"
|
||||||
#include "launcher/signal.h"
|
#include "launcher/signal.h"
|
||||||
#include "util/deferlog.h"
|
#include "util/deferlog.h"
|
||||||
@@ -1179,6 +1180,15 @@ namespace avs {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hack, both hello! popn and popn hc using popn.dll
|
||||||
|
if (games::popn::is_pikapika_model()) {
|
||||||
|
auto old_size = HEAP_SIZE;
|
||||||
|
HEAP_SIZE = 0x20000000;
|
||||||
|
DEFAULT_HEAP_SIZE_SET = true;
|
||||||
|
log_info("avs-core", "updated heap size: {} -> {}", old_size, avs::core::HEAP_SIZE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (HEAP_SIZE_DEFAULTS.find(dll_name) != HEAP_SIZE_DEFAULTS.end()) {
|
if (HEAP_SIZE_DEFAULTS.find(dll_name) != HEAP_SIZE_DEFAULTS.end()) {
|
||||||
auto old_size = HEAP_SIZE;
|
auto old_size = HEAP_SIZE;
|
||||||
|
|
||||||
|
|||||||
@@ -217,8 +217,10 @@ namespace avs {
|
|||||||
ea3_config_name = "prop/eamuse-config.xml";
|
ea3_config_name = "prop/eamuse-config.xml";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avs::core::file_exists(ea3_config_name)) {
|
if (fileutils::file_exists(ea3_config_name)) {
|
||||||
log_info("avs-ea3", "booting (using {})", ea3_config_name);
|
log_info("avs-ea3", "found {} on disk", ea3_config_name);
|
||||||
|
} else if (CFG_PATH.size()) {
|
||||||
|
log_fatal("avs-ea3", "user-specified ea3 config file is missing: {}", ea3_config_name);
|
||||||
} else {
|
} else {
|
||||||
log_warning("avs-ea3", "looked for the following files in order:");
|
log_warning("avs-ea3", "looked for the following files in order:");
|
||||||
log_warning("avs-ea3", " * prop/ea3-config.xml");
|
log_warning("avs-ea3", " * prop/ea3-config.xml");
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# set to EN-US for consistency
|
# set to EN-US for consistency
|
||||||
LANG=en_us_8859_1
|
LANG=en_us_8859_1
|
||||||
@@ -43,10 +43,16 @@ GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2> /dev/null || echo "none")
|
|||||||
GIT_HEAD=$(git rev-parse HEAD || echo "none")
|
GIT_HEAD=$(git rev-parse HEAD || echo "none")
|
||||||
TOOLCHAIN_32="${TOOLCHAIN_32:-"/usr/share/mingw/toolchain-i686-w64-mingw32.cmake"}"
|
TOOLCHAIN_32="${TOOLCHAIN_32:-"/usr/share/mingw/toolchain-i686-w64-mingw32.cmake"}"
|
||||||
TOOLCHAIN_64="${TOOLCHAIN_64:-"/usr/share/mingw/toolchain-x86_64-w64-mingw32.cmake"}"
|
TOOLCHAIN_64="${TOOLCHAIN_64:-"/usr/share/mingw/toolchain-x86_64-w64-mingw32.cmake"}"
|
||||||
|
TOOLCHAIN_WINXP_32="${TOOLCHAIN_WINXP_32:-"/opt/llvm-mingw-xp/toolchain-files/cmake/i686-mingw32-clang.cmake"}"
|
||||||
|
TOOLCHAIN_WINXP_64="${TOOLCHAIN_WINXP_64:-"/opt/llvm-mingw-xp/toolchain-files/cmake/x86_64-mingw32-clang.cmake"}"
|
||||||
BUILDDIR_32_RELEASE="./cmake-build-release-32"
|
BUILDDIR_32_RELEASE="./cmake-build-release-32"
|
||||||
BUILDDIR_32_DEBUG="./cmake-build-debug-32"
|
BUILDDIR_32_DEBUG="./cmake-build-debug-32"
|
||||||
BUILDDIR_64_RELEASE="./cmake-build-release-64"
|
BUILDDIR_64_RELEASE="./cmake-build-release-64"
|
||||||
BUILDDIR_64_DEBUG="./cmake-build-debug-64"
|
BUILDDIR_64_DEBUG="./cmake-build-debug-64"
|
||||||
|
BUILDDIR_WINXP_32_RELEASE="./cmake-build-release-winxp-32"
|
||||||
|
BUILDDIR_WINXP_32_DEBUG="./cmake-build-debug-winxp-32"
|
||||||
|
BUILDDIR_WINXP_64_RELEASE="./cmake-build-release-winxp-64"
|
||||||
|
BUILDDIR_WINXP_64_DEBUG="./cmake-build-debug-winxp-64"
|
||||||
DEBUG=0
|
DEBUG=0
|
||||||
OUTDIR="./bin/spice2x"
|
OUTDIR="./bin/spice2x"
|
||||||
OUTDIR_EXTRAS="./bin/spice2x/extras"
|
OUTDIR_EXTRAS="./bin/spice2x/extras"
|
||||||
@@ -64,19 +70,36 @@ DIST_NAME_EXTRAS="spice2x-$(date +%y)-$(date +%m)-$(date +%d)-full.zip"
|
|||||||
DIST_COMMENT=${DIST_NAME}$'\n'"$GIT_BRANCH - $GIT_HEAD"$'\nThank you for playing.'
|
DIST_COMMENT=${DIST_NAME}$'\n'"$GIT_BRANCH - $GIT_HEAD"$'\nThank you for playing.'
|
||||||
TARGETS_32="spicetools_stubs_kbt spicetools_stubs_kld spicetools_cfg spicetools_cfg_linux spicetools_spice spicetools_spice_laa spicetools_spice_linux spicetools_stubs_cpusbxpkm"
|
TARGETS_32="spicetools_stubs_kbt spicetools_stubs_kld spicetools_cfg spicetools_cfg_linux spicetools_spice spicetools_spice_laa spicetools_spice_linux spicetools_stubs_cpusbxpkm"
|
||||||
TARGETS_64="spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvEncodeAPI64 spicetools_stubs_nvcuvid spicetools_stubs_nvcuda spicetools_spice64 spicetools_spice64_linux"
|
TARGETS_64="spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvEncodeAPI64 spicetools_stubs_nvcuvid spicetools_stubs_nvcuda spicetools_spice64 spicetools_spice64_linux"
|
||||||
|
TARGETS_XP32="spicetools_cfg spicetools_spice"
|
||||||
|
TARGETS_XP64="spicetools_spice64"
|
||||||
|
|
||||||
# determine build type
|
# determine build type
|
||||||
BUILD_TYPE="Release"
|
BUILD_TYPE="Release"
|
||||||
BUILDDIR_32=${BUILDDIR_32_RELEASE}
|
BUILDDIR_32=${BUILDDIR_32_RELEASE}
|
||||||
BUILDDIR_64=${BUILDDIR_64_RELEASE}
|
BUILDDIR_64=${BUILDDIR_64_RELEASE}
|
||||||
|
BUILDDIR_WINXP_32=${BUILDDIR_WINXP_32_RELEASE}
|
||||||
|
BUILDDIR_WINXP_64=${BUILDDIR_WINXP_64_RELEASE}
|
||||||
if ((DEBUG > 0))
|
if ((DEBUG > 0))
|
||||||
then
|
then
|
||||||
BUILD_TYPE="Debug"
|
BUILD_TYPE="Debug"
|
||||||
BUILDDIR_32=${BUILDDIR_32_DEBUG}
|
BUILDDIR_32=${BUILDDIR_32_DEBUG}
|
||||||
BUILDDIR_64=${BUILDDIR_64_DEBUG}
|
BUILDDIR_64=${BUILDDIR_64_DEBUG}
|
||||||
|
BUILDDIR_WINXP_32=${BUILDDIR_WINXP_32_DEBUG}
|
||||||
|
BUILDDIR_WINXP_64=${BUILDDIR_WINXP_64_DEBUG}
|
||||||
DIST_NAME=$(echo "${DIST_NAME}" | sed 's/\.[^.]*$/-dbg&/')
|
DIST_NAME=$(echo "${DIST_NAME}" | sed 's/\.[^.]*$/-dbg&/')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# is the XP-compatible toolchain installed?
|
||||||
|
XP_MUST_BUILD=0
|
||||||
|
BUILD_XP=0
|
||||||
|
if [ -f "$TOOLCHAIN_WINXP_32" ] && [ -f "$TOOLCHAIN_WINXP_64" ]; then
|
||||||
|
BUILD_XP=1;
|
||||||
|
elif ((XP_MUST_BUILD > 0))
|
||||||
|
then
|
||||||
|
echo "WinXP toolchain not available, aborting"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# determine number of cores
|
# determine number of cores
|
||||||
CORES=$(nproc)
|
CORES=$(nproc)
|
||||||
|
|
||||||
@@ -90,6 +113,13 @@ echo "Git Branch: $GIT_BRANCH"
|
|||||||
echo "Git Head: $GIT_HEAD"
|
echo "Git Head: $GIT_HEAD"
|
||||||
echo "Toolchain for 32bit targets: $TOOLCHAIN_32"
|
echo "Toolchain for 32bit targets: $TOOLCHAIN_32"
|
||||||
echo "Toolchain for 64bit targets: $TOOLCHAIN_64"
|
echo "Toolchain for 64bit targets: $TOOLCHAIN_64"
|
||||||
|
if ((BUILD_XP > 0))
|
||||||
|
then
|
||||||
|
echo "Toolchain for WinXP 32bit targets: $TOOLCHAIN_WINXP_32"
|
||||||
|
echo "Toolchain for WinXP 64bit targets: $TOOLCHAIN_WINXP_64"
|
||||||
|
else
|
||||||
|
echo "WinXP toolchain not available, skipping WinXP builds"
|
||||||
|
fi
|
||||||
echo "Distribution Name: $DIST_NAME"
|
echo "Distribution Name: $DIST_NAME"
|
||||||
echo "Build Type: $BUILD_TYPE"
|
echo "Build Type: $BUILD_TYPE"
|
||||||
echo "Cores: $CORES"
|
echo "Cores: $CORES"
|
||||||
@@ -130,11 +160,59 @@ time (
|
|||||||
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} "$OLDPWD" && ninja ${TARGETS_64}
|
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} "$OLDPWD" && ninja ${TARGETS_64}
|
||||||
popd > /dev/null
|
popd > /dev/null
|
||||||
|
|
||||||
|
if ((BUILD_XP > 0))
|
||||||
|
then
|
||||||
|
# 32 bit Windows XP
|
||||||
|
echo ""
|
||||||
|
echo "Building 32bit targets (WinXP toolchain)..."
|
||||||
|
echo "========================="
|
||||||
|
if ((CLEAN_BUILD > 0))
|
||||||
|
then
|
||||||
|
rm -rf ${BUILDDIR_WINXP_32}
|
||||||
|
fi
|
||||||
|
mkdir -p ${BUILDDIR_WINXP_32}
|
||||||
|
pushd ${BUILDDIR_WINXP_32} > /dev/null
|
||||||
|
CXXFLAGS="$CXXFLAGS -DSPICE_XP=1" cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_WINXP_32} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} "$OLDPWD" && ninja ${TARGETS_XP32}
|
||||||
|
popd > /dev/null
|
||||||
|
|
||||||
|
# 64 bit Windows XP
|
||||||
|
echo ""
|
||||||
|
echo "Building 64bit targets (WinXP toolchain)..."
|
||||||
|
echo "========================="
|
||||||
|
if ((CLEAN_BUILD > 0))
|
||||||
|
then
|
||||||
|
rm -rf ${BUILDDIR_WINXP_64}
|
||||||
|
fi
|
||||||
|
mkdir -p ${BUILDDIR_WINXP_64}
|
||||||
|
pushd ${BUILDDIR_WINXP_64} > /dev/null
|
||||||
|
CXXFLAGS="$CXXFLAGS -DSPICE_XP=1" cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_WINXP_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} "$OLDPWD" && ninja ${TARGETS_XP64}
|
||||||
|
popd > /dev/null
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
echo "Skipping WinXP builds, toolchain not specified"
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Compilation process done :)"
|
echo "Compilation process done :)"
|
||||||
echo "==========================="
|
echo "==========================="
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if ((BUILD_XP > 0))
|
||||||
|
then
|
||||||
|
echo ""
|
||||||
|
echo "Checking XP compatibility..."
|
||||||
|
echo "============================="
|
||||||
|
if ! command -v windows_dll_compat_checker &> /dev/null; then
|
||||||
|
echo "WARNING: windows_dll_compat_checker not found, skipping XP compatibility check"
|
||||||
|
else
|
||||||
|
windows_dll_compat_checker -s PREMADE/winxp_x86_64.ini \
|
||||||
|
${BUILDDIR_WINXP_64}/spicetools/64/spice64.exe
|
||||||
|
windows_dll_compat_checker -s PREMADE/winxp_x86_64_32bit_dlls.ini \
|
||||||
|
${BUILDDIR_WINXP_32}/spicetools/spicecfg.exe \
|
||||||
|
${BUILDDIR_WINXP_32}/spicetools/32/spice.exe
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# generate PDBs
|
# generate PDBs
|
||||||
if false # ((DEBUG > 0))
|
if false # ((DEBUG > 0))
|
||||||
then
|
then
|
||||||
@@ -191,6 +269,10 @@ rm -rf ${OUTDIR_EXTRAS}
|
|||||||
mkdir -p ${OUTDIR_EXTRAS}
|
mkdir -p ${OUTDIR_EXTRAS}
|
||||||
mkdir -p ${OUTDIR_EXTRAS}/largeaddressaware
|
mkdir -p ${OUTDIR_EXTRAS}/largeaddressaware
|
||||||
mkdir -p ${OUTDIR_EXTRAS}/linux
|
mkdir -p ${OUTDIR_EXTRAS}/linux
|
||||||
|
if ((BUILD_XP > 0))
|
||||||
|
then
|
||||||
|
mkdir -p ${OUTDIR_EXTRAS}/winxp
|
||||||
|
fi
|
||||||
|
|
||||||
if false # ((DEBUG > 0))
|
if false # ((DEBUG > 0))
|
||||||
then
|
then
|
||||||
@@ -226,6 +308,12 @@ else
|
|||||||
cp ${BUILDDIR_64}/spicetools/64/nvcuda.dll ${OUTDIR}/stubs/64 2>/dev/null
|
cp ${BUILDDIR_64}/spicetools/64/nvcuda.dll ${OUTDIR}/stubs/64 2>/dev/null
|
||||||
cp ${BUILDDIR_64}/spicetools/64/nvcuvid.dll ${OUTDIR}/stubs/64 2>/dev/null
|
cp ${BUILDDIR_64}/spicetools/64/nvcuvid.dll ${OUTDIR}/stubs/64 2>/dev/null
|
||||||
cp ${BUILDDIR_32}/spicetools/32/cpusbxpkm.dll ${OUTDIR}/stubs/32 2>/dev/null
|
cp ${BUILDDIR_32}/spicetools/32/cpusbxpkm.dll ${OUTDIR}/stubs/32 2>/dev/null
|
||||||
|
if ((BUILD_XP > 0))
|
||||||
|
then
|
||||||
|
cp ${BUILDDIR_WINXP_32}/spicetools/spicecfg.exe ${OUTDIR_EXTRAS}/winxp 2>/dev/null
|
||||||
|
cp ${BUILDDIR_WINXP_32}/spicetools/32/spice.exe ${OUTDIR_EXTRAS}/winxp 2>/dev/null
|
||||||
|
cp ${BUILDDIR_WINXP_64}/spicetools/64/spice64.exe ${OUTDIR_EXTRAS}/winxp 2>/dev/null
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# pack source files to output directory
|
# pack source files to output directory
|
||||||
@@ -250,10 +338,10 @@ then
|
|||||||
echo "Building dist..."
|
echo "Building dist..."
|
||||||
mkdir -p ${DIST_FOLDER}
|
mkdir -p ${DIST_FOLDER}
|
||||||
rm -rf "${DIST_FOLDER}/${DIST_NAME}"
|
rm -rf "${DIST_FOLDER}/${DIST_NAME}"
|
||||||
pushd ${OUTDIR}/.. > /dev/null
|
pushd ${OUTDIR} > /dev/null
|
||||||
zip -qrXT9 "$OLDPWD/${DIST_FOLDER}/${DIST_NAME}" spice2x -x "spice2x/extras/*" -z <<< "$DIST_COMMENT"
|
zip -qrXT9 "$OLDPWD/${DIST_FOLDER}/${DIST_NAME}" . -x "extras/*" -z <<< "$DIST_COMMENT"
|
||||||
echo "Building extras..."
|
echo "Building extras..."
|
||||||
zip -qrXT9 "$OLDPWD/${DIST_FOLDER}/${DIST_NAME_EXTRAS}" spice2x -z <<< "$DIST_COMMENT"
|
zip -qrXT9 "$OLDPWD/${DIST_FOLDER}/${DIST_NAME_EXTRAS}" . -z <<< "$DIST_COMMENT"
|
||||||
popd > /dev/null
|
popd > /dev/null
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
docker build --pull "$PWD/external/docker" -t spicetools/deps --platform linux/x86_64
|
docker build --pull "$PWD/external/docker" -t spicetools/deps --platform linux/x86_64
|
||||||
docker build --build-context gitroot="$PWD/../../.git" . -t spicetools/spice:latest
|
docker build --build-context gitroot="$PWD/../../.git" . -t spicetools/spice:latest
|
||||||
docker run --rm -v "$PWD/dist:/src/src/spice2x/dist" -v "$PWD/bin:/src/src/spice2x/bin" -v "$PWD/.ccache:/src/src/spice2x/.ccache" spicetools/spice "$@"
|
|
||||||
|
# Interactive TTY if available, so docker build can be Ctrl+C'd
|
||||||
|
DOCKER_FLAGS=""
|
||||||
|
[ -t 0 ] && DOCKER_FLAGS="-it"
|
||||||
|
|
||||||
|
docker run $DOCKER_FLAGS --rm -v "$PWD/dist:/src/src/spice2x/dist" -v "$PWD/bin:/src/src/spice2x/bin" -v "$PWD/.ccache:/src/src/spice2x/.ccache" spicetools/spice "$@"
|
||||||
|
|||||||
@@ -90,6 +90,10 @@ std::string Analog::getDisplayString(rawinput::RawInputManager *manager) {
|
|||||||
return "MIDI Unknown Index " + indexString + " (" + device->desc + ")";
|
return "MIDI Unknown Index " + indexString + " (" + device->desc + ")";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case rawinput::XINPUT_GAMEPAD:
|
||||||
|
return fmt::format("{} ({})",
|
||||||
|
xinput::get_analog_string(static_cast<xinput::XInputAnalogEnum>(index)),
|
||||||
|
device->desc);
|
||||||
case rawinput::DESTROYED:
|
case rawinput::DESTROYED:
|
||||||
return "Device unplugged (" + indexString + ")";
|
return "Device unplugged (" + indexString + ")";
|
||||||
default:
|
default:
|
||||||
|
|||||||
+52
-7
@@ -1,5 +1,6 @@
|
|||||||
#include "api.h"
|
#include "api.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include "launcher/superexit.h"
|
#include "launcher/superexit.h"
|
||||||
@@ -198,9 +199,19 @@ GameAPI::Buttons::State GameAPI::Buttons::getState(rawinput::RawInputManager *ma
|
|||||||
if (vKey < value_states->size()) {
|
if (vKey < value_states->size()) {
|
||||||
auto value = value_states->at(vKey);
|
auto value = value_states->at(vKey);
|
||||||
if (current_button->getAnalogType() == BAT_POSITIVE) {
|
if (current_button->getAnalogType() == BAT_POSITIVE) {
|
||||||
state = value > 0.6f ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
float threshold = 0.6f;
|
||||||
|
if (current_button->getBatThreshold() > 0) {
|
||||||
|
threshold = std::clamp(current_button->getBatThreshold() / 100.f, 0.01f, 0.99f);
|
||||||
|
}
|
||||||
|
state = value > threshold ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
||||||
|
|
||||||
} else if (current_button->getAnalogType() == BAT_NEGATIVE) {
|
} else if (current_button->getAnalogType() == BAT_NEGATIVE) {
|
||||||
state = value < 0.4f ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
float threshold = 0.4f;
|
||||||
|
if (current_button->getBatThreshold() > 0) {
|
||||||
|
threshold = std::clamp((100 - current_button->getBatThreshold()) / 100.f, 0.01f, 0.99f);
|
||||||
|
}
|
||||||
|
state = value < threshold ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
state = value > 0.01f ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
state = value > 0.01f ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
||||||
}
|
}
|
||||||
@@ -389,6 +400,16 @@ GameAPI::Buttons::State GameAPI::Buttons::getState(rawinput::RawInputManager *ma
|
|||||||
}
|
}
|
||||||
case rawinput::PIUIO_DEVICE: {
|
case rawinput::PIUIO_DEVICE: {
|
||||||
state = device->piuioDev->IsPressed(vKey) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
state = device->piuioDev->IsPressed(vKey) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case rawinput::XINPUT_GAMEPAD: {
|
||||||
|
assert(reinterpret_cast<uintptr_t>(device->handle) < XUSER_MAX_COUNT);
|
||||||
|
const auto player = static_cast<uint8_t>(reinterpret_cast<uintptr_t>(device->handle));
|
||||||
|
state = manager->XINPUT_MGR->is_button_pressed(
|
||||||
|
player,
|
||||||
|
static_cast<xinput::XInputButtonEnum>(vKey)) ?
|
||||||
|
BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -586,7 +607,7 @@ float Buttons::getVelocity(std::unique_ptr<rawinput::RawInputManager> &manager,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float GameAPI::Analogs::getState(rawinput::Device *device, Analog &analog) {
|
float GameAPI::Analogs::getState(rawinput::RawInputManager *manager, rawinput::Device *device, Analog &analog) {
|
||||||
float value = 0.5f;
|
float value = 0.5f;
|
||||||
if (!device) {
|
if (!device) {
|
||||||
return value;
|
return value;
|
||||||
@@ -767,6 +788,18 @@ float GameAPI::Analogs::getState(rawinput::Device *device, Analog &analog) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case rawinput::XINPUT_GAMEPAD: {
|
||||||
|
assert(reinterpret_cast<uintptr_t>(device->handle) < XUSER_MAX_COUNT);
|
||||||
|
const auto player = static_cast<uint8_t>(reinterpret_cast<uintptr_t>(device->handle));
|
||||||
|
value = manager->XINPUT_MGR->get_analog_state(
|
||||||
|
player,
|
||||||
|
static_cast<xinput::XInputAnalogEnum>(index));
|
||||||
|
// invert value
|
||||||
|
if (inverted) {
|
||||||
|
value = 1.f - value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -822,7 +855,7 @@ float GameAPI::Analogs::getState(rawinput::RawInputManager *manager, Analog &ana
|
|||||||
return analog.getLastState();
|
return analog.getLastState();
|
||||||
}
|
}
|
||||||
|
|
||||||
float state = getState(device, analog);
|
float state = getState(manager, device, analog);
|
||||||
analog.setLastState(state);
|
analog.setLastState(state);
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
@@ -904,7 +937,7 @@ void GameAPI::Lights::sortLightsWithCategory(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void GameAPI::Lights::writeLight(rawinput::Device *device, int index, float value) {
|
void GameAPI::Lights::writeLight(rawinput::RawInputManager *manager, rawinput::Device *device, int index, float value) {
|
||||||
|
|
||||||
// check device
|
// check device
|
||||||
if (!device) {
|
if (!device) {
|
||||||
@@ -991,6 +1024,18 @@ void GameAPI::Lights::writeLight(rawinput::Device *device, int index, float valu
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case rawinput::XINPUT_GAMEPAD: {
|
||||||
|
assert(reinterpret_cast<uintptr_t>(device->handle) < XUSER_MAX_COUNT);
|
||||||
|
const auto player = static_cast<uint8_t>(reinterpret_cast<uintptr_t>(device->handle));
|
||||||
|
if (index < static_cast<int>(xinput::XInputOutputEnum::COUNT)) {
|
||||||
|
manager->XINPUT_MGR->set_output_state(
|
||||||
|
player, static_cast<xinput::XInputOutputEnum>(index), value);
|
||||||
|
// no need to set output_pending; xinput output handled immediately
|
||||||
|
} else {
|
||||||
|
log_warning("api", "invalid xinput light index: {}", index);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1016,9 +1061,9 @@ void GameAPI::Lights::writeLight(rawinput::RawInputManager *manager, Light &ligh
|
|||||||
|
|
||||||
// write state
|
// write state
|
||||||
if (light.override_enabled) {
|
if (light.override_enabled) {
|
||||||
writeLight(device, light.getIndex(), light.override_state);
|
writeLight(manager, device, light.getIndex(), light.override_state);
|
||||||
} else {
|
} else {
|
||||||
writeLight(device, light.getIndex(), value);
|
writeLight(manager, device, light.getIndex(), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ namespace GameAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float getState(rawinput::Device *device, Analog &analog);
|
float getState(rawinput::RawInputManager *manager, rawinput::Device *device, Analog &analog);
|
||||||
float getState(rawinput::RawInputManager *manager, Analog &analog);
|
float getState(rawinput::RawInputManager *manager, Analog &analog);
|
||||||
float getState(std::unique_ptr<rawinput::RawInputManager> &manager, Analog &analog);
|
float getState(std::unique_ptr<rawinput::RawInputManager> &manager, Analog &analog);
|
||||||
}
|
}
|
||||||
@@ -135,7 +135,7 @@ namespace GameAPI {
|
|||||||
|
|
||||||
void sortLightsWithCategory(std::vector<Light> *lights, const std::initializer_list<LightAndCategory> list);
|
void sortLightsWithCategory(std::vector<Light> *lights, const std::initializer_list<LightAndCategory> list);
|
||||||
|
|
||||||
void writeLight(rawinput::Device *device, int index, float value);
|
void writeLight(rawinput::RawInputManager *manager, rawinput::Device *device, int index, float value);
|
||||||
void writeLight(rawinput::RawInputManager *manager, Light &light, float value);
|
void writeLight(rawinput::RawInputManager *manager, Light &light, float value);
|
||||||
void writeLight(std::unique_ptr<rawinput::RawInputManager> &manager, Light &light, float value);
|
void writeLight(std::unique_ptr<rawinput::RawInputManager> &manager, Light &light, float value);
|
||||||
|
|
||||||
|
|||||||
@@ -417,6 +417,10 @@ std::string Button::getDisplayString(rawinput::RawInputManager* manager) {
|
|||||||
}
|
}
|
||||||
case rawinput::PIUIO_DEVICE:
|
case rawinput::PIUIO_DEVICE:
|
||||||
return "PIUIO " + vKeyString;
|
return "PIUIO " + vKeyString;
|
||||||
|
case rawinput::XINPUT_GAMEPAD:
|
||||||
|
return fmt::format("{} ({})",
|
||||||
|
xinput::get_button_string(static_cast<xinput::XInputButtonEnum>(vKey)),
|
||||||
|
device->desc);
|
||||||
case rawinput::DESTROYED:
|
case rawinput::DESTROYED:
|
||||||
return "Device unplugged (" + vKeyString + ")";
|
return "Device unplugged (" + vKeyString + ")";
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ private:
|
|||||||
ButtonAnalogType analog_type = BAT_NONE;
|
ButtonAnalogType analog_type = BAT_NONE;
|
||||||
double debounce_up = 0.0;
|
double debounce_up = 0.0;
|
||||||
double debounce_down = 0.0;
|
double debounce_down = 0.0;
|
||||||
|
int bat_threshold = 0; // for positive/negative only, value in percentage, zero is default
|
||||||
bool invert = false;
|
bool invert = false;
|
||||||
bool is_temporary = false;
|
bool is_temporary = false;
|
||||||
|
|
||||||
@@ -109,6 +110,7 @@ public:
|
|||||||
alternatives.clear();
|
alternatives.clear();
|
||||||
device_identifier = "";
|
device_identifier = "";
|
||||||
analog_type = BAT_NONE;
|
analog_type = BAT_NONE;
|
||||||
|
bat_threshold = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getDisplayString(rawinput::RawInputManager* manager);
|
std::string getDisplayString(rawinput::RawInputManager* manager);
|
||||||
@@ -169,6 +171,14 @@ public:
|
|||||||
this->debounce_down = debounce_time_down;
|
this->debounce_down = debounce_time_down;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void setBatThreshold(int bat_threshold) {
|
||||||
|
this->bat_threshold = bat_threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int getBatThreshold() const {
|
||||||
|
return this->bat_threshold;
|
||||||
|
}
|
||||||
|
|
||||||
inline bool getInvert() const {
|
inline bool getInvert() const {
|
||||||
return this->invert;
|
return this->invert;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ bool Config::addGame(Game &game) {
|
|||||||
auto analogType = (int) BAT_NONE;
|
auto analogType = (int) BAT_NONE;
|
||||||
double debounce_up = 0.0;
|
double debounce_up = 0.0;
|
||||||
double debounce_down = 0.0;
|
double debounce_down = 0.0;
|
||||||
|
int bat_threshold = 0;
|
||||||
int velocity_threshold = 0;
|
int velocity_threshold = 0;
|
||||||
bool invert = false;
|
bool invert = false;
|
||||||
tinyxml2::XMLError attrError = gameButtonNode->QueryIntAttribute("vkey", &vKey);
|
tinyxml2::XMLError attrError = gameButtonNode->QueryIntAttribute("vkey", &vKey);
|
||||||
@@ -169,6 +170,7 @@ bool Config::addGame(Game &game) {
|
|||||||
gameButtonNode->QueryIntAttribute("analogtype", &analogType);
|
gameButtonNode->QueryIntAttribute("analogtype", &analogType);
|
||||||
gameButtonNode->QueryDoubleAttribute("debounce_up", &debounce_up);
|
gameButtonNode->QueryDoubleAttribute("debounce_up", &debounce_up);
|
||||||
gameButtonNode->QueryDoubleAttribute("debounce_down", &debounce_down);
|
gameButtonNode->QueryDoubleAttribute("debounce_down", &debounce_down);
|
||||||
|
gameButtonNode->QueryIntAttribute("bat_threshold", &bat_threshold);
|
||||||
gameButtonNode->QueryIntAttribute("velocity_threshold", &velocity_threshold);
|
gameButtonNode->QueryIntAttribute("velocity_threshold", &velocity_threshold);
|
||||||
gameButtonNode->QueryBoolAttribute("invert", &invert);
|
gameButtonNode->QueryBoolAttribute("invert", &invert);
|
||||||
if (attrError != tinyxml2::XMLError::XML_SUCCESS) {
|
if (attrError != tinyxml2::XMLError::XML_SUCCESS) {
|
||||||
@@ -180,6 +182,7 @@ bool Config::addGame(Game &game) {
|
|||||||
gameButtonNode->SetAttribute("devid", button->getDeviceIdentifier().c_str());
|
gameButtonNode->SetAttribute("devid", button->getDeviceIdentifier().c_str());
|
||||||
gameButtonNode->SetAttribute("debounce_up", debounce_up);
|
gameButtonNode->SetAttribute("debounce_up", debounce_up);
|
||||||
gameButtonNode->SetAttribute("debounce_down", debounce_down);
|
gameButtonNode->SetAttribute("debounce_down", debounce_down);
|
||||||
|
gameButtonNode->SetAttribute("bat_threshold", bat_threshold);
|
||||||
gameButtonNode->SetAttribute("velocity_threshold", velocity_threshold);
|
gameButtonNode->SetAttribute("velocity_threshold", velocity_threshold);
|
||||||
gameButtonNode->SetAttribute("invert", invert);
|
gameButtonNode->SetAttribute("invert", invert);
|
||||||
gameButtonsNode->InsertEndChild(gameButtonNode);
|
gameButtonsNode->InsertEndChild(gameButtonNode);
|
||||||
@@ -188,6 +191,7 @@ bool Config::addGame(Game &game) {
|
|||||||
button->setAnalogType((ButtonAnalogType) analogType);
|
button->setAnalogType((ButtonAnalogType) analogType);
|
||||||
button->setDebounceUp(debounce_up);
|
button->setDebounceUp(debounce_up);
|
||||||
button->setDebounceDown(debounce_down);
|
button->setDebounceDown(debounce_down);
|
||||||
|
button->setBatThreshold(bat_threshold);
|
||||||
button->setVelocityThreshold(velocity_threshold);
|
button->setVelocityThreshold(velocity_threshold);
|
||||||
button->setInvert(invert);
|
button->setInvert(invert);
|
||||||
if (devid) {
|
if (devid) {
|
||||||
@@ -207,6 +211,7 @@ bool Config::addGame(Game &game) {
|
|||||||
gameButtonNode->SetAttribute("analogtype", (int) it.getAnalogType());
|
gameButtonNode->SetAttribute("analogtype", (int) it.getAnalogType());
|
||||||
gameButtonNode->SetAttribute("debounce_up", it.getDebounceUp());
|
gameButtonNode->SetAttribute("debounce_up", it.getDebounceUp());
|
||||||
gameButtonNode->SetAttribute("debounce_down", it.getDebounceDown());
|
gameButtonNode->SetAttribute("debounce_down", it.getDebounceDown());
|
||||||
|
gameButtonNode->SetAttribute("bat_threshold", it.getBatThreshold());
|
||||||
gameButtonNode->SetAttribute("velocity_threshold", it.getVelocityThreshold());
|
gameButtonNode->SetAttribute("velocity_threshold", it.getVelocityThreshold());
|
||||||
gameButtonNode->SetAttribute("invert", it.getInvert());
|
gameButtonNode->SetAttribute("invert", it.getInvert());
|
||||||
gameButtonNode->SetAttribute("devid", it.getDeviceIdentifier().c_str());
|
gameButtonNode->SetAttribute("devid", it.getDeviceIdentifier().c_str());
|
||||||
@@ -432,6 +437,7 @@ bool Config::addGame(Game &game) {
|
|||||||
gameButtonNode->SetAttribute("analogtype", it.getAnalogType());
|
gameButtonNode->SetAttribute("analogtype", it.getAnalogType());
|
||||||
gameButtonNode->SetAttribute("debounce_up", it.getDebounceUp());
|
gameButtonNode->SetAttribute("debounce_up", it.getDebounceUp());
|
||||||
gameButtonNode->SetAttribute("debounce_down", it.getDebounceDown());
|
gameButtonNode->SetAttribute("debounce_down", it.getDebounceDown());
|
||||||
|
gameButtonNode->SetAttribute("bat_threshold", it.getBatThreshold());
|
||||||
gameButtonNode->SetAttribute("velocity_threshold", it.getVelocityThreshold());
|
gameButtonNode->SetAttribute("velocity_threshold", it.getVelocityThreshold());
|
||||||
gameButtonNode->SetAttribute("invert", it.getInvert());
|
gameButtonNode->SetAttribute("invert", it.getInvert());
|
||||||
gameButtonNode->SetAttribute("devid", it.getDeviceIdentifier().c_str());
|
gameButtonNode->SetAttribute("devid", it.getDeviceIdentifier().c_str());
|
||||||
@@ -535,6 +541,7 @@ bool Config::updateBinding(const Game &game, const Button &button, int alternati
|
|||||||
gameButtonNode->SetAttribute("analogtype", (int) button.getAnalogType());
|
gameButtonNode->SetAttribute("analogtype", (int) button.getAnalogType());
|
||||||
gameButtonNode->SetAttribute("debounce_up", button.getDebounceUp());
|
gameButtonNode->SetAttribute("debounce_up", button.getDebounceUp());
|
||||||
gameButtonNode->SetAttribute("debounce_down", button.getDebounceDown());
|
gameButtonNode->SetAttribute("debounce_down", button.getDebounceDown());
|
||||||
|
gameButtonNode->SetAttribute("bat_threshold", button.getBatThreshold());
|
||||||
gameButtonNode->SetAttribute("velocity_threshold", button.getVelocityThreshold());
|
gameButtonNode->SetAttribute("velocity_threshold", button.getVelocityThreshold());
|
||||||
gameButtonNode->SetAttribute("invert", button.getInvert());
|
gameButtonNode->SetAttribute("invert", button.getInvert());
|
||||||
gameButtonNode->SetAttribute("devid", button.getDeviceIdentifier().c_str());
|
gameButtonNode->SetAttribute("devid", button.getDeviceIdentifier().c_str());
|
||||||
@@ -551,6 +558,7 @@ bool Config::updateBinding(const Game &game, const Button &button, int alternati
|
|||||||
gameButtonNode->SetAttribute("analogtype", 0);
|
gameButtonNode->SetAttribute("analogtype", 0);
|
||||||
gameButtonNode->SetAttribute("debounce_up", 0.0);
|
gameButtonNode->SetAttribute("debounce_up", 0.0);
|
||||||
gameButtonNode->SetAttribute("debounce_down", 0.0);
|
gameButtonNode->SetAttribute("debounce_down", 0.0);
|
||||||
|
gameButtonNode->SetAttribute("bat_threshold", 0);
|
||||||
gameButtonNode->SetAttribute("velocity_threshold", 0);
|
gameButtonNode->SetAttribute("velocity_threshold", 0);
|
||||||
gameButtonNode->SetAttribute("invert", false);
|
gameButtonNode->SetAttribute("invert", false);
|
||||||
gameButtonNode->SetAttribute("devid", "");
|
gameButtonNode->SetAttribute("devid", "");
|
||||||
@@ -946,12 +954,14 @@ std::vector<Button> Config::getButtons(const std::string &gameName) {
|
|||||||
auto analogType = (int) BAT_NONE;
|
auto analogType = (int) BAT_NONE;
|
||||||
double debounce_up = 0.0;
|
double debounce_up = 0.0;
|
||||||
double debounce_down = 0.0;
|
double debounce_down = 0.0;
|
||||||
|
int bat_threshold = 0;
|
||||||
int velocity_threshold = 0;
|
int velocity_threshold = 0;
|
||||||
bool invert = false;
|
bool invert = false;
|
||||||
gameButtonNode->QueryIntAttribute("vkey", &vKey);
|
gameButtonNode->QueryIntAttribute("vkey", &vKey);
|
||||||
gameButtonNode->QueryIntAttribute("analogtype", &analogType);
|
gameButtonNode->QueryIntAttribute("analogtype", &analogType);
|
||||||
gameButtonNode->QueryDoubleAttribute("debounce_up", &debounce_up);
|
gameButtonNode->QueryDoubleAttribute("debounce_up", &debounce_up);
|
||||||
gameButtonNode->QueryDoubleAttribute("debounce_down", &debounce_down);
|
gameButtonNode->QueryDoubleAttribute("debounce_down", &debounce_down);
|
||||||
|
gameButtonNode->QueryIntAttribute("bat_threshold", &bat_threshold);
|
||||||
gameButtonNode->QueryIntAttribute("velocity_threshold", &velocity_threshold);
|
gameButtonNode->QueryIntAttribute("velocity_threshold", &velocity_threshold);
|
||||||
gameButtonNode->QueryBoolAttribute("invert", &invert);
|
gameButtonNode->QueryBoolAttribute("invert", &invert);
|
||||||
const char *devid = gameButtonNode->Attribute("devid");
|
const char *devid = gameButtonNode->Attribute("devid");
|
||||||
@@ -966,6 +976,7 @@ std::vector<Button> Config::getButtons(const std::string &gameName) {
|
|||||||
alt.setAnalogType((ButtonAnalogType) analogType);
|
alt.setAnalogType((ButtonAnalogType) analogType);
|
||||||
alt.setDebounceUp(debounce_up);
|
alt.setDebounceUp(debounce_up);
|
||||||
alt.setDebounceDown(debounce_down);
|
alt.setDebounceDown(debounce_down);
|
||||||
|
alt.setBatThreshold(bat_threshold);
|
||||||
alt.setVelocityThreshold(velocity_threshold);
|
alt.setVelocityThreshold(velocity_threshold);
|
||||||
alt.setInvert(invert);
|
alt.setInvert(invert);
|
||||||
if (devid) {
|
if (devid) {
|
||||||
@@ -984,6 +995,7 @@ std::vector<Button> Config::getButtons(const std::string &gameName) {
|
|||||||
button.setAnalogType((ButtonAnalogType) analogType);
|
button.setAnalogType((ButtonAnalogType) analogType);
|
||||||
button.setDebounceUp(debounce_up);
|
button.setDebounceUp(debounce_up);
|
||||||
button.setDebounceDown(debounce_down);
|
button.setDebounceDown(debounce_down);
|
||||||
|
button.setBatThreshold(bat_threshold);
|
||||||
button.setVelocityThreshold(velocity_threshold);
|
button.setVelocityThreshold(velocity_threshold);
|
||||||
button.setInvert(invert);
|
button.setInvert(invert);
|
||||||
if (devid) {
|
if (devid) {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ namespace overlay::windows {
|
|||||||
el->SetAttribute("invert", entry.invert);
|
el->SetAttribute("invert", entry.invert);
|
||||||
el->SetAttribute("debounce_up", entry.debounce_up);
|
el->SetAttribute("debounce_up", entry.debounce_up);
|
||||||
el->SetAttribute("debounce_down", entry.debounce_down);
|
el->SetAttribute("debounce_down", entry.debounce_down);
|
||||||
|
el->SetAttribute("bat_threshold", entry.bat_threshold);
|
||||||
el->SetAttribute("velocity_threshold", entry.velocity_threshold);
|
el->SetAttribute("velocity_threshold", entry.velocity_threshold);
|
||||||
parent->InsertEndChild(el);
|
parent->InsertEndChild(el);
|
||||||
}
|
}
|
||||||
@@ -49,6 +50,10 @@ namespace overlay::windows {
|
|||||||
el->QueryIntAttribute("velocity_threshold", &vel);
|
el->QueryIntAttribute("velocity_threshold", &vel);
|
||||||
entry.velocity_threshold = (unsigned short)vel;
|
entry.velocity_threshold = (unsigned short)vel;
|
||||||
|
|
||||||
|
int bat = 0;
|
||||||
|
el->QueryIntAttribute("bat_threshold", &bat);
|
||||||
|
entry.bat_threshold = bat;
|
||||||
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,6 +85,15 @@ std::string Light::getDisplayString(rawinput::RawInputManager* manager) {
|
|||||||
|
|
||||||
return "Invalid SMX Dedicab Light (" + index_string + ")";
|
return "Invalid SMX Dedicab Light (" + index_string + ")";
|
||||||
}
|
}
|
||||||
|
case rawinput::XINPUT_GAMEPAD: {
|
||||||
|
if (index < static_cast<size_t>(xinput::XInputOutputEnum::COUNT)) {
|
||||||
|
return fmt::format("{} ({})",
|
||||||
|
xinput::get_output_string(static_cast<xinput::XInputOutputEnum>(index)),
|
||||||
|
device->desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Invalid XInput Gamepad Light (" + index_string + ")";
|
||||||
|
}
|
||||||
case rawinput::DESTROYED:
|
case rawinput::DESTROYED:
|
||||||
return "Unplugged device (" + index_string + ")";
|
return "Unplugged device (" + index_string + ")";
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ namespace cfg {
|
|||||||
// window = rectangle including the frame
|
// window = rectangle including the frame
|
||||||
// client = just the content area without frames.
|
// client = just the content area without frames.
|
||||||
bool window_always_on_top = false;
|
bool window_always_on_top = false;
|
||||||
|
bool window_disable_round_corners = false;
|
||||||
bool client_keep_aspect_ratio = true;
|
bool client_keep_aspect_ratio = true;
|
||||||
bool enable_window_resize = false;
|
bool enable_window_resize = false;
|
||||||
int window_decoration = 0; // enum type WindowDecorationMode
|
int window_decoration = 0; // enum type WindowDecorationMode
|
||||||
|
|||||||
+8
@@ -15,3 +15,11 @@ RUN pacman --noconfirm -Syu git \
|
|||||||
ccache
|
ccache
|
||||||
RUN useradd user -m && echo "ALL ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
RUN useradd user -m && echo "ALL ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
||||||
RUN su user -c "cd /tmp/ && git clone https://aur.archlinux.org/yay-bin.git --depth=1 && cd yay-bin && makepkg --noconfirm -si && yay --noconfirm -S mingw-w64-cmake"
|
RUN su user -c "cd /tmp/ && git clone https://aur.archlinux.org/yay-bin.git --depth=1 && cd yay-bin && makepkg --noconfirm -si && yay --noconfirm -S mingw-w64-cmake"
|
||||||
|
|
||||||
|
RUN mkdir -p /opt/llvm-mingw-xp \
|
||||||
|
&& curl -fsSL "https://github.com/mon/llvm-mingw-xp/releases/download/llvm-mingw-xp-22.1.0/llvm-mingw-llvm-mingw-xp-22.1.0-msvcrt-ubuntu-22.04-x86_64.tar.xz" \
|
||||||
|
| tar -xJ --strip-components=1 -C /opt/llvm-mingw-xp
|
||||||
|
ENV PATH="$PATH:/opt/llvm-mingw-xp/bin"
|
||||||
|
|
||||||
|
RUN curl -fsSL "https://github.com/mon/windows-dll-compat-checker/releases/download/v1.3/windows_dll_compat_checker-linux-x86_64.tar.xz" \
|
||||||
|
| tar -xJ -C /usr/local/bin
|
||||||
|
|||||||
@@ -174,6 +174,7 @@ namespace games::gitadora {
|
|||||||
static_assert(sizeof(AIO_IOB5_Y32D) == 0x2e8);
|
static_assert(sizeof(AIO_IOB5_Y32D) == 0x2e8);
|
||||||
static_assert(sizeof(AIO_IOB2_BI2X_AC1) == 0x4570);
|
static_assert(sizeof(AIO_IOB2_BI2X_AC1) == 0x4570);
|
||||||
static_assert(sizeof(AIO_IOB2_BI2X_WRFIRM) == 0x20f48);
|
static_assert(sizeof(AIO_IOB2_BI2X_WRFIRM) == 0x20f48);
|
||||||
|
static_assert(sizeof(AIO_IOB2_BI2X_AC1__DEVSTATUS) == 0x2C8);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* typedefs
|
* typedefs
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "camera.h"
|
#include "camera.h"
|
||||||
|
|
||||||
#if SPICE64
|
#if SPICE64 && !SPICE_XP
|
||||||
|
|
||||||
#include <d3d9.h>
|
#include <d3d9.h>
|
||||||
#include "mf_wrappers.h"
|
#include "mf_wrappers.h"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#if SPICE64
|
#if SPICE64 && !SPICE_XP
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "games/iidx/local_camera.h"
|
#include "games/iidx/local_camera.h"
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include "util/memutils.h"
|
#include "util/memutils.h"
|
||||||
#include "util/sigscan.h"
|
#include "util/sigscan.h"
|
||||||
#include "util/socd_cleaner.h"
|
#include "util/socd_cleaner.h"
|
||||||
|
#include "util/sysutils.h"
|
||||||
#include "util/time.h"
|
#include "util/time.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include "launcher/signal.h"
|
#include "launcher/signal.h"
|
||||||
@@ -423,6 +424,14 @@ namespace games::iidx {
|
|||||||
init_legacy_camera_hook(FLIP_CAMS);
|
init_legacy_camera_hook(FLIP_CAMS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
|
||||||
|
if (TDJ_MODE) {
|
||||||
|
sysutils::hook_EnumDisplayDevicesA();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
// init cfgmgr32 hooks
|
// init cfgmgr32 hooks
|
||||||
cfgmgr32hook_init(avs::game::DLL_INSTANCE);
|
cfgmgr32hook_init(avs::game::DLL_INSTANCE);
|
||||||
}
|
}
|
||||||
@@ -906,8 +915,7 @@ namespace games::iidx {
|
|||||||
log_warning(
|
log_warning(
|
||||||
"iidx",
|
"iidx",
|
||||||
"Failed to apply ASIO compatibility fix for iidx32+. "
|
"Failed to apply ASIO compatibility fix for iidx32+. "
|
||||||
"Unless patches are applied, your ASIO device ({}) may hang or fail to work",
|
"Unless patches are applied, your ASIO device may hang or fail to work");
|
||||||
ASIO_DRIVER->c_str());
|
|
||||||
} else {
|
} else {
|
||||||
log_info(
|
log_info(
|
||||||
"iidx",
|
"iidx",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "games/iidx/local_camera.h"
|
#include "games/iidx/local_camera.h"
|
||||||
|
|
||||||
#if SPICE64
|
#if SPICE64 && !SPICE_XP
|
||||||
|
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#if SPICE64
|
#if SPICE64 && !SPICE_XP
|
||||||
|
|
||||||
#include <d3d9.h>
|
#include <d3d9.h>
|
||||||
#include <dxva2api.h>
|
#include <dxva2api.h>
|
||||||
|
|||||||
+47
-46
@@ -51,7 +51,7 @@ namespace games {
|
|||||||
static robin_hood::unordered_map<std::string, std::vector<Analog> &> analogs;
|
static robin_hood::unordered_map<std::string, std::vector<Analog> &> analogs;
|
||||||
static robin_hood::unordered_map<std::string, std::vector<Light> &> lights;
|
static robin_hood::unordered_map<std::string, std::vector<Light> &> lights;
|
||||||
static robin_hood::unordered_map<std::string, std::vector<Option>> options;
|
static robin_hood::unordered_map<std::string, std::vector<Option>> options;
|
||||||
static robin_hood::unordered_map<std::string, std::vector<std::string>> file_hints;
|
static robin_hood::unordered_map<std::string, std::vector<std::vector<std::string>>> file_hints;
|
||||||
|
|
||||||
static void initialize() {
|
static void initialize() {
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ namespace games {
|
|||||||
buttons_help.insert({ bbc, bbc::get_buttons_help() });
|
buttons_help.insert({ bbc, bbc::get_buttons_help() });
|
||||||
analogs.insert({ bbc, bbc::get_analogs() });
|
analogs.insert({ bbc, bbc::get_analogs() });
|
||||||
lights.insert({ bbc, bbc::get_lights() });
|
lights.insert({ bbc, bbc::get_lights() });
|
||||||
file_hints[bbc].emplace_back("bsch.dll");
|
file_hints[bbc].push_back({"bsch.dll"});
|
||||||
|
|
||||||
// hpm
|
// hpm
|
||||||
const std::string hpm("HELLO! Pop'n Music");
|
const std::string hpm("HELLO! Pop'n Music");
|
||||||
@@ -76,32 +76,32 @@ namespace games {
|
|||||||
buttons.insert({ hpm, hpm::get_buttons() });
|
buttons.insert({ hpm, hpm::get_buttons() });
|
||||||
buttons_help.insert({ hpm, hpm::get_buttons_help() });
|
buttons_help.insert({ hpm, hpm::get_buttons_help() });
|
||||||
lights.insert({ hpm, hpm::get_lights() });
|
lights.insert({ hpm, hpm::get_lights() });
|
||||||
file_hints[hpm].emplace_back("popn.dll");
|
file_hints[hpm].push_back({"popn.dll", "pdh.dll"});
|
||||||
|
|
||||||
// bs
|
// bs
|
||||||
const std::string bs("Beatstream");
|
const std::string bs("Beatstream");
|
||||||
games.push_back(bs);
|
games.push_back(bs);
|
||||||
buttons.insert({ bs, bs::get_buttons() });
|
buttons.insert({ bs, bs::get_buttons() });
|
||||||
lights.insert({ bs, bs::get_lights() });
|
lights.insert({ bs, bs::get_lights() });
|
||||||
file_hints[bs].emplace_back("beatstream.dll");
|
file_hints[bs].push_back({"beatstream.dll"});
|
||||||
file_hints[bs].emplace_back("beatstream1.dll");
|
file_hints[bs].push_back({"beatstream1.dll"});
|
||||||
file_hints[bs].emplace_back("beatstream2.dll");
|
file_hints[bs].push_back({"beatstream2.dll"});
|
||||||
|
|
||||||
// ddr
|
// ddr
|
||||||
const std::string ddr("Dance Dance Revolution");
|
const std::string ddr("Dance Dance Revolution");
|
||||||
games.push_back(ddr);
|
games.push_back(ddr);
|
||||||
buttons.insert({ ddr, ddr::get_buttons() });
|
buttons.insert({ ddr, ddr::get_buttons() });
|
||||||
lights.insert({ ddr, ddr::get_lights() });
|
lights.insert({ ddr, ddr::get_lights() });
|
||||||
file_hints[ddr].emplace_back("ddr.dll");
|
file_hints[ddr].push_back({"ddr.dll"});
|
||||||
file_hints[ddr].emplace_back("mdxja_945.dll");
|
file_hints[ddr].push_back({"mdxja_945.dll"});
|
||||||
file_hints[ddr].emplace_back("arkmdxp3.dll");
|
file_hints[ddr].push_back({"arkmdxp3.dll"});
|
||||||
|
|
||||||
// dea
|
// dea
|
||||||
const std::string dea("Dance Evolution");
|
const std::string dea("Dance Evolution");
|
||||||
games.push_back(dea);
|
games.push_back(dea);
|
||||||
buttons.insert({ dea, dea::get_buttons() });
|
buttons.insert({ dea, dea::get_buttons() });
|
||||||
lights.insert({ dea, dea::get_lights() });
|
lights.insert({ dea, dea::get_lights() });
|
||||||
file_hints[dea].emplace_back("arkkdm.dll");
|
file_hints[dea].push_back({"arkkdm.dll"});
|
||||||
|
|
||||||
// gitadora
|
// gitadora
|
||||||
const std::string gitadora("GitaDora");
|
const std::string gitadora("GitaDora");
|
||||||
@@ -111,7 +111,7 @@ namespace games {
|
|||||||
analogs_help.insert({ gitadora, gitadora::get_analogs_help() });
|
analogs_help.insert({ gitadora, gitadora::get_analogs_help() });
|
||||||
analogs.insert({ gitadora, gitadora::get_analogs() });
|
analogs.insert({ gitadora, gitadora::get_analogs() });
|
||||||
lights.insert({ gitadora, gitadora::get_lights() });
|
lights.insert({ gitadora, gitadora::get_lights() });
|
||||||
file_hints[gitadora].emplace_back("gdxg.dll");
|
file_hints[gitadora].push_back({"gdxg.dll"});
|
||||||
|
|
||||||
// iidx
|
// iidx
|
||||||
const std::string iidx("Beatmania IIDX");
|
const std::string iidx("Beatmania IIDX");
|
||||||
@@ -120,7 +120,7 @@ namespace games {
|
|||||||
buttons_help.insert({ iidx, iidx::get_buttons_help() });
|
buttons_help.insert({ iidx, iidx::get_buttons_help() });
|
||||||
analogs.insert({ iidx, iidx::get_analogs() });
|
analogs.insert({ iidx, iidx::get_analogs() });
|
||||||
lights.insert({ iidx, iidx::get_lights() });
|
lights.insert({ iidx, iidx::get_lights() });
|
||||||
file_hints[iidx].emplace_back("bm2dx.dll");
|
file_hints[iidx].push_back({"bm2dx.dll"});
|
||||||
|
|
||||||
// jb
|
// jb
|
||||||
const std::string jb("Jubeat");
|
const std::string jb("Jubeat");
|
||||||
@@ -128,7 +128,7 @@ namespace games {
|
|||||||
buttons.insert({ jb, jb::get_buttons() });
|
buttons.insert({ jb, jb::get_buttons() });
|
||||||
buttons_help.insert({ jb, jb::get_buttons_help() });
|
buttons_help.insert({ jb, jb::get_buttons_help() });
|
||||||
lights.insert({ jb, jb::get_lights() });
|
lights.insert({ jb, jb::get_lights() });
|
||||||
file_hints[jb].emplace_back("jubeat.dll");
|
file_hints[jb].push_back({"jubeat.dll"});
|
||||||
|
|
||||||
// mga
|
// mga
|
||||||
const std::string mga("Metal Gear");
|
const std::string mga("Metal Gear");
|
||||||
@@ -136,7 +136,7 @@ namespace games {
|
|||||||
buttons.insert({ mga, mga::get_buttons() });
|
buttons.insert({ mga, mga::get_buttons() });
|
||||||
analogs.insert({ mga, mga::get_analogs() });
|
analogs.insert({ mga, mga::get_analogs() });
|
||||||
lights.insert({ mga, mga::get_lights() });
|
lights.insert({ mga, mga::get_lights() });
|
||||||
file_hints[mga].emplace_back("launch.dll");
|
file_hints[mga].push_back({"launch.dll"});
|
||||||
|
|
||||||
// museca
|
// museca
|
||||||
const std::string museca("Museca");
|
const std::string museca("Museca");
|
||||||
@@ -145,7 +145,7 @@ namespace games {
|
|||||||
buttons_help.insert({ museca, museca::get_buttons_help() });
|
buttons_help.insert({ museca, museca::get_buttons_help() });
|
||||||
analogs.insert({ museca, museca::get_analogs() });
|
analogs.insert({ museca, museca::get_analogs() });
|
||||||
lights.insert({ museca, museca::get_lights() });
|
lights.insert({ museca, museca::get_lights() });
|
||||||
file_hints[museca].emplace_back("museca.dll");
|
file_hints[museca].push_back({"museca.dll"});
|
||||||
|
|
||||||
// nost
|
// nost
|
||||||
const std::string nost("Nostalgia");
|
const std::string nost("Nostalgia");
|
||||||
@@ -154,7 +154,7 @@ namespace games {
|
|||||||
buttons_help.insert({ nost, nost::get_buttons_help() });
|
buttons_help.insert({ nost, nost::get_buttons_help() });
|
||||||
analogs.insert({ nost, nost::get_analogs() });
|
analogs.insert({ nost, nost::get_analogs() });
|
||||||
lights.insert({ nost, nost::get_lights() });
|
lights.insert({ nost, nost::get_lights() });
|
||||||
file_hints[nost].emplace_back("nostalgia.dll");
|
file_hints[nost].push_back({"nostalgia.dll"});
|
||||||
|
|
||||||
// popn
|
// popn
|
||||||
const std::string popn("Pop'n Music");
|
const std::string popn("Pop'n Music");
|
||||||
@@ -162,41 +162,42 @@ namespace games {
|
|||||||
buttons.insert({ popn, popn::get_buttons() });
|
buttons.insert({ popn, popn::get_buttons() });
|
||||||
buttons_help.insert({ popn, popn::get_buttons_help() });
|
buttons_help.insert({ popn, popn::get_buttons_help() });
|
||||||
lights.insert({ popn, popn::get_lights() });
|
lights.insert({ popn, popn::get_lights() });
|
||||||
file_hints[popn].emplace_back("popn19.dll");
|
file_hints[popn].push_back({"popn19.dll"});
|
||||||
file_hints[popn].emplace_back("popn20.dll");
|
file_hints[popn].push_back({"popn20.dll"});
|
||||||
file_hints[popn].emplace_back("popn21.dll");
|
file_hints[popn].push_back({"popn21.dll"});
|
||||||
file_hints[popn].emplace_back("popn22.dll");
|
file_hints[popn].push_back({"popn22.dll"});
|
||||||
file_hints[popn].emplace_back("popn23.dll");
|
file_hints[popn].push_back({"popn23.dll"});
|
||||||
file_hints[popn].emplace_back("popn24.dll");
|
file_hints[popn].push_back({"popn24.dll"});
|
||||||
file_hints[popn].emplace_back("popn25.dll");
|
file_hints[popn].push_back({"popn25.dll"});
|
||||||
|
file_hints[popn].push_back({"popn.dll", "libaio-iob2_video.dll"});
|
||||||
|
|
||||||
// qma
|
// qma
|
||||||
const std::string qma("Quiz Magic Academy");
|
const std::string qma("Quiz Magic Academy");
|
||||||
games.push_back(qma);
|
games.push_back(qma);
|
||||||
buttons.insert({ qma, qma::get_buttons() });
|
buttons.insert({ qma, qma::get_buttons() });
|
||||||
lights.insert({ qma, qma::get_lights() });
|
lights.insert({ qma, qma::get_lights() });
|
||||||
file_hints[qma].emplace_back("client.dll");
|
file_hints[qma].push_back({"client.dll"});
|
||||||
|
|
||||||
// rb
|
// rb
|
||||||
const std::string rb("Reflec Beat");
|
const std::string rb("Reflec Beat");
|
||||||
games.push_back(rb);
|
games.push_back(rb);
|
||||||
buttons.insert({ rb, rb::get_buttons() });
|
buttons.insert({ rb, rb::get_buttons() });
|
||||||
lights.insert({ rb, rb::get_lights() });
|
lights.insert({ rb, rb::get_lights() });
|
||||||
file_hints[rb].emplace_back("reflecbeat.dll");
|
file_hints[rb].push_back({"reflecbeat.dll"});
|
||||||
|
|
||||||
// shogikai
|
// shogikai
|
||||||
std::string shogikai("Tenkaichi Shogikai");
|
std::string shogikai("Tenkaichi Shogikai");
|
||||||
games.push_back(shogikai);
|
games.push_back(shogikai);
|
||||||
buttons.insert({ shogikai, shogikai::get_buttons() });
|
buttons.insert({ shogikai, shogikai::get_buttons() });
|
||||||
lights.insert({ shogikai, shogikai::get_lights() });
|
lights.insert({ shogikai, shogikai::get_lights() });
|
||||||
file_hints[shogikai].emplace_back("shogi_engine.dll");
|
file_hints[shogikai].push_back({"shogi_engine.dll"});
|
||||||
|
|
||||||
// rf3d
|
// rf3d
|
||||||
const std::string rf3d("Road Fighters 3D");
|
const std::string rf3d("Road Fighters 3D");
|
||||||
games.push_back(rf3d);
|
games.push_back(rf3d);
|
||||||
buttons.insert({ rf3d, rf3d::get_buttons() });
|
buttons.insert({ rf3d, rf3d::get_buttons() });
|
||||||
analogs.insert({ rf3d, rf3d::get_analogs() });
|
analogs.insert({ rf3d, rf3d::get_analogs() });
|
||||||
file_hints[rf3d].emplace_back("jgt.dll");
|
file_hints[rf3d].push_back({"jgt.dll"});
|
||||||
|
|
||||||
// sc
|
// sc
|
||||||
const std::string sc("Steel Chronicle");
|
const std::string sc("Steel Chronicle");
|
||||||
@@ -204,7 +205,7 @@ namespace games {
|
|||||||
buttons.insert({ sc, sc::get_buttons() });
|
buttons.insert({ sc, sc::get_buttons() });
|
||||||
analogs.insert({ sc, sc::get_analogs() });
|
analogs.insert({ sc, sc::get_analogs() });
|
||||||
lights.insert({ sc, sc::get_lights() });
|
lights.insert({ sc, sc::get_lights() });
|
||||||
file_hints[sc].emplace_back("gamekgg.dll");
|
file_hints[sc].push_back({"gamekgg.dll"});
|
||||||
|
|
||||||
// sdvx
|
// sdvx
|
||||||
const std::string sdvx("Sound Voltex");
|
const std::string sdvx("Sound Voltex");
|
||||||
@@ -213,13 +214,13 @@ namespace games {
|
|||||||
buttons_help.insert({ sdvx, sdvx::get_buttons_help() });
|
buttons_help.insert({ sdvx, sdvx::get_buttons_help() });
|
||||||
analogs.insert({ sdvx, sdvx::get_analogs() });
|
analogs.insert({ sdvx, sdvx::get_analogs() });
|
||||||
lights.insert({ sdvx, sdvx::get_lights() });
|
lights.insert({ sdvx, sdvx::get_lights() });
|
||||||
file_hints[sdvx].emplace_back("soundvoltex.dll");
|
file_hints[sdvx].push_back({"soundvoltex.dll"});
|
||||||
|
|
||||||
// mfc
|
// mfc
|
||||||
const std::string mfc("Mahjong Fight Club");
|
const std::string mfc("Mahjong Fight Club");
|
||||||
games.push_back(mfc);
|
games.push_back(mfc);
|
||||||
buttons.insert({ mfc, mfc::get_buttons() });
|
buttons.insert({ mfc, mfc::get_buttons() });
|
||||||
file_hints[mfc].emplace_back("allinone.dll");
|
file_hints[mfc].push_back({"allinone.dll"});
|
||||||
|
|
||||||
// ftt
|
// ftt
|
||||||
const std::string ftt("FutureTomTom");
|
const std::string ftt("FutureTomTom");
|
||||||
@@ -228,21 +229,21 @@ namespace games {
|
|||||||
buttons_help.insert({ ftt, ftt::get_buttons_help() });
|
buttons_help.insert({ ftt, ftt::get_buttons_help() });
|
||||||
analogs.insert({ ftt, ftt::get_analogs() });
|
analogs.insert({ ftt, ftt::get_analogs() });
|
||||||
lights.insert({ ftt, ftt::get_lights() });
|
lights.insert({ ftt, ftt::get_lights() });
|
||||||
file_hints[ftt].emplace_back("arkmmd.dll");
|
file_hints[ftt].push_back({"arkmmd.dll"});
|
||||||
|
|
||||||
// loveplus
|
// loveplus
|
||||||
const std::string loveplus("LovePlus");
|
const std::string loveplus("LovePlus");
|
||||||
games.push_back(loveplus);
|
games.push_back(loveplus);
|
||||||
buttons.insert({ loveplus, loveplus::get_buttons() });
|
buttons.insert({ loveplus, loveplus::get_buttons() });
|
||||||
lights.insert({ loveplus, loveplus::get_lights() });
|
lights.insert({ loveplus, loveplus::get_lights() });
|
||||||
file_hints[loveplus].emplace_back("arkklp.dll");
|
file_hints[loveplus].push_back({"arkklp.dll"});
|
||||||
|
|
||||||
// scotto
|
// scotto
|
||||||
const std::string scotto("Scotto");
|
const std::string scotto("Scotto");
|
||||||
games.push_back(scotto);
|
games.push_back(scotto);
|
||||||
buttons.insert({ scotto, scotto::get_buttons() });
|
buttons.insert({ scotto, scotto::get_buttons() });
|
||||||
lights.insert({ scotto, scotto::get_lights() });
|
lights.insert({ scotto, scotto::get_lights() });
|
||||||
file_hints[scotto].emplace_back("scotto.dll");
|
file_hints[scotto].push_back({"scotto.dll"});
|
||||||
|
|
||||||
// drs
|
// drs
|
||||||
const std::string drs("DANCERUSH");
|
const std::string drs("DANCERUSH");
|
||||||
@@ -250,14 +251,14 @@ namespace games {
|
|||||||
buttons.insert({ drs, drs::get_buttons() });
|
buttons.insert({ drs, drs::get_buttons() });
|
||||||
buttons_help.insert({ drs, drs::get_buttons_help() });
|
buttons_help.insert({ drs, drs::get_buttons_help() });
|
||||||
lights.insert({ drs, drs::get_lights() });
|
lights.insert({ drs, drs::get_lights() });
|
||||||
file_hints[drs].emplace_back("superstep.dll");
|
file_hints[drs].push_back({"superstep.dll"});
|
||||||
|
|
||||||
// otoca
|
// otoca
|
||||||
const std::string otoca("Otoca D'or");
|
const std::string otoca("Otoca D'or");
|
||||||
games.push_back(otoca);
|
games.push_back(otoca);
|
||||||
buttons.insert({ otoca, otoca::get_buttons() });
|
buttons.insert({ otoca, otoca::get_buttons() });
|
||||||
buttons_help.insert({ otoca, otoca::get_buttons_help() });
|
buttons_help.insert({ otoca, otoca::get_buttons_help() });
|
||||||
file_hints[otoca].emplace_back("arkkep.dll");
|
file_hints[otoca].push_back({"arkkep.dll"});
|
||||||
|
|
||||||
// winning eleven
|
// winning eleven
|
||||||
const std::string we("Winning Eleven");
|
const std::string we("Winning Eleven");
|
||||||
@@ -265,27 +266,27 @@ namespace games {
|
|||||||
buttons.insert({ we, we::get_buttons() });
|
buttons.insert({ we, we::get_buttons() });
|
||||||
analogs.insert({ we, we::get_analogs() });
|
analogs.insert({ we, we::get_analogs() });
|
||||||
lights.insert({ we, we::get_lights() });
|
lights.insert({ we, we::get_lights() });
|
||||||
file_hints[we].emplace_back("weac12_bootstrap_release.dll");
|
file_hints[we].push_back({"weac12_bootstrap_release.dll"});
|
||||||
file_hints[we].emplace_back("arknck.dll");
|
file_hints[we].push_back({"arknck.dll"});
|
||||||
|
|
||||||
// silent scope: bone eater
|
// silent scope: bone eater
|
||||||
const std::string silentscope("Silent Scope: Bone Eater");
|
const std::string silentscope("Silent Scope: Bone Eater");
|
||||||
games.push_back(silentscope);
|
games.push_back(silentscope);
|
||||||
buttons.insert({ silentscope, silentscope::get_buttons() });
|
buttons.insert({ silentscope, silentscope::get_buttons() });
|
||||||
analogs.insert({ silentscope, silentscope::get_analogs() });
|
analogs.insert({ silentscope, silentscope::get_analogs() });
|
||||||
file_hints[silentscope].emplace_back("arkndd.dll");
|
file_hints[silentscope].push_back({"arkndd.dll"});
|
||||||
|
|
||||||
// charge machine
|
// charge machine
|
||||||
const std::string pcm("Charge Machine");
|
const std::string pcm("Charge Machine");
|
||||||
games.push_back(pcm);
|
games.push_back(pcm);
|
||||||
buttons.insert({ pcm, pcm::get_buttons() });
|
buttons.insert({ pcm, pcm::get_buttons() });
|
||||||
file_hints[pcm].emplace_back("launch.dll");
|
file_hints[pcm].push_back({"launch.dll"});
|
||||||
|
|
||||||
// ongaku paradise
|
// ongaku paradise
|
||||||
const std::string op("Ongaku Paradise");
|
const std::string op("Ongaku Paradise");
|
||||||
games.push_back(op);
|
games.push_back(op);
|
||||||
buttons.insert({ op, onpara::get_buttons() });
|
buttons.insert({ op, onpara::get_buttons() });
|
||||||
file_hints[op].emplace_back("arkjc9.dll");
|
file_hints[op].push_back({"arkjc9.dll"});
|
||||||
|
|
||||||
// bc
|
// bc
|
||||||
const std::string bc("Busou Shinki: Armored Princess Battle Conductor");
|
const std::string bc("Busou Shinki: Armored Princess Battle Conductor");
|
||||||
@@ -293,7 +294,7 @@ namespace games {
|
|||||||
buttons.insert({ bc, bc::get_buttons() });
|
buttons.insert({ bc, bc::get_buttons() });
|
||||||
buttons_help.insert({ bc, bc::get_buttons_help() });
|
buttons_help.insert({ bc, bc::get_buttons_help() });
|
||||||
analogs.insert({ bc, bc::get_analogs() });
|
analogs.insert({ bc, bc::get_analogs() });
|
||||||
file_hints[bc].emplace_back("game/bsac_app.exe");
|
file_hints[bc].push_back({"game/bsac_app.exe"});
|
||||||
|
|
||||||
// ccj
|
// ccj
|
||||||
const std::string ccj("Chase Chase Jokers");
|
const std::string ccj("Chase Chase Jokers");
|
||||||
@@ -302,21 +303,21 @@ namespace games {
|
|||||||
buttons_help.insert({ ccj, ccj::get_buttons_help() });
|
buttons_help.insert({ ccj, ccj::get_buttons_help() });
|
||||||
analogs.insert({ ccj, ccj::get_analogs() });
|
analogs.insert({ ccj, ccj::get_analogs() });
|
||||||
lights.insert({ ccj, ccj::get_lights() });
|
lights.insert({ ccj, ccj::get_lights() });
|
||||||
file_hints[ccj].emplace_back("game/chaseproject.exe");
|
file_hints[ccj].push_back({"game/chaseproject.exe"});
|
||||||
|
|
||||||
// QuizKnock STADIUM
|
// QuizKnock STADIUM
|
||||||
const std::string qks("QuizKnock STADIUM");
|
const std::string qks("QuizKnock STADIUM");
|
||||||
games.push_back(qks);
|
games.push_back(qks);
|
||||||
buttons.insert({ qks, qks::get_buttons() });
|
buttons.insert({ qks, qks::get_buttons() });
|
||||||
buttons_help.insert({ qks, qks::get_buttons_help() });
|
buttons_help.insert({ qks, qks::get_buttons_help() });
|
||||||
file_hints[qks].emplace_back("game/uks.exe");
|
file_hints[qks].push_back({"game/uks.exe"});
|
||||||
|
|
||||||
// Mahjong Fight Girl
|
// Mahjong Fight Girl
|
||||||
const std::string mfg("Mahjong Fight Girl");
|
const std::string mfg("Mahjong Fight Girl");
|
||||||
games.push_back(mfg);
|
games.push_back(mfg);
|
||||||
buttons.insert({ mfg, mfg::get_buttons() });
|
buttons.insert({ mfg, mfg::get_buttons() });
|
||||||
buttons_help.insert({ mfg, mfg::get_buttons_help() });
|
buttons_help.insert({ mfg, mfg::get_buttons_help() });
|
||||||
file_hints[mfg].emplace_back("game/MFGClient_Data");
|
file_hints[mfg].push_back({"game/MFGClient_Data"});
|
||||||
|
|
||||||
// Polaris Chord
|
// Polaris Chord
|
||||||
const std::string pc("Polaris Chord");
|
const std::string pc("Polaris Chord");
|
||||||
@@ -325,7 +326,7 @@ namespace games {
|
|||||||
buttons_help.insert({ pc, pc::get_buttons_help() });
|
buttons_help.insert({ pc, pc::get_buttons_help() });
|
||||||
analogs.insert({ pc, pc::get_analogs() });
|
analogs.insert({ pc, pc::get_analogs() });
|
||||||
lights.insert({ pc, pc::get_lights() });
|
lights.insert({ pc, pc::get_lights() });
|
||||||
file_hints[pc].emplace_back("game/svm.exe");
|
file_hints[pc].push_back({"game/svm.exe"});
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<std::string> &get_games() {
|
const std::vector<std::string> &get_games() {
|
||||||
@@ -559,7 +560,7 @@ namespace games {
|
|||||||
return &it->second;
|
return &it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> *get_game_file_hints(const std::string &game) {
|
std::vector<std::vector<std::string>> *get_game_file_hints(const std::string &game) {
|
||||||
initialize();
|
initialize();
|
||||||
auto it = file_hints.find(game);
|
auto it = file_hints.find(game);
|
||||||
if (it == file_hints.end()) {
|
if (it == file_hints.end()) {
|
||||||
|
|||||||
@@ -64,5 +64,5 @@ namespace games {
|
|||||||
std::vector<Analog> *get_analogs(const std::string &game);
|
std::vector<Analog> *get_analogs(const std::string &game);
|
||||||
std::vector<Light> *get_lights(const std::string &game);
|
std::vector<Light> *get_lights(const std::string &game);
|
||||||
std::vector<Option> *get_options(const std::string &game);
|
std::vector<Option> *get_options(const std::string &game);
|
||||||
std::vector<std::string> *get_game_file_hints(const std::string &game);
|
std::vector<std::vector<std::string>> *get_game_file_hints(const std::string &game);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,552 @@
|
|||||||
|
#include "bi3a_hook.h"
|
||||||
|
|
||||||
|
#if SPICE64 && !SPICE_XP
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <cstdint>
|
||||||
|
#include "util/detour.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
#include "rawinput/rawinput.h"
|
||||||
|
#include "misc/eamuse.h"
|
||||||
|
#include "games/io.h"
|
||||||
|
#include "popn.h"
|
||||||
|
#include "io.h"
|
||||||
|
#include "util/tapeled.h"
|
||||||
|
#include <typeinfo>
|
||||||
|
|
||||||
|
#ifdef min
|
||||||
|
#undef min
|
||||||
|
#endif
|
||||||
|
#ifdef max
|
||||||
|
#undef max
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace games::popn {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* class definitions
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct AIO_NMGR_IOB2 {
|
||||||
|
uint8_t data[0xe18];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_NMGR_IOB5 {
|
||||||
|
uint8_t data[0xb10];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_SCI_COMM {
|
||||||
|
uint8_t data[0x108];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_SCI_COMM_T {
|
||||||
|
uint64_t unk_0;
|
||||||
|
struct AIO_SCI_COMM *aioSciComm;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_COMM_STATUS {
|
||||||
|
uint64_t unk[5];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AIO_IOB5_BI3A {
|
||||||
|
uint8_t data[0x4FF0];
|
||||||
|
};
|
||||||
|
|
||||||
|
union AIO_IOB5_BI3A__DEVSTATUS {
|
||||||
|
uint8_t RawBytes[648];
|
||||||
|
struct {
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
// verified with M39-004-2025121500
|
||||||
|
static_assert(sizeof(AIO_NMGR_IOB2) == 0xe18);
|
||||||
|
static_assert(sizeof(AIO_NMGR_IOB5) == 0xb10);
|
||||||
|
static_assert(sizeof(AIO_SCI_COMM) == 0x108);
|
||||||
|
static_assert(sizeof(AIO_IOB5_BI3A) == 0x4FF0);
|
||||||
|
static_assert(sizeof(AIO_IOB5_BI3A__DEVSTATUS) == 0x288);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* typedefs
|
||||||
|
*/
|
||||||
|
|
||||||
|
// libaio.dll
|
||||||
|
typedef AIO_SCI_COMM_T *(__fastcall *aioSciComm_Open_t)(AIO_SCI_COMM_T *unk);
|
||||||
|
typedef void(__fastcall *aioSci_Destroy_t)(AIO_SCI_COMM *i_pNodeMgr);
|
||||||
|
typedef void(__fastcall *aioSci_GetCommStatus_t)(AIO_SCI_COMM *i_pNodeMgr, AIO_COMM_STATUS *i_Status);
|
||||||
|
typedef void(__fastcall *aioNodeMgr_Destroy_t)(AIO_NMGR_IOB5 *i_pNodeMgr);
|
||||||
|
typedef int32_t(__fastcall *aioNodeMgr_GetState_t)(AIO_NMGR_IOB5 *i_pNodeMgr);
|
||||||
|
typedef bool(__fastcall *aioNodeMgr_IsReady_t)(AIO_NMGR_IOB5 *i_pNodeMgr, int32_t i_State);
|
||||||
|
typedef bool(__fastcall *aioNodeMgr_IsError_t)(AIO_NMGR_IOB5 *i_pNodeMgr, int32_t i_State);
|
||||||
|
typedef void (__fastcall *aioNodeCtl_Destroy_t)(AIO_IOB5_BI3A *i_pNodeCtl);
|
||||||
|
typedef int32_t (__fastcall *aioNodeCtl_GetState_t)(AIO_IOB5_BI3A *i_pNodeCtl);
|
||||||
|
typedef bool (__fastcall *aioNodeCtl_IsReady_t)(AIO_IOB5_BI3A *i_pNodeCtl, int32_t i_State);
|
||||||
|
typedef bool (__fastcall *aioNodeCtl_IsError_t)(AIO_IOB5_BI3A *i_pNodeCtl, int32_t i_State);
|
||||||
|
typedef void(__fastcall *aioNodeCtl_UpdateDevicesStatus_t)();
|
||||||
|
|
||||||
|
// libaio-iob5.dll
|
||||||
|
typedef AIO_NMGR_IOB5 *(__fastcall *aioNMgrIob5_Create_t)(AIO_SCI_COMM *i_pSci, uint32_t i_bfMode);
|
||||||
|
typedef AIO_IOB5_BI3A *(__fastcall *aioIob5Bi3a_Create_t)(AIO_NMGR_IOB5 *i_pNodeMgr, uint8_t i, void *p);
|
||||||
|
typedef void(__fastcall *aioIob5Bi3a_GetDeviceStatus_t)(AIO_IOB5_BI3A *i_pNodeCtl, AIO_IOB5_BI3A__DEVSTATUS *o_DevStatus);
|
||||||
|
typedef void(__fastcall *aioIob5Bi3a_SetOutputData_t)(AIO_IOB5_BI3A *a1, unsigned int a2, unsigned __int8 a3);
|
||||||
|
typedef void(__fastcall *aioIob5Bi3a_SetTapeLedDataPart_t)(AIO_IOB5_BI3A *a1, unsigned int a2, char a3, const void *a4, unsigned int a5, bool a6);
|
||||||
|
typedef int64_t (__fastcall *aioIob5Bi3a_SetTapeLedDataLimit_t)(AIO_IOB5_BI3A *i_pNodeCtl, uint32_t i_Channel, uint8_t i_Scale, uint8_t i_Limit);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* function pointers
|
||||||
|
*/
|
||||||
|
|
||||||
|
// libaio.dll
|
||||||
|
static aioSciComm_Open_t aioSciComm_Open_orig = nullptr;
|
||||||
|
static aioSci_Destroy_t aioSci_Destroy_orig = nullptr;
|
||||||
|
static aioSci_GetCommStatus_t aioSci_GetCommStatus_orig = nullptr;
|
||||||
|
static aioNodeMgr_Destroy_t aioNodeMgr_Destroy_orig = nullptr;
|
||||||
|
static aioNodeMgr_GetState_t aioNodeMgr_GetState_orig = nullptr;
|
||||||
|
static aioNodeMgr_IsReady_t aioNodeMgr_IsReady_orig = nullptr;
|
||||||
|
static aioNodeMgr_IsError_t aioNodeMgr_IsError_orig = nullptr;
|
||||||
|
static aioNodeCtl_Destroy_t aioNodeCtl_Destroy_orig = nullptr;
|
||||||
|
static aioNodeCtl_GetState_t aioNodeCtl_GetState_orig = nullptr;
|
||||||
|
static aioNodeCtl_IsReady_t aioNodeCtl_IsReady_orig = nullptr;
|
||||||
|
static aioNodeCtl_IsError_t aioNodeCtl_IsError_orig = nullptr;
|
||||||
|
static aioNodeCtl_UpdateDevicesStatus_t aioNodeCtl_UpdateDevicesStatus_orig = nullptr;
|
||||||
|
|
||||||
|
// libaio-iob5.dll
|
||||||
|
static aioNMgrIob5_Create_t aioNMgrIob5_Create_orig = nullptr;
|
||||||
|
static aioIob5Bi3a_Create_t aioIob5Bi3a_Create_orig = nullptr;
|
||||||
|
static aioIob5Bi3a_GetDeviceStatus_t aioIob5Bi3a_GetDeviceStatus_orig = nullptr;
|
||||||
|
static aioIob5Bi3a_SetOutputData_t aioIob5Bi3a_SetOutputData_orig = nullptr;
|
||||||
|
static aioIob5Bi3a_SetTapeLedDataPart_t aioIob5Bi3a_SetTapeLedDataPart_orig = nullptr;
|
||||||
|
static aioIob5Bi3a_SetTapeLedDataLimit_t aioIob5Bi3a_SetTapeLedDataLimit_orig = nullptr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* variables
|
||||||
|
*/
|
||||||
|
static AIO_NMGR_IOB2 *aioNmgrIob2;
|
||||||
|
static AIO_NMGR_IOB5 *aioNmgrIob5;
|
||||||
|
static AIO_SCI_COMM *aioSciComm;
|
||||||
|
static AIO_IOB5_BI3A *aioIob5Bi3a;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* implementations
|
||||||
|
*/
|
||||||
|
|
||||||
|
// libaio.dll
|
||||||
|
|
||||||
|
static AIO_SCI_COMM_T *__fastcall aioSciComm_Open(AIO_SCI_COMM_T *unk) {
|
||||||
|
aioSciComm = new AIO_SCI_COMM;
|
||||||
|
memset(unk, 0, sizeof(AIO_SCI_COMM_T));
|
||||||
|
unk->aioSciComm = aioSciComm;
|
||||||
|
return unk;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __fastcall aioSci_GetCommStatus(AIO_SCI_COMM *i_pNodeMgr, AIO_COMM_STATUS *i_Status) {
|
||||||
|
if (i_pNodeMgr == aioSciComm) {
|
||||||
|
memset(i_Status, 0, sizeof(AIO_COMM_STATUS));
|
||||||
|
} else {
|
||||||
|
return aioSci_GetCommStatus_orig(i_pNodeMgr, i_Status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __fastcall aioSci_Destroy(AIO_SCI_COMM *i_pNodeMgr) {
|
||||||
|
if (i_pNodeMgr == aioSciComm) {
|
||||||
|
delete aioSciComm;
|
||||||
|
} else {
|
||||||
|
return aioSci_Destroy_orig(i_pNodeMgr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __fastcall aioNodeMgr_Destroy(AIO_NMGR_IOB5 *i_pNodeMgr) {
|
||||||
|
if (i_pNodeMgr == aioNmgrIob5 ||
|
||||||
|
i_pNodeMgr == (AIO_NMGR_IOB5 *)aioNmgrIob2) {
|
||||||
|
|
||||||
|
delete aioNmgrIob5;
|
||||||
|
aioNmgrIob5 = nullptr;
|
||||||
|
} else {
|
||||||
|
return aioNodeMgr_Destroy_orig(i_pNodeMgr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t __fastcall aioNodeMgr_GetState(AIO_NMGR_IOB5 *i_pNodeMgr) {
|
||||||
|
if (i_pNodeMgr == aioNmgrIob5 ||
|
||||||
|
i_pNodeMgr == (AIO_NMGR_IOB5 *)aioNmgrIob2) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return aioNodeMgr_GetState_orig(i_pNodeMgr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __fastcall aioNodeMgr_IsReady(AIO_NMGR_IOB5 *i_pNodeMgr, int32_t i_State) {
|
||||||
|
if (i_pNodeMgr == aioNmgrIob5 ||
|
||||||
|
i_pNodeMgr == (AIO_NMGR_IOB5 *)aioNmgrIob2) {
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return aioNodeMgr_IsReady_orig(i_pNodeMgr, i_State);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __fastcall aioNodeMgr_IsError(AIO_NMGR_IOB5 *i_pNodeMgr, int32_t i_State) {
|
||||||
|
if (i_pNodeMgr == aioNmgrIob5 ||
|
||||||
|
i_pNodeMgr == (AIO_NMGR_IOB5 *)aioNmgrIob2) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return aioNodeMgr_IsError_orig(i_pNodeMgr, i_State);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __fastcall aioNodeCtl_Destroy(AIO_IOB5_BI3A *i_pNodeCtl) {
|
||||||
|
if (i_pNodeCtl != aioIob5Bi3a) {
|
||||||
|
return aioNodeCtl_Destroy_orig(i_pNodeCtl);
|
||||||
|
}
|
||||||
|
delete aioIob5Bi3a;
|
||||||
|
aioIob5Bi3a = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t __fastcall aioNodeCtl_GetState(AIO_IOB5_BI3A *i_pNodeCtl) {
|
||||||
|
if (i_pNodeCtl == aioIob5Bi3a) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return aioNodeCtl_GetState_orig(i_pNodeCtl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __fastcall aioNodeCtl_IsReady(AIO_IOB5_BI3A *i_pNodeCtl, int32_t i_State) {
|
||||||
|
if (i_pNodeCtl == aioIob5Bi3a) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return aioNodeCtl_IsReady_orig(i_pNodeCtl, i_State);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __fastcall aioNodeCtl_IsError(AIO_IOB5_BI3A *i_pNodeCtl, int32_t i_State) {
|
||||||
|
if (i_pNodeCtl == aioIob5Bi3a) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return aioNodeCtl_IsError_orig(i_pNodeCtl, i_State);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __fastcall aioNodeCtl_UpdateDevicesStatus() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// libaio-iob5.dll
|
||||||
|
|
||||||
|
static AIO_NMGR_IOB5 *__fastcall aioNMgrIob5_Create(AIO_SCI_COMM *i_pSci, uint32_t i_bfMode) {
|
||||||
|
|
||||||
|
if (i_pSci == aioSciComm) {
|
||||||
|
aioNmgrIob5 = new AIO_NMGR_IOB5;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static AIO_IOB5_BI3A *__fastcall aioIob5Bi3a_Create(AIO_NMGR_IOB5 *i_pNodeMgr, uint8_t i, void *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("bi3a_hook", "aioIob5Bi3a_Create: returning custom AIO_IOB5_BI3A: {}", fmt::ptr(aioIob5Bi3a));
|
||||||
|
return aioIob5Bi3a;
|
||||||
|
} else {
|
||||||
|
return aioIob5Bi3a_Create_orig(i_pNodeMgr, i, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __fastcall aioIob5Bi3a_GetDeviceStatus(AIO_IOB5_BI3A *i_pNodeCtl, AIO_IOB5_BI3A__DEVSTATUS *o_DevStatus) {
|
||||||
|
RI_MGR->devices_flush_output();
|
||||||
|
if (i_pNodeCtl != aioIob5Bi3a) {
|
||||||
|
return aioIob5Bi3a_GetDeviceStatus_orig(i_pNodeCtl, o_DevStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(o_DevStatus, 0, sizeof(*o_DevStatus));
|
||||||
|
auto &buttons = get_buttons();
|
||||||
|
|
||||||
|
// operator
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Test])) {
|
||||||
|
o_DevStatus->Input.TestButton = 1;
|
||||||
|
}
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Service])) {
|
||||||
|
o_DevStatus->Input.ServiceButton = 1;
|
||||||
|
}
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::CoinMech])) {
|
||||||
|
o_DevStatus->Input.CoinMech = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// buttons 1-9
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button1])) {
|
||||||
|
o_DevStatus->Input.Buttons_0_8 |= (1 << 0);
|
||||||
|
}
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button2])) {
|
||||||
|
o_DevStatus->Input.Buttons_0_8 |= (1 << 1);
|
||||||
|
}
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button3])) {
|
||||||
|
o_DevStatus->Input.Buttons_0_8 |= (1 << 2);
|
||||||
|
}
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button4])) {
|
||||||
|
o_DevStatus->Input.Buttons_0_8 |= (1 << 3);
|
||||||
|
}
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button5])) {
|
||||||
|
o_DevStatus->Input.Buttons_0_8 |= (1 << 4);
|
||||||
|
}
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button6])) {
|
||||||
|
o_DevStatus->Input.Buttons_0_8 |= (1 << 5);
|
||||||
|
}
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button7])) {
|
||||||
|
o_DevStatus->Input.Buttons_0_8 |= (1 << 6);
|
||||||
|
}
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button8])) {
|
||||||
|
o_DevStatus->Input.Buttons_0_8 |= (1 << 7);
|
||||||
|
}
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button9])) {
|
||||||
|
o_DevStatus->Input.Button_9 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// deka pop-kuns
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::RedPop])) {
|
||||||
|
o_DevStatus->Input.JOYL_SW = 1;
|
||||||
|
}
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::BluePop])) {
|
||||||
|
o_DevStatus->Input.JOYR_SW = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// audio
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Headphone])) {
|
||||||
|
o_DevStatus->Input.Headphones = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
if (i_pNodeCtl != aioIob5Bi3a) {
|
||||||
|
return aioIob5Bi3a_SetOutputData_orig(i_pNodeCtl, i_CnPin, i_Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7 = IC_CARD_R
|
||||||
|
// 8 = IC_CARD_G
|
||||||
|
// 9 = IC_CARD_B
|
||||||
|
// 10 = WOOFER_R
|
||||||
|
// 11 = WOOFER_G
|
||||||
|
// 12 = WOOFER_B
|
||||||
|
// values = 0 to 255 in test menu
|
||||||
|
|
||||||
|
std::optional<Lights::popn_lights_t> 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;
|
||||||
|
case 11:
|
||||||
|
light = Lights::WooferLED_G;
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
light = Lights::WooferLED_B;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (light.has_value()) {
|
||||||
|
auto &lights = get_lights();
|
||||||
|
GameAPI::Lights::writeLight(RI_MGR, lights.at(light.value()), i_Data / 255.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64_t __fastcall aioIob5Bi3a_SetTapeLedDataLimit(
|
||||||
|
AIO_IOB5_BI3A *i_pNodeCtl, uint32_t i_Channel, uint8_t i_Scale, uint8_t i_Limit) {
|
||||||
|
|
||||||
|
if (i_pNodeCtl != aioIob5Bi3a) {
|
||||||
|
return aioIob5Bi3a_SetTapeLedDataLimit_orig(i_pNodeCtl, i_Channel, i_Scale, i_Limit);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __fastcall aioIob5Bi3a_SetTapeLedDataPart(
|
||||||
|
AIO_IOB5_BI3A *i_pNodeCtl, uint32_t i_CnPin, char i_LedType, const void *i_pData, uint32_t i_DataSize, bool i_bIsLast) {
|
||||||
|
|
||||||
|
if (i_pNodeCtl != aioIob5Bi3a) {
|
||||||
|
return aioIob5Bi3a_SetTapeLedDataPart_orig(i_pNodeCtl, i_CnPin, i_LedType, i_pData, i_DataSize, i_bIsLast);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto number_of_leds = i_DataSize;
|
||||||
|
// buttons 1-9, but game passes in wrong length; fix it up
|
||||||
|
if (i_CnPin == 0 && i_DataSize == 9) {
|
||||||
|
number_of_leds = i_DataSize * 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FALSE) {
|
||||||
|
std::string data;
|
||||||
|
for (uint32_t i = 0; i < number_of_leds; i++) {
|
||||||
|
auto dataByte = ((uint8_t *)i_pData)[i];
|
||||||
|
data += fmt::format("{:02X} ", dataByte);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info(
|
||||||
|
"bi3a_hook",
|
||||||
|
"aioIob5Bi3a_SetTapeLedDataPart Pin={}, Size={}, Type={}, data@{} = {}",
|
||||||
|
i_CnPin, number_of_leds, (uint8_t)i_LedType, fmt::ptr(i_pData), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &lights = get_lights();
|
||||||
|
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] =
|
||||||
|
std::max({ ((uint8_t *)i_pData)[i], ((uint8_t *)i_pData)[i + 1], ((uint8_t *)i_pData)[i + 2] });
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Lights::popn_lights_t legacy_button_lights[] = {
|
||||||
|
Lights::popn_lights_t::Button1,
|
||||||
|
Lights::popn_lights_t::Button2,
|
||||||
|
Lights::popn_lights_t::Button3,
|
||||||
|
Lights::popn_lights_t::Button4,
|
||||||
|
Lights::popn_lights_t::Button5,
|
||||||
|
Lights::popn_lights_t::Button6,
|
||||||
|
Lights::popn_lights_t::Button7,
|
||||||
|
Lights::popn_lights_t::Button8,
|
||||||
|
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,
|
||||||
|
// otherwise off, to avoid dimly lit "off" state
|
||||||
|
GameAPI::Lights::writeLight(
|
||||||
|
RI_MGR,
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(map, (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() {
|
||||||
|
|
||||||
|
// avoid double init
|
||||||
|
static bool initialized = false;
|
||||||
|
if (initialized) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// announce
|
||||||
|
log_info("bi3a_hook", "init");
|
||||||
|
|
||||||
|
// libaio-iob5.dll
|
||||||
|
const auto libaioIob5Dll = "libaio-iob5.dll";
|
||||||
|
detour::trampoline_try(libaioIob5Dll, "aioNMgrIob5_Create",
|
||||||
|
aioNMgrIob5_Create, &aioNMgrIob5_Create_orig);
|
||||||
|
detour::trampoline_try(libaioIob5Dll, "aioIob5Bi3a_Create",
|
||||||
|
aioIob5Bi3a_Create, &aioIob5Bi3a_Create_orig);
|
||||||
|
detour::trampoline_try(libaioIob5Dll, "aioIob5Bi3a_GetDeviceStatus",
|
||||||
|
aioIob5Bi3a_GetDeviceStatus, &aioIob5Bi3a_GetDeviceStatus_orig);
|
||||||
|
detour::trampoline_try(libaioIob5Dll, "aioIob5Bi3a_SetOutputData",
|
||||||
|
aioIob5Bi3a_SetOutputData, &aioIob5Bi3a_SetOutputData_orig);
|
||||||
|
detour::trampoline_try(libaioIob5Dll, "aioIob5Bi3a_SetTapeLedDataPart",
|
||||||
|
aioIob5Bi3a_SetTapeLedDataPart, &aioIob5Bi3a_SetTapeLedDataPart_orig);
|
||||||
|
detour::trampoline_try(libaioIob5Dll, "aioIob5Bi3a_SetTapeLedDataLimit",
|
||||||
|
aioIob5Bi3a_SetTapeLedDataLimit, &aioIob5Bi3a_SetTapeLedDataLimit_orig);
|
||||||
|
|
||||||
|
// libaio.dll
|
||||||
|
const auto libaioDll = "libaio.dll";
|
||||||
|
detour::trampoline_try(libaioDll, "?Open@AIO_SCI_COMM@@SA?AU?$AC_ERESULT_T@PEAVAIO_SCI_COMM@@@@PEBD0AEBUSETTING@1@I@Z",
|
||||||
|
aioSciComm_Open, &aioSciComm_Open_orig);
|
||||||
|
detour::trampoline_try(libaioDll, "aioSci_GetCommStatus",
|
||||||
|
aioSci_GetCommStatus, &aioSci_GetCommStatus_orig);
|
||||||
|
detour::trampoline_try(libaioDll, "aioSci_Destroy",
|
||||||
|
aioSci_Destroy, &aioSci_Destroy_orig);
|
||||||
|
detour::trampoline_try(libaioDll, "aioNodeMgr_Destroy",
|
||||||
|
aioNodeMgr_Destroy, &aioNodeMgr_Destroy_orig);
|
||||||
|
detour::trampoline_try(libaioDll, "aioNodeMgr_GetState",
|
||||||
|
aioNodeMgr_GetState, &aioNodeMgr_GetState_orig);
|
||||||
|
detour::trampoline_try(libaioDll, "aioNodeMgr_IsReady",
|
||||||
|
aioNodeMgr_IsReady, &aioNodeMgr_IsReady_orig);
|
||||||
|
detour::trampoline_try(libaioDll, "aioNodeMgr_IsError",
|
||||||
|
aioNodeMgr_IsError, &aioNodeMgr_IsError_orig);
|
||||||
|
detour::trampoline_try(libaioDll, "aioNodeCtl_Destroy",
|
||||||
|
aioNodeCtl_Destroy, &aioNodeCtl_Destroy_orig);
|
||||||
|
detour::trampoline_try(libaioDll, "aioNodeCtl_GetState",
|
||||||
|
aioNodeCtl_GetState, &aioNodeCtl_GetState_orig);
|
||||||
|
detour::trampoline_try(libaioDll, "aioNodeCtl_IsReady",
|
||||||
|
aioNodeCtl_IsReady, &aioNodeCtl_IsReady_orig);
|
||||||
|
detour::trampoline_try(libaioDll, "aioNodeCtl_IsError",
|
||||||
|
aioNodeCtl_IsError, &aioNodeCtl_IsError_orig);
|
||||||
|
detour::trampoline_try(libaioDll, "aioNodeCtl_UpdateDevicesStatus",
|
||||||
|
aioNodeCtl_UpdateDevicesStatus, &aioNodeCtl_UpdateDevicesStatus_orig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if SPICE64 && !SPICE_XP
|
||||||
|
|
||||||
|
namespace games::popn {
|
||||||
|
void bi3a_hook_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
+128
-78
@@ -11,37 +11,7 @@ std::vector<Button> &games::popn::get_buttons() {
|
|||||||
"Service",
|
"Service",
|
||||||
"Test",
|
"Test",
|
||||||
"Coin Mech",
|
"Coin Mech",
|
||||||
"Button 1",
|
"Headphones",
|
||||||
"Button 2",
|
|
||||||
"Button 3",
|
|
||||||
"Button 4",
|
|
||||||
"Button 5",
|
|
||||||
"Button 6",
|
|
||||||
"Button 7",
|
|
||||||
"Button 8",
|
|
||||||
"Button 9"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return buttons;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string games::popn::get_buttons_help() {
|
|
||||||
// keep to max 100 characters wide
|
|
||||||
return
|
|
||||||
" 2 4 6 8 \n"
|
|
||||||
" 1 3 5 7 9 "
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Light> &games::popn::get_lights() {
|
|
||||||
static std::vector<Light> lights;
|
|
||||||
|
|
||||||
if (lights.empty()) {
|
|
||||||
lights = GameAPI::Lights::getLights("Pop'n Music");
|
|
||||||
|
|
||||||
GameAPI::Lights::sortLights(
|
|
||||||
&lights,
|
|
||||||
"Button 1",
|
"Button 1",
|
||||||
"Button 2",
|
"Button 2",
|
||||||
"Button 3",
|
"Button 3",
|
||||||
@@ -51,55 +21,135 @@ std::vector<Light> &games::popn::get_lights() {
|
|||||||
"Button 7",
|
"Button 7",
|
||||||
"Button 8",
|
"Button 8",
|
||||||
"Button 9",
|
"Button 9",
|
||||||
"Top LED 1",
|
"Red Pop-Kun",
|
||||||
"Top LED 2",
|
"Blue Pop-Kun"
|
||||||
"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"
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return buttons;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string games::popn::get_buttons_help() {
|
||||||
|
// keep to max 100 characters wide
|
||||||
|
return
|
||||||
|
"RED 2 4 6 8 BLUE\n"
|
||||||
|
" 1 3 5 7 9"
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Light> &games::popn::get_lights() {
|
||||||
|
static std::vector<Light> lights;
|
||||||
|
|
||||||
|
if (lights.empty()) {
|
||||||
|
lights = GameAPI::Lights::getLights("Pop'n Music");
|
||||||
|
|
||||||
|
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"},
|
||||||
|
{"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"},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return lights;
|
return lights;
|
||||||
}
|
}
|
||||||
|
|||||||
+62
-11
@@ -11,6 +11,7 @@ namespace games::popn {
|
|||||||
Service,
|
Service,
|
||||||
Test,
|
Test,
|
||||||
CoinMech,
|
CoinMech,
|
||||||
|
Headphone,
|
||||||
Button1,
|
Button1,
|
||||||
Button2,
|
Button2,
|
||||||
Button3,
|
Button3,
|
||||||
@@ -20,12 +21,14 @@ namespace games::popn {
|
|||||||
Button7,
|
Button7,
|
||||||
Button8,
|
Button8,
|
||||||
Button9,
|
Button9,
|
||||||
|
RedPop,
|
||||||
|
BluePop
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// all lights in correct order
|
// all lights in correct order
|
||||||
namespace Lights {
|
namespace Lights {
|
||||||
enum {
|
typedef enum {
|
||||||
Button1,
|
Button1,
|
||||||
Button2,
|
Button2,
|
||||||
Button3,
|
Button3,
|
||||||
@@ -35,6 +38,15 @@ namespace games::popn {
|
|||||||
Button7,
|
Button7,
|
||||||
Button8,
|
Button8,
|
||||||
Button9,
|
Button9,
|
||||||
|
HiLamp1,
|
||||||
|
HiLamp2,
|
||||||
|
HiLamp3,
|
||||||
|
HiLamp4,
|
||||||
|
HiLamp5,
|
||||||
|
LeftLamp1,
|
||||||
|
LeftLamp2,
|
||||||
|
RightLamp1,
|
||||||
|
RightLamp2,
|
||||||
TopLED1,
|
TopLED1,
|
||||||
TopLED2,
|
TopLED2,
|
||||||
TopLED3,
|
TopLED3,
|
||||||
@@ -70,19 +82,58 @@ namespace games::popn {
|
|||||||
TopLED_R,
|
TopLED_R,
|
||||||
TopLED_G,
|
TopLED_G,
|
||||||
TopLED_B,
|
TopLED_B,
|
||||||
HiLamp1,
|
|
||||||
HiLamp2,
|
|
||||||
HiLamp3,
|
|
||||||
HiLamp4,
|
|
||||||
HiLamp5,
|
|
||||||
LeftLamp1,
|
|
||||||
LeftLamp2,
|
|
||||||
RightLamp1,
|
|
||||||
RightLamp2,
|
|
||||||
WooferLED_R,
|
WooferLED_R,
|
||||||
WooferLED_G,
|
WooferLED_G,
|
||||||
WooferLED_B,
|
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,
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// getters
|
// getters
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
#include "popn.h"
|
#include "popn.h"
|
||||||
|
#include "bi3a_hook.h"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include "hooks/graphics/graphics.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/fileutils.h"
|
#include "util/fileutils.h"
|
||||||
@@ -9,14 +11,327 @@
|
|||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include "cfg/button.h"
|
#include "cfg/button.h"
|
||||||
#include "cfg/api.h"
|
#include "cfg/api.h"
|
||||||
|
#include "hooks/setupapihook.h"
|
||||||
#include "hooks/sleephook.h"
|
#include "hooks/sleephook.h"
|
||||||
#include "launcher/launcher.h"
|
#include "launcher/launcher.h"
|
||||||
#include "launcher/logger.h"
|
#include "launcher/logger.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
|
#include "util/sysutils.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
|
#include "util/deferlog.h"
|
||||||
|
#include "misc/nativetouchhook.h"
|
||||||
|
#include "misc/wintouchemu.h"
|
||||||
|
|
||||||
namespace games::popn {
|
namespace games::popn {
|
||||||
|
|
||||||
|
bool SHOW_PIKA_MONITOR_WARNING = false;
|
||||||
|
bool NATIVE_TOUCH = false;
|
||||||
|
|
||||||
|
#if SPICE64 && !SPICE_XP
|
||||||
|
|
||||||
|
const HMONITOR FAKE_MONITOR_HMONITOR = (HMONITOR)-1;
|
||||||
|
constexpr LONG FAKE_MONITOR_OFFSET_X_Y = -999999;
|
||||||
|
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-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] 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"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// [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"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static decltype(GetDisplayConfigBufferSizes) *GetDisplayConfigBufferSizes_orig = nullptr;
|
||||||
|
static decltype(QueryDisplayConfig) *QueryDisplayConfig_orig = nullptr;
|
||||||
|
static decltype(DisplayConfigGetDeviceInfo) *DisplayConfigGetDeviceInfo_orig = nullptr;
|
||||||
|
|
||||||
|
static UINT32 pNumPathArrayElements_original = 0;
|
||||||
|
static UINT32 pNumModeInfoArrayElements_original = 0;
|
||||||
|
|
||||||
|
static
|
||||||
|
LONG
|
||||||
|
WINAPI
|
||||||
|
GetDisplayConfigBufferSizes_hook(
|
||||||
|
UINT32 Flags,
|
||||||
|
UINT32 *pNumPathArrayElements,
|
||||||
|
UINT32 *pNumModeInfoArrayElements)
|
||||||
|
{
|
||||||
|
const auto ret = GetDisplayConfigBufferSizes_orig(Flags, pNumPathArrayElements, pNumModeInfoArrayElements);
|
||||||
|
if (FAKE_SUBSCREEN_ADAPTER) {
|
||||||
|
log_misc("popn", "GetDisplayConfigBufferSizes returning fake monitor paths and modes");
|
||||||
|
pNumPathArrayElements_original = *pNumPathArrayElements;
|
||||||
|
pNumModeInfoArrayElements_original = *pNumModeInfoArrayElements;
|
||||||
|
*pNumPathArrayElements += 1;
|
||||||
|
*pNumModeInfoArrayElements += 2;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
LONG
|
||||||
|
WINAPI
|
||||||
|
QueryDisplayConfig_hook(
|
||||||
|
UINT32 flags,
|
||||||
|
UINT32* numPathArrayElements,
|
||||||
|
DISPLAYCONFIG_PATH_INFO* pathArray,
|
||||||
|
UINT32* numModeInfoArrayElements,
|
||||||
|
DISPLAYCONFIG_MODE_INFO* modeInfoArray,
|
||||||
|
DISPLAYCONFIG_TOPOLOGY_ID* currentTopologyId)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!FAKE_SUBSCREEN_ADAPTER) {
|
||||||
|
return QueryDisplayConfig_orig(
|
||||||
|
flags,
|
||||||
|
numPathArrayElements, pathArray,
|
||||||
|
numModeInfoArrayElements, modeInfoArray,
|
||||||
|
currentTopologyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// call original to fill in real monitor info
|
||||||
|
UINT32 num_paths = pNumPathArrayElements_original;
|
||||||
|
UINT32 num_modes = pNumModeInfoArrayElements_original;
|
||||||
|
const auto ret = QueryDisplayConfig_orig(
|
||||||
|
flags,
|
||||||
|
&num_paths, pathArray,
|
||||||
|
&num_modes, modeInfoArray,
|
||||||
|
currentTopologyId);
|
||||||
|
|
||||||
|
if (ret != ERROR_SUCCESS) {
|
||||||
|
log_warning("popn", "QueryDisplayConfig failed with error code {}", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_misc("popn", "QueryDisplayConfig returning fake monitor paths and modes");
|
||||||
|
|
||||||
|
// insert a fake path
|
||||||
|
DISPLAYCONFIG_PATH_INFO *path = &pathArray[pNumPathArrayElements_original];
|
||||||
|
*path = {};
|
||||||
|
path->flags = DISPLAYCONFIG_PATH_ACTIVE;
|
||||||
|
|
||||||
|
path->sourceInfo.adapterId.HighPart = -1;
|
||||||
|
path->sourceInfo.adapterId.LowPart = -1;
|
||||||
|
path->sourceInfo.id = -1;
|
||||||
|
path->sourceInfo.modeInfoIdx = pNumModeInfoArrayElements_original;
|
||||||
|
path->sourceInfo.statusFlags = DISPLAYCONFIG_SOURCE_IN_USE;
|
||||||
|
|
||||||
|
path->targetInfo.adapterId.HighPart = -1;
|
||||||
|
path->targetInfo.adapterId.LowPart = -1;
|
||||||
|
path->targetInfo.id = -2;
|
||||||
|
path->targetInfo.modeInfoIdx = pNumModeInfoArrayElements_original + 1;
|
||||||
|
path->targetInfo.outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI;
|
||||||
|
path->targetInfo.rotation = DISPLAYCONFIG_ROTATION_IDENTITY;
|
||||||
|
path->targetInfo.scaling = DISPLAYCONFIG_SCALING_IDENTITY;
|
||||||
|
path->targetInfo.refreshRate.Numerator = 60000;
|
||||||
|
path->targetInfo.refreshRate.Denominator = 1000;
|
||||||
|
path->targetInfo.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE;
|
||||||
|
path->targetInfo.targetAvailable = true;
|
||||||
|
path->targetInfo.statusFlags = DISPLAYCONFIG_TARGET_IN_USE;
|
||||||
|
|
||||||
|
// insert fake mode source
|
||||||
|
DISPLAYCONFIG_MODE_INFO *mode_source = &modeInfoArray[pNumModeInfoArrayElements_original];
|
||||||
|
*mode_source = {};
|
||||||
|
mode_source->infoType = DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE;
|
||||||
|
mode_source->id = -1;
|
||||||
|
mode_source->adapterId.HighPart = -1;
|
||||||
|
mode_source->adapterId.LowPart = -1;
|
||||||
|
mode_source->sourceMode.width = FAKE_MONITOR_WIDTH;
|
||||||
|
mode_source->sourceMode.height = FAKE_MONITOR_HEIGHT;
|
||||||
|
mode_source->sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP;
|
||||||
|
mode_source->sourceMode.position.x = FAKE_MONITOR_OFFSET_X_Y;
|
||||||
|
mode_source->sourceMode.position.y = FAKE_MONITOR_OFFSET_X_Y;
|
||||||
|
|
||||||
|
// insert fake mode target
|
||||||
|
DISPLAYCONFIG_MODE_INFO *mode_target = &modeInfoArray[pNumModeInfoArrayElements_original+1];
|
||||||
|
*mode_target = {};
|
||||||
|
mode_target->infoType = DISPLAYCONFIG_MODE_INFO_TYPE_TARGET;
|
||||||
|
mode_target->id = -2;
|
||||||
|
mode_target->adapterId.HighPart = -1;
|
||||||
|
mode_target->adapterId.LowPart = -1;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
LONG
|
||||||
|
WINAPI
|
||||||
|
DisplayConfigGetDeviceInfo_hook(DISPLAYCONFIG_DEVICE_INFO_HEADER* requestPacket)
|
||||||
|
{
|
||||||
|
if (requestPacket == nullptr) {
|
||||||
|
return DisplayConfigGetDeviceInfo_orig(requestPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fake monitor
|
||||||
|
if (FAKE_SUBSCREEN_ADAPTER &&
|
||||||
|
(requestPacket->id == static_cast<UINT32>(-1) ||
|
||||||
|
requestPacket->id == static_cast<UINT32>(-2)) &&
|
||||||
|
requestPacket->adapterId.HighPart == static_cast<LONG>(-1) &&
|
||||||
|
requestPacket->adapterId.LowPart == static_cast<DWORD>(-1)) {
|
||||||
|
log_misc(
|
||||||
|
"popn",
|
||||||
|
"DisplayConfigGetDeviceInfo hook hit for fake monitor, type={}, size={}, id={}, luid={}/{}",
|
||||||
|
static_cast<int>(requestPacket->type),
|
||||||
|
requestPacket->size,
|
||||||
|
requestPacket->id,
|
||||||
|
requestPacket->adapterId.HighPart,
|
||||||
|
requestPacket->adapterId.LowPart);
|
||||||
|
|
||||||
|
if (requestPacket->type == DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME) {
|
||||||
|
const auto target = reinterpret_cast<DISPLAYCONFIG_TARGET_DEVICE_NAME*>(requestPacket);
|
||||||
|
target->flags.value = 0;
|
||||||
|
target->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI;
|
||||||
|
target->connectorInstance = 0;
|
||||||
|
wcscpy(target->monitorFriendlyDeviceName, L"Spice Fake Monitor");
|
||||||
|
wcscpy(target->monitorDevicePath, L"\\\\?\\SpiceFakeMonitor");
|
||||||
|
} else if (requestPacket->type == DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME) {
|
||||||
|
const auto source = reinterpret_cast<DISPLAYCONFIG_SOURCE_DEVICE_NAME*>(requestPacket);
|
||||||
|
// value must match WrappedIDirect3D9::GetAdapterIdentifier
|
||||||
|
wcscpy(source->viewGdiDeviceName, L"\\\\.\\DISPLAY_SPICE_FAKE");
|
||||||
|
} else {
|
||||||
|
log_fatal(
|
||||||
|
"popn",
|
||||||
|
"unexpected device info type {} for fake monitor",
|
||||||
|
static_cast<int>(requestPacket->type));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto ret = DisplayConfigGetDeviceInfo_orig(requestPacket);
|
||||||
|
log_misc(
|
||||||
|
"popn",
|
||||||
|
"DisplayConfigGetDeviceInfo returned {}, type={}, size={}, id={}, luid={}/{}",
|
||||||
|
ret,
|
||||||
|
static_cast<int>(requestPacket->type),
|
||||||
|
requestPacket->size,
|
||||||
|
requestPacket->id,
|
||||||
|
requestPacket->adapterId.HighPart,
|
||||||
|
requestPacket->adapterId.LowPart);
|
||||||
|
if (ret == ERROR_SUCCESS) {
|
||||||
|
if (requestPacket->type == DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME) {
|
||||||
|
const auto sourceName = reinterpret_cast<DISPLAYCONFIG_SOURCE_DEVICE_NAME*>(requestPacket);
|
||||||
|
log_misc(
|
||||||
|
"popn",
|
||||||
|
"... name={}",
|
||||||
|
ws2s(sourceName->viewGdiDeviceName));
|
||||||
|
} else if (requestPacket->type == DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME) {
|
||||||
|
const auto targetName = reinterpret_cast<DISPLAYCONFIG_TARGET_DEVICE_NAME*>(requestPacket);
|
||||||
|
log_misc(
|
||||||
|
"popn",
|
||||||
|
"... flags={}, outputTechnology: {}, connectorInstance={}, friendlyname={} path={}",
|
||||||
|
targetName->flags.value,
|
||||||
|
static_cast<int>(targetName->outputTechnology),
|
||||||
|
targetName->connectorInstance,
|
||||||
|
ws2s(targetName->monitorFriendlyDeviceName),
|
||||||
|
ws2s(targetName->monitorDevicePath));
|
||||||
|
|
||||||
|
// need to fix up some values for the primary monitor...
|
||||||
|
const auto monitors = sysutils::enumerate_monitors();
|
||||||
|
for (const auto& monitor : monitors) {
|
||||||
|
if (monitor.id == targetName->header.id &&
|
||||||
|
monitor.adapter_id_HighPart == targetName->header.adapterId.HighPart &&
|
||||||
|
monitor.adapter_id_LowPart == targetName->header.adapterId.LowPart) {
|
||||||
|
|
||||||
|
if (monitor.is_primary) {
|
||||||
|
targetName->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL;
|
||||||
|
targetName->connectorInstance = 2;
|
||||||
|
log_info(
|
||||||
|
"popn",
|
||||||
|
"... overriding primary monitor ({}) to pretend to be DP port #2",
|
||||||
|
monitor.display_name);
|
||||||
|
} else {
|
||||||
|
// TODO: is this what the game expects for subscreen?
|
||||||
|
// TODO: what if there are 3+ monitors?
|
||||||
|
targetName->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI;
|
||||||
|
targetName->connectorInstance = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !SPICE64
|
||||||
|
|
||||||
static int __cdecl usbCheckAlive() {
|
static int __cdecl usbCheckAlive() {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -227,6 +542,8 @@ namespace games::popn {
|
|||||||
return MMSYSERR_NOERROR;
|
return MMSYSERR_NOERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
static bool log_hook(void *user, const std::string &data, logger::Style style, std::string &out) {
|
static bool log_hook(void *user, const std::string &data, logger::Style style, std::string &out) {
|
||||||
|
|
||||||
// get rid of the log spam
|
// get rid of the log spam
|
||||||
@@ -249,6 +566,8 @@ namespace games::popn {
|
|||||||
void POPNGame::pre_attach() {
|
void POPNGame::pre_attach() {
|
||||||
Game::pre_attach();
|
Game::pre_attach();
|
||||||
|
|
||||||
|
#if !SPICE64
|
||||||
|
|
||||||
// only needed for older versions (19-21, inclusive) but create them anyway since 21 and above are all M39
|
// only needed for older versions (19-21, inclusive) but create them anyway since 21 and above are all M39
|
||||||
fileutils::dir_create_recursive_log("popn", "dev\\raw\\bookkeeping");
|
fileutils::dir_create_recursive_log("popn", "dev\\raw\\bookkeeping");
|
||||||
fileutils::dir_create_recursive_log("popn", "dev\\raw\\ranking");
|
fileutils::dir_create_recursive_log("popn", "dev\\raw\\ranking");
|
||||||
@@ -257,11 +576,15 @@ namespace games::popn {
|
|||||||
// load without resolving references
|
// load without resolving references
|
||||||
// makes game not trigger DLLMain which results in an error due to missing EZUSB device
|
// makes game not trigger DLLMain which results in an error due to missing EZUSB device
|
||||||
LoadLibraryExW((MODULE_PATH / "ezusb.dll").c_str(), nullptr, DONT_RESOLVE_DLL_REFERENCES);
|
LoadLibraryExW((MODULE_PATH / "ezusb.dll").c_str(), nullptr, DONT_RESOLVE_DLL_REFERENCES);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void POPNGame::attach() {
|
void POPNGame::attach() {
|
||||||
Game::attach();
|
Game::attach();
|
||||||
|
|
||||||
|
#if !SPICE64
|
||||||
/*
|
/*
|
||||||
* Fast Boot (TM) Patch
|
* Fast Boot (TM) Patch
|
||||||
* Game tries to create some directories and if it fails it will sleep for 1 second
|
* Game tries to create some directories and if it fails it will sleep for 1 second
|
||||||
@@ -320,5 +643,91 @@ namespace games::popn {
|
|||||||
detour::inline_hook((void *) usbWdtStartDone,
|
detour::inline_hook((void *) usbWdtStartDone,
|
||||||
libutils::try_proc(ezusb, "?usbWdtStartDone@@YAHXZ"));
|
libutils::try_proc(ezusb, "?usbWdtStartDone@@YAHXZ"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SPICE64 && !SPICE_XP
|
||||||
|
if (!GRAPHICS_WINDOWED && D3D9_ADAPTER.has_value()) {
|
||||||
|
SHOW_PIKA_MONITOR_WARNING = true;
|
||||||
|
log_warning(
|
||||||
|
"popn",
|
||||||
|
"\n\n"
|
||||||
|
"!!! using -dxmainadapter option is NOT recommended due to known !!!\n"
|
||||||
|
"!!! compatibility issues with the game !!!\n"
|
||||||
|
"!!! !!!\n"
|
||||||
|
"!!! * game may launch in wrong resolution or refresh rate !!!\n"
|
||||||
|
"!!! * touch / mouse input may stop working in subscreen / overlay !!!\n"
|
||||||
|
"!!! !!!\n"
|
||||||
|
"!!! recommendation is to use the Change Main Monitor (-mainmonitor) !!!\n"
|
||||||
|
"!!! option instead of -dxmainadapter !!!\n\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
deferredlogs::defer_error_messages({
|
||||||
|
"-dxmainadapter option is NOT recommended due to known compatibility ",
|
||||||
|
" issues with the game!",
|
||||||
|
" * game may launch in wrong resolution or refresh rate",
|
||||||
|
" * touch / mouse input may stop working in subscreen / overlay",
|
||||||
|
" use Change Main Monitor (-mainmonitor) option instead"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// monitor hook
|
||||||
|
GetDisplayConfigBufferSizes_orig =
|
||||||
|
detour::iat_try("GetDisplayConfigBufferSizes",
|
||||||
|
GetDisplayConfigBufferSizes_hook, avs::game::DLL_INSTANCE);
|
||||||
|
QueryDisplayConfig_orig =
|
||||||
|
detour::iat_try("QueryDisplayConfig",
|
||||||
|
QueryDisplayConfig_hook, avs::game::DLL_INSTANCE);
|
||||||
|
DisplayConfigGetDeviceInfo_orig =
|
||||||
|
detour::iat_try("DisplayConfigGetDeviceInfo",
|
||||||
|
DisplayConfigGetDeviceInfo_hook, avs::game::DLL_INSTANCE);
|
||||||
|
|
||||||
|
// GUID_DEVCLASS_USB = {86E0D1E0-11D0-89B0-00A0C9054129}
|
||||||
|
SETUPAPI_SETTINGS settings{};
|
||||||
|
settings.class_guid[0] = 0x86E0D1E0;
|
||||||
|
settings.class_guid[1] = 0x11D08089;
|
||||||
|
settings.class_guid[2] = 0x0008E49C;
|
||||||
|
settings.class_guid[3] = 0x731F303E;
|
||||||
|
|
||||||
|
// BIO3 - 8058 (maybe U region cabs with more lights?)
|
||||||
|
// BIO3 - 8054 (probably J region?)
|
||||||
|
// BIO2 - 8050 (need bi2x_hook, everything will work except buttons 1-9)
|
||||||
|
|
||||||
|
const char property[] = "1CCF(8058)_000";
|
||||||
|
const char property_hardwareid[] = "USB\\VID_1CCF&PID_8058&MI_00\\000";
|
||||||
|
memcpy(settings.property_devicedesc, property, sizeof(property));
|
||||||
|
memcpy(settings.property_hardwareid, property_hardwareid, sizeof(property_hardwareid));
|
||||||
|
setupapihook_init(avs::game::DLL_INSTANCE);
|
||||||
|
setupapihook_add(settings);
|
||||||
|
|
||||||
|
bi3a_hook_init();
|
||||||
|
|
||||||
|
// note: if for whatever reason you want to use BIO2, the game needs to be patched
|
||||||
|
// check popn.dll and look for static table that looks like this:
|
||||||
|
// 00000001 00000003 00000001 (button 1)
|
||||||
|
// 00000002 00000004 00000001 (button 2)
|
||||||
|
// 00000004 00000005 00000001 (button 3)
|
||||||
|
// 00000008 00000006 00000001 (button 4)
|
||||||
|
// 00000010 00000007 00000001 (button 5)
|
||||||
|
// 00000020 00000008 00000001 (button 6)
|
||||||
|
// 00000040 00000009 00000001 (button 7)
|
||||||
|
// 00000080 0000000A 00000001 (button 8)
|
||||||
|
// 00000100 0000000B 00000001 (button 9)
|
||||||
|
// set third column to 0 and it will work with BIO2
|
||||||
|
|
||||||
|
if (!GRAPHICS_WINDOWED) {
|
||||||
|
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();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,29 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "avs/game.h"
|
||||||
#include "games/game.h"
|
#include "games/game.h"
|
||||||
|
#include "util/tapeled.h"
|
||||||
|
|
||||||
namespace games::popn {
|
namespace games::popn {
|
||||||
|
|
||||||
|
static inline bool is_pikapika_model() {
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
return (avs::game::is_model("M39") && avs::game::SPEC[0] == 'D');
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
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 {
|
class POPNGame : public games::Game {
|
||||||
public:
|
public:
|
||||||
POPNGame();
|
POPNGame();
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include "util/socd_cleaner.h"
|
#include "util/socd_cleaner.h"
|
||||||
#include "util/time.h"
|
#include "util/time.h"
|
||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
|
#include "util/sysutils.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "misc/nativetouchhook.h"
|
#include "misc/nativetouchhook.h"
|
||||||
#include "misc/wintouchemu.h"
|
#include "misc/wintouchemu.h"
|
||||||
@@ -354,7 +355,7 @@ namespace games::sdvx {
|
|||||||
if (!GRAPHICS_WINDOWED && D3D9_ADAPTER.has_value() && is_valkyrie_model()) {
|
if (!GRAPHICS_WINDOWED && D3D9_ADAPTER.has_value() && is_valkyrie_model()) {
|
||||||
SHOW_VM_MONITOR_WARNING = true;
|
SHOW_VM_MONITOR_WARNING = true;
|
||||||
log_warning(
|
log_warning(
|
||||||
"iidx",
|
"sdvx",
|
||||||
"\n\n"
|
"\n\n"
|
||||||
"!!! using -dxmainadapter option with Valkyrie mode is NOT !!!\n"
|
"!!! using -dxmainadapter option with Valkyrie mode is NOT !!!\n"
|
||||||
"!!! recommended due to known compatibility issues with the game !!!\n"
|
"!!! recommended due to known compatibility issues with the game !!!\n"
|
||||||
@@ -466,6 +467,7 @@ namespace games::sdvx {
|
|||||||
ENABLE_COM_PORT_SCAN_HOOK = true;
|
ENABLE_COM_PORT_SCAN_HOOK = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// ASIO device hook
|
// ASIO device hook
|
||||||
@@ -501,6 +503,9 @@ namespace games::sdvx {
|
|||||||
// remove log spam
|
// remove log spam
|
||||||
logger::hook_add(sdvx64_spam_remover, nullptr);
|
logger::hook_add(sdvx64_spam_remover, nullptr);
|
||||||
|
|
||||||
|
if (is_valkyrie_model()) {
|
||||||
|
sysutils::hook_EnumDisplayDevicesA();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,14 +66,16 @@ static HRESULT STDAPICALLTYPE CoCreateInstance_hook(
|
|||||||
if (FAILED(ret)) {
|
if (FAILED(ret)) {
|
||||||
if (IsEqualCLSID(rclsid, CLSID_MMDeviceEnumerator)) {
|
if (IsEqualCLSID(rclsid, CLSID_MMDeviceEnumerator)) {
|
||||||
log_warning("audio", "CoCreateInstance failed for CLSID_MMDeviceEnumerator, hr={}", FMT_HRESULT(ret));
|
log_warning("audio", "CoCreateInstance failed for CLSID_MMDeviceEnumerator, hr={}", FMT_HRESULT(ret));
|
||||||
}
|
} else if (IsEqualCLSID(rclsid, CLSID_DirectSoundI3DL2ReverbDMO)) {
|
||||||
|
// deal with reverb DMO missing from dsdmo.dll
|
||||||
// Windows 11 KB5052093 issue - reverb DMO went missing from dsdmo.dll (fails with 0x80040154)
|
// it usually fails with 0x80040154 (REGDB_E_CLASSNOTREG), but we have also seen 0x8007007e (ERROR_MOD_NOT_FOUND)
|
||||||
if (IsEqualCLSID(rclsid, CLSID_DirectSoundI3DL2ReverbDMO) && ret == (HRESULT)0x80040154) {
|
static std::once_flag printed;
|
||||||
|
std::call_once(printed, [ret]() {
|
||||||
log_warning(
|
log_warning(
|
||||||
"audio",
|
"audio",
|
||||||
"CoCreateInstance for CLSID_DirectSoundI3DL2ReverbDMO failed with 0x80040154, swap with CLSID_DirectSoundWavesReverbDMO "
|
"CoCreateInstance for CLSID_DirectSoundI3DL2ReverbDMO failed with {}, swap with CLSID_DirectSoundWavesReverbDMO...",
|
||||||
"(workaround for Windows 11 KB5052093 issue); REVERB EX replaced with REVERB");
|
FMT_HRESULT(ret));
|
||||||
|
});
|
||||||
ret = CoCreateInstance_orig(CLSID_DirectSoundWavesReverbDMO, pUnkOuter, dwClsContext, riid, ppv);
|
ret = CoCreateInstance_orig(CLSID_DirectSoundWavesReverbDMO, pUnkOuter, dwClsContext, riid, ppv);
|
||||||
if (FAILED(ret)) {
|
if (FAILED(ret)) {
|
||||||
log_warning("audio", "CoCreateInstance(CLSID_DirectSoundWavesReverbDMO...) failed, hr={}", FMT_HRESULT(ret));
|
log_warning("audio", "CoCreateInstance(CLSID_DirectSoundWavesReverbDMO...) failed, hr={}", FMT_HRESULT(ret));
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include "cfg/screen_resize.h"
|
#include "cfg/screen_resize.h"
|
||||||
#include "games/gitadora/gitadora.h"
|
#include "games/gitadora/gitadora.h"
|
||||||
#include "games/iidx/iidx.h"
|
#include "games/iidx/iidx.h"
|
||||||
|
#include "games/popn/popn.h"
|
||||||
#include "games/sdvx/sdvx.h"
|
#include "games/sdvx/sdvx.h"
|
||||||
#include "games/mfc/mfc.h"
|
#include "games/mfc/mfc.h"
|
||||||
#include "games/io.h"
|
#include "games/io.h"
|
||||||
@@ -462,7 +463,20 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::RegisterSoftwareDevice(void *pIniti
|
|||||||
}
|
}
|
||||||
|
|
||||||
UINT STDMETHODCALLTYPE WrappedIDirect3D9::GetAdapterCount() {
|
UINT STDMETHODCALLTYPE WrappedIDirect3D9::GetAdapterCount() {
|
||||||
return pReal->GetAdapterCount();
|
UINT result = pReal->GetAdapterCount();
|
||||||
|
|
||||||
|
if (!FAKE_SUBSCREEN_ADAPTER && games::popn::is_pikapika_model() && result == 1) {
|
||||||
|
FAKE_SUBSCREEN_ADAPTER = true;
|
||||||
|
log_info(
|
||||||
|
"graphics::d3d9",
|
||||||
|
"GetAdapterCount returned 1, popn needs 2 adapters - enabliing fake submonitor mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FAKE_SUBSCREEN_ADAPTER) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::GetAdapterIdentifier(
|
HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::GetAdapterIdentifier(
|
||||||
@@ -470,10 +484,26 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::GetAdapterIdentifier(
|
|||||||
DWORD Flags,
|
DWORD Flags,
|
||||||
D3DADAPTER_IDENTIFIER9 *pIdentifier)
|
D3DADAPTER_IDENTIFIER9 *pIdentifier)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (FAKE_SUBSCREEN_ADAPTER && Adapter == 1 && pIdentifier) {
|
||||||
|
*pIdentifier = {};
|
||||||
|
strcpy(pIdentifier->DeviceName, "\\\\.\\DISPLAY_SPICE_FAKE");
|
||||||
|
log_misc(
|
||||||
|
"graphics::d3d9",
|
||||||
|
"GetAdapterIdentifier called for fake subscreen adapter 1: {}",
|
||||||
|
pIdentifier->DeviceName);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
CHECK_RESULT(pReal->GetAdapterIdentifier(Adapter, Flags, pIdentifier));
|
CHECK_RESULT(pReal->GetAdapterIdentifier(Adapter, Flags, pIdentifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
UINT STDMETHODCALLTYPE WrappedIDirect3D9::GetAdapterModeCount(UINT Adapter, D3DFORMAT Format) {
|
UINT STDMETHODCALLTYPE WrappedIDirect3D9::GetAdapterModeCount(UINT Adapter, D3DFORMAT Format) {
|
||||||
|
if (FAKE_SUBSCREEN_ADAPTER && Adapter == 1) {
|
||||||
|
log_misc("graphics::d3d9", "GetAdapterModeCount called for fake subscreen adapter");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
return pReal->GetAdapterModeCount(Adapter, Format);
|
return pReal->GetAdapterModeCount(Adapter, Format);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -483,6 +513,19 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::EnumAdapterModes(
|
|||||||
UINT Mode,
|
UINT Mode,
|
||||||
D3DDISPLAYMODE *pMode)
|
D3DDISPLAYMODE *pMode)
|
||||||
{
|
{
|
||||||
|
if (FAKE_SUBSCREEN_ADAPTER && Adapter == 1) {
|
||||||
|
log_misc("graphics::d3d9", "EnumAdapterModes called for fake subscreen adapter");
|
||||||
|
if (Mode == 0 && pMode) {
|
||||||
|
pMode->Width = 1280;
|
||||||
|
pMode->Height = 800;
|
||||||
|
pMode->RefreshRate = 60;
|
||||||
|
pMode->Format = D3DFMT_X8R8G8B8;
|
||||||
|
return S_OK;
|
||||||
|
} else {
|
||||||
|
return D3DERR_INVALIDCALL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HRESULT ret = pReal->EnumAdapterModes(Adapter, Format, Mode, pMode);
|
HRESULT ret = pReal->EnumAdapterModes(Adapter, Format, Mode, pMode);
|
||||||
|
|
||||||
if (SUCCEEDED(ret) && pMode) {
|
if (SUCCEEDED(ret) && pMode) {
|
||||||
@@ -579,6 +622,11 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CheckDeviceFormat(
|
|||||||
D3DRESOURCETYPE RType,
|
D3DRESOURCETYPE RType,
|
||||||
D3DFORMAT CheckFormat)
|
D3DFORMAT CheckFormat)
|
||||||
{
|
{
|
||||||
|
if (FAKE_SUBSCREEN_ADAPTER && Adapter == 1) {
|
||||||
|
log_misc("graphics::d3d9", "CheckDeviceFormat called for fake subscreen adapter");
|
||||||
|
return D3D_OK;
|
||||||
|
}
|
||||||
|
|
||||||
CHECK_RESULT(pReal->CheckDeviceFormat(Adapter, DeviceType, AdapterFormat, Usage, RType, CheckFormat));
|
CHECK_RESULT(pReal->CheckDeviceFormat(Adapter, DeviceType, AdapterFormat, Usage, RType, CheckFormat));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -660,7 +708,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::GetDeviceCaps(UINT Adapter, D3DDEVT
|
|||||||
}
|
}
|
||||||
// in windowed mode, LDJ will always launch two windows, no special handling needed here
|
// in windowed mode, LDJ will always launch two windows, no special handling needed here
|
||||||
} else if (avs::game::is_model("KFC")) {
|
} else if (avs::game::is_model("KFC")) {
|
||||||
if (GRAPHICS_WINDOWED & GRAPHICS_PREVENT_SECONDARY_WINDOW) {
|
if (GRAPHICS_WINDOWED && GRAPHICS_PREVENT_SECONDARY_WINDOW) {
|
||||||
// user wants windowed mode but does not want subscreen at all
|
// user wants windowed mode but does not want subscreen at all
|
||||||
pCaps->NumberOfAdaptersInGroup = 1;
|
pCaps->NumberOfAdaptersInGroup = 1;
|
||||||
} else {
|
} else {
|
||||||
@@ -675,6 +723,8 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::GetDeviceCaps(UINT Adapter, D3DDEVT
|
|||||||
} else if (GRAPHICS_FORCE_SINGLE_ADAPTER) {
|
} else if (GRAPHICS_FORCE_SINGLE_ADAPTER) {
|
||||||
pCaps->NumberOfAdaptersInGroup = 1;
|
pCaps->NumberOfAdaptersInGroup = 1;
|
||||||
}
|
}
|
||||||
|
} else if (games::popn::is_pikapika_model()) {
|
||||||
|
pCaps->NumberOfAdaptersInGroup = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -1021,7 +1071,8 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
|||||||
//
|
//
|
||||||
// note from MSDN: `pFullscreenDisplayMode` must be NULL for windowed mode.
|
// note from MSDN: `pFullscreenDisplayMode` must be NULL for windowed mode.
|
||||||
if (GRAPHICS_WINDOWED) {
|
if (GRAPHICS_WINDOWED) {
|
||||||
if (avs::game::is_model({"LDJ", "KFC"}) && (BehaviorFlags & D3DCREATE_ADAPTERGROUP_DEVICE)) {
|
if (avs::game::is_model({"LDJ", "KFC", "M39"}) &&
|
||||||
|
(BehaviorFlags & D3DCREATE_ADAPTERGROUP_DEVICE)) {
|
||||||
log_misc("graphics::d3d9", "disabling adapter group device in windowed mode");
|
log_misc("graphics::d3d9", "disabling adapter group device in windowed mode");
|
||||||
|
|
||||||
D3D9_BEHAVIOR_DISABLE |= D3DCREATE_ADAPTERGROUP_DEVICE;
|
D3D9_BEHAVIOR_DISABLE |= D3DCREATE_ADAPTERGROUP_DEVICE;
|
||||||
@@ -1093,7 +1144,8 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
|||||||
*ppReturnedDeviceInterface = new WrappedIDirect3DDevice9(hFocusWindow, *ppReturnedDeviceInterface);
|
*ppReturnedDeviceInterface = new WrappedIDirect3DDevice9(hFocusWindow, *ppReturnedDeviceInterface);
|
||||||
|
|
||||||
// initialize sub screen if IIDX/SDVX requested a multi-head context
|
// initialize sub screen if IIDX/SDVX requested a multi-head context
|
||||||
if (avs::game::is_model({"LDJ", "KFC"}) && (orig_behavior_flags & D3DCREATE_ADAPTERGROUP_DEVICE)) {
|
if (avs::game::is_model({"LDJ", "KFC", "M39"}) &&
|
||||||
|
(orig_behavior_flags & D3DCREATE_ADAPTERGROUP_DEVICE)) {
|
||||||
graphics_d3d9_ldj_init_sub_screen(*ppReturnedDeviceInterface, &pPresentationParameters[1]);
|
graphics_d3d9_ldj_init_sub_screen(*ppReturnedDeviceInterface, &pPresentationParameters[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1132,8 +1184,6 @@ static void graphics_d3d9_ldj_init_sub_screen(IDirect3DDevice9Ex *device, D3DPRE
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if (GRAPHICS_WINDOWED) {
|
if (GRAPHICS_WINDOWED) {
|
||||||
log_info("graphics::d3d9", "creating additional swap chain");
|
|
||||||
|
|
||||||
present_params->Windowed = true;
|
present_params->Windowed = true;
|
||||||
present_params->FullScreen_RefreshRateInHz = 0;
|
present_params->FullScreen_RefreshRateInHz = 0;
|
||||||
|
|
||||||
@@ -1142,18 +1192,23 @@ static void graphics_d3d9_ldj_init_sub_screen(IDirect3DDevice9Ex *device, D3DPRE
|
|||||||
hr = device->CreateAdditionalSwapChain(present_params, &SUB_SWAP_CHAIN);
|
hr = device->CreateAdditionalSwapChain(present_params, &SUB_SWAP_CHAIN);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
log_warning("graphics::d3d9", "failed to create additional swap chain, hr={}", FMT_HRESULT(hr));
|
log_warning("graphics::d3d9", "failed to create additional swap chain, hr={}", FMT_HRESULT(hr));
|
||||||
|
} else {
|
||||||
|
log_info("graphics::d3d9", "created additional swap chain for windowed mode");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
hr = device->GetSwapChain(1, &SUB_SWAP_CHAIN);
|
hr = device->GetSwapChain(1, &SUB_SWAP_CHAIN);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
log_warning("graphics::d3d9", "failed to acquire fullscreen sub swap chain, hr={}", FMT_HRESULT(hr));
|
log_warning("graphics::d3d9", "failed to acquire fullscreen sub swap chain, hr={}", FMT_HRESULT(hr));
|
||||||
} else {
|
} else {
|
||||||
|
log_info("graphics::d3d9", "acquired fullscreen sub swap chain");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = device->CreateAdditionalSwapChain(present_params, &SUB_SWAP_CHAIN);
|
hr = device->CreateAdditionalSwapChain(present_params, &SUB_SWAP_CHAIN);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
log_warning("graphics::d3d9", "failed to get additional swap chain, hr={}", FMT_HRESULT(hr));
|
log_warning("graphics::d3d9", "failed to get additional swap chain, hr={}", FMT_HRESULT(hr));
|
||||||
|
} else {
|
||||||
|
log_info("graphics::d3d9", "created additional swap chain for fullscreen mode");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1196,7 +1251,10 @@ static void graphics_d3d9_ldj_on_present(IDirect3DDevice9 *wrapped_device) {
|
|||||||
if (SUB_SWAP_CHAIN != nullptr) {
|
if (SUB_SWAP_CHAIN != nullptr) {
|
||||||
wintouchemu::update();
|
wintouchemu::update();
|
||||||
|
|
||||||
if (GRAPHICS_WINDOWED || SUBSCREEN_FORCE_REDRAW) {
|
// newer versions of exceed gear needs SUBSCREEN_FORCE_REDRAW
|
||||||
|
// (when enabled on older versions of EG, you end up with graphical glitches on the subscreen
|
||||||
|
// early versions of popn HC needs this as well, otherwise the subscreen doesn't update at all
|
||||||
|
if (GRAPHICS_WINDOWED || SUBSCREEN_FORCE_REDRAW || games::popn::is_pikapika_model()) {
|
||||||
SUB_SWAP_CHAIN->Present(nullptr, nullptr, nullptr, nullptr, 0);
|
SUB_SWAP_CHAIN->Present(nullptr, nullptr, nullptr, nullptr, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1396,7 +1454,8 @@ void graphics_d3d9_on_present(
|
|||||||
const bool is_tdj = avs::game::is_model("LDJ") && games::iidx::TDJ_MODE;
|
const bool is_tdj = avs::game::is_model("LDJ") && games::iidx::TDJ_MODE;
|
||||||
const bool is_gfdm_arena =
|
const bool is_gfdm_arena =
|
||||||
games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW;
|
games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW;
|
||||||
if (is_vm || is_tdj || is_gfdm_arena) {
|
const bool is_pika = games::popn::is_pikapika_model();
|
||||||
|
if (is_vm || is_tdj || is_gfdm_arena || is_pika) {
|
||||||
graphics_d3d9_ldj_on_present(wrapped_device);
|
graphics_d3d9_ldj_on_present(wrapped_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateAdditionalSwapChain(
|
|||||||
|
|
||||||
HRESULT hr = pReal->CreateAdditionalSwapChain(pPresentationParameters, ppSwapChain);
|
HRESULT hr = pReal->CreateAdditionalSwapChain(pPresentationParameters, ppSwapChain);
|
||||||
bool create_swap_chain = false;
|
bool create_swap_chain = false;
|
||||||
if (avs::game::is_model({"LDJ", "KFC"})) {
|
if (avs::game::is_model({"LDJ", "KFC", "M39"})) {
|
||||||
create_swap_chain = true;
|
create_swap_chain = true;
|
||||||
} else if (games::gitadora::is_arena_model() &&
|
} else if (games::gitadora::is_arena_model() &&
|
||||||
games::gitadora::ARENA_SINGLE_WINDOW &&
|
games::gitadora::ARENA_SINGLE_WINDOW &&
|
||||||
@@ -305,7 +305,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetSwapChain(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool swap = false;
|
bool swap = false;
|
||||||
if (iSwapChain == 1 && avs::game::is_model({"LDJ", "KFC"})) {
|
if (iSwapChain == 1 && avs::game::is_model({"LDJ", "KFC", "M39"})) {
|
||||||
swap = true;
|
swap = true;
|
||||||
}
|
}
|
||||||
if (games::gitadora::is_arena_model() &&
|
if (games::gitadora::is_arena_model() &&
|
||||||
@@ -344,7 +344,7 @@ UINT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetNumberOfSwapChains() {
|
|||||||
|
|
||||||
UINT n = pReal->GetNumberOfSwapChains();
|
UINT n = pReal->GetNumberOfSwapChains();
|
||||||
|
|
||||||
if (sub_swapchain && avs::game::is_model({"LDJ", "KFC"})) {
|
if (sub_swapchain && avs::game::is_model({"LDJ", "KFC", "M39"})) {
|
||||||
n += 1;
|
n += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// GetDisplayConfigBufferSizes etc is Vista+
|
||||||
|
#define _WIN32_WINNT 0x0601
|
||||||
|
|
||||||
#include <initguid.h>
|
#include <initguid.h>
|
||||||
|
|
||||||
#include "graphics.h"
|
#include "graphics.h"
|
||||||
@@ -13,6 +16,7 @@
|
|||||||
#include "games/ddr/ddr.h"
|
#include "games/ddr/ddr.h"
|
||||||
#include "games/gitadora/gitadora.h"
|
#include "games/gitadora/gitadora.h"
|
||||||
#include "games/iidx/iidx.h"
|
#include "games/iidx/iidx.h"
|
||||||
|
#include "games/popn/popn.h"
|
||||||
#include "hooks/graphics/backends/d3d9/d3d9_backend.h"
|
#include "hooks/graphics/backends/d3d9/d3d9_backend.h"
|
||||||
#include "launcher/shutdown.h"
|
#include "launcher/shutdown.h"
|
||||||
#include "overlay/overlay.h"
|
#include "overlay/overlay.h"
|
||||||
@@ -35,6 +39,8 @@ struct CaptureData {
|
|||||||
HWND TDJ_SUBSCREEN_WINDOW = nullptr;
|
HWND TDJ_SUBSCREEN_WINDOW = nullptr;
|
||||||
HWND SDVX_SUBSCREEN_WINDOW = nullptr;
|
HWND SDVX_SUBSCREEN_WINDOW = nullptr;
|
||||||
HWND GFDM_SUBSCREEN_WINDOW = nullptr;
|
HWND GFDM_SUBSCREEN_WINDOW = nullptr;
|
||||||
|
HWND POPN_SUBSCREEN_WINDOW = nullptr;
|
||||||
|
bool FAKE_SUBSCREEN_ADAPTER = false;
|
||||||
|
|
||||||
// icon
|
// icon
|
||||||
static HICON WINDOW_ICON = LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(MAINICON));
|
static HICON WINDOW_ICON = LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(MAINICON));
|
||||||
@@ -331,17 +337,18 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
|||||||
|
|
||||||
bool is_tdj_sub_window = avs::game::is_model("LDJ") && window_name.ends_with(" sub");
|
bool is_tdj_sub_window = avs::game::is_model("LDJ") && window_name.ends_with(" sub");
|
||||||
bool is_sdvx_sub_window = avs::game::is_model("KFC") && window_name.ends_with(" Sub Screen");
|
bool is_sdvx_sub_window = avs::game::is_model("KFC") && window_name.ends_with(" Sub Screen");
|
||||||
|
bool is_popn_sub_window = avs::game::is_model("M39") && window_name.ends_with("Sub Screen");
|
||||||
bool is_gfdm_sub_window = games::gitadora::is_arena_model() && window_name.ends_with("SMALL");
|
bool is_gfdm_sub_window = games::gitadora::is_arena_model() && window_name.ends_with("SMALL");
|
||||||
|
|
||||||
// hide maximize button (prevent misaligned touches)
|
// hide maximize button (prevent misaligned touches)
|
||||||
if ((is_tdj_sub_window && GRAPHICS_IIDX_WSUB) || is_sdvx_sub_window || is_gfdm_sub_window) {
|
if ((is_tdj_sub_window && GRAPHICS_IIDX_WSUB) || is_sdvx_sub_window || is_gfdm_sub_window || is_popn_sub_window) {
|
||||||
dwStyle &= ~(WS_MAXIMIZEBOX);
|
dwStyle &= ~(WS_MAXIMIZEBOX);
|
||||||
}
|
}
|
||||||
// mouse clicks become misaligned when resized
|
// mouse clicks become misaligned when resized
|
||||||
if (is_gfdm_sub_window) {
|
if (is_gfdm_sub_window) {
|
||||||
dwStyle &= ~(WS_SIZEBOX);
|
dwStyle &= ~(WS_SIZEBOX);
|
||||||
}
|
}
|
||||||
if ((is_tdj_sub_window || is_sdvx_sub_window) && GRAPHICS_WSUB_BORDERLESS) {
|
if ((is_tdj_sub_window || is_sdvx_sub_window || is_popn_sub_window) && GRAPHICS_WSUB_BORDERLESS) {
|
||||||
dwStyle &= ~(WS_OVERLAPPEDWINDOW);
|
dwStyle &= ~(WS_OVERLAPPEDWINDOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,6 +370,10 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GRAPHICS_WINDOWED) {
|
||||||
|
graphics_window_check_bounds_before_creation(x, y, nWidth, nHeight);
|
||||||
|
}
|
||||||
|
|
||||||
// call original
|
// call original
|
||||||
HWND result = CreateWindowExA_orig(dwExStyle, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight,
|
HWND result = CreateWindowExA_orig(dwExStyle, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight,
|
||||||
hWndParent, hMenu, hInstance, lpParam);
|
hWndParent, hMenu, hInstance, lpParam);
|
||||||
@@ -390,6 +401,13 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
|||||||
graphics_hook_subscreen_window(GFDM_SUBSCREEN_WINDOW);
|
graphics_hook_subscreen_window(GFDM_SUBSCREEN_WINDOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_popn_sub_window) {
|
||||||
|
POPN_SUBSCREEN_WINDOW = result;
|
||||||
|
if (!GRAPHICS_PREVENT_SECONDARY_WINDOW) {
|
||||||
|
graphics_hook_subscreen_window(POPN_SUBSCREEN_WINDOW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
disable_touch_indicators(result);
|
disable_touch_indicators(result);
|
||||||
log_misc(
|
log_misc(
|
||||||
"graphics",
|
"graphics",
|
||||||
@@ -458,6 +476,18 @@ 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);
|
||||||
|
}
|
||||||
|
|
||||||
// call original
|
// call original
|
||||||
HWND result = CreateWindowExW_orig(
|
HWND result = CreateWindowExW_orig(
|
||||||
dwExStyle, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight,
|
dwExStyle, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight,
|
||||||
@@ -653,6 +683,13 @@ static BOOL WINAPI ShowWindow_hook(HWND hWnd, int nCmdShow) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (games::popn::is_pikapika_model() &&
|
||||||
|
GRAPHICS_PREVENT_SECONDARY_WINDOW &&
|
||||||
|
hWnd == POPN_SUBSCREEN_WINDOW) {
|
||||||
|
log_info("graphics", "ShowWindow_hook - hiding sub window {}", fmt::ptr(hWnd));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// call original
|
// call original
|
||||||
return ShowWindow_orig(hWnd, nCmdShow);
|
return ShowWindow_orig(hWnd, nCmdShow);
|
||||||
}
|
}
|
||||||
@@ -895,6 +932,9 @@ void graphics_hook_subscreen_window(HWND hWnd) {
|
|||||||
if (GRAPHICS_WSUB_ALWAYS_ON_TOP) {
|
if (GRAPHICS_WSUB_ALWAYS_ON_TOP) {
|
||||||
graphics_update_z_order(hWnd, true);
|
graphics_update_z_order(hWnd, true);
|
||||||
}
|
}
|
||||||
|
if (GRAPHICS_WINDOW_DISABLE_ROUNDED_CORNERS) {
|
||||||
|
graphics_set_corner_preference(hWnd, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void graphics_screens_register(int screen) {
|
void graphics_screens_register(int screen) {
|
||||||
|
|||||||
@@ -7,6 +7,9 @@
|
|||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <d3d9.h>
|
#include <d3d9.h>
|
||||||
|
#if !SPICE_XP
|
||||||
|
#include <dwmapi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "external/toojpeg/toojpeg.h"
|
#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 std::optional<std::string> GRAPHICS_WINDOW_POS;
|
||||||
extern bool GRAPHICS_WINDOW_ALWAYS_ON_TOP;
|
extern bool GRAPHICS_WINDOW_ALWAYS_ON_TOP;
|
||||||
extern bool GRAPHICS_WINDOW_BACKBUFFER_SCALE;
|
extern bool GRAPHICS_WINDOW_BACKBUFFER_SCALE;
|
||||||
|
extern bool GRAPHICS_WINDOW_DISABLE_ROUNDED_CORNERS;
|
||||||
extern std::optional<HWND> GRAPHICS_HOOKED_WINDOW;
|
extern std::optional<HWND> GRAPHICS_HOOKED_WINDOW;
|
||||||
|
|
||||||
extern bool GRAPHICS_IIDX_WSUB;
|
extern bool GRAPHICS_IIDX_WSUB;
|
||||||
@@ -63,9 +67,11 @@ extern bool GRAPHICS_WSUB_BORDERLESS;
|
|||||||
extern bool GRAPHICS_WSUB_ALWAYS_ON_TOP;
|
extern bool GRAPHICS_WSUB_ALWAYS_ON_TOP;
|
||||||
extern HWND TDJ_SUBSCREEN_WINDOW;
|
extern HWND TDJ_SUBSCREEN_WINDOW;
|
||||||
extern HWND SDVX_SUBSCREEN_WINDOW;
|
extern HWND SDVX_SUBSCREEN_WINDOW;
|
||||||
|
extern HWND POPN_SUBSCREEN_WINDOW;
|
||||||
extern HWND GFDM_SUBSCREEN_WINDOW;
|
extern HWND GFDM_SUBSCREEN_WINDOW;
|
||||||
|
|
||||||
extern bool SUBSCREEN_FORCE_REDRAW;
|
extern bool SUBSCREEN_FORCE_REDRAW;
|
||||||
|
extern bool FAKE_SUBSCREEN_ADAPTER;
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
extern std::string GRAPHICS_DEVICEID;
|
extern std::string GRAPHICS_DEVICEID;
|
||||||
@@ -99,14 +105,16 @@ std::string graphics_screenshot_genpath();
|
|||||||
// graphics_windowed.cpp
|
// graphics_windowed.cpp
|
||||||
void graphics_windowed_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
void graphics_windowed_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||||
void graphics_capture_initial_window(HWND hWnd);
|
void graphics_capture_initial_window(HWND hWnd);
|
||||||
void graphics_update_window_style(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_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);
|
void graphics_move_resize_window(HWND hWnd);
|
||||||
bool graphics_window_options_breaks_game();
|
bool graphics_window_options_breaks_game();
|
||||||
bool graphics_window_decoration_change_crashes_game();
|
bool graphics_window_decoration_change_crashes_game();
|
||||||
bool graphics_window_resize_breaks_game();
|
bool graphics_window_resize_breaks_game();
|
||||||
bool graphics_window_move_and_resize_breaks_game();
|
bool graphics_window_move_and_resize_breaks_game();
|
||||||
void graphics_load_windowed_subscreen_parameters();
|
void graphics_load_windowed_subscreen_parameters();
|
||||||
|
void graphics_window_check_bounds_before_creation(int &x, int &y, const int width, const int height);
|
||||||
|
|
||||||
void change_primary_monitor(const std::string &monitor_name);
|
void change_primary_monitor(const std::string &monitor_name);
|
||||||
void update_monitor_on_boot(std::optional<graphics_orientation> target_orientation, UINT target_refresh_rate);
|
void update_monitor_on_boot(std::optional<graphics_orientation> target_orientation, UINT target_refresh_rate);
|
||||||
@@ -24,6 +24,7 @@ std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_WINDOW_SIZE;
|
|||||||
std::optional<std::string> GRAPHICS_WINDOW_POS;
|
std::optional<std::string> GRAPHICS_WINDOW_POS;
|
||||||
bool GRAPHICS_WINDOW_ALWAYS_ON_TOP = false;
|
bool GRAPHICS_WINDOW_ALWAYS_ON_TOP = false;
|
||||||
bool GRAPHICS_WINDOW_BACKBUFFER_SCALE = false;
|
bool GRAPHICS_WINDOW_BACKBUFFER_SCALE = false;
|
||||||
|
bool GRAPHICS_WINDOW_DISABLE_ROUNDED_CORNERS = false;
|
||||||
std::optional<HWND> GRAPHICS_HOOKED_WINDOW;
|
std::optional<HWND> GRAPHICS_HOOKED_WINDOW;
|
||||||
|
|
||||||
// IIDX Windowed Subscreen - starts out as false, enabled by IIDX module on pre-attach as needed
|
// IIDX Windowed Subscreen - starts out as false, enabled by IIDX module on pre-attach as needed
|
||||||
@@ -121,18 +122,21 @@ void graphics_capture_initial_window(HWND hWnd) {
|
|||||||
cfg::SCREENRESIZE->client_height);
|
cfg::SCREENRESIZE->client_height);
|
||||||
graphics_move_resize_window(hWnd);
|
graphics_move_resize_window(hWnd);
|
||||||
}
|
}
|
||||||
// ddr hangs when window frame doesn't have overlapped
|
|
||||||
if (cfg::SCREENRESIZE->window_decoration != cfg::WindowDecorationMode::Default) {
|
if (cfg::SCREENRESIZE->window_decoration != cfg::WindowDecorationMode::Default) {
|
||||||
log_info(
|
log_info(
|
||||||
"graphics-windowed", "[{}] change window style - decoration: {}",
|
"graphics-windowed", "[{}] change window style - decoration: {}",
|
||||||
fmt::ptr(hWnd),
|
fmt::ptr(hWnd),
|
||||||
cfg::SCREENRESIZE->window_decoration);
|
cfg::SCREENRESIZE->window_decoration);
|
||||||
graphics_update_window_style(hWnd);
|
graphics_update_window_style(hWnd, true); // force
|
||||||
}
|
}
|
||||||
if (cfg::SCREENRESIZE->window_always_on_top) {
|
if (cfg::SCREENRESIZE->window_always_on_top) {
|
||||||
log_info("graphics-windowed", "[{}] change window z-order - always on top", fmt::ptr(hWnd));
|
log_info("graphics-windowed", "[{}] change window z-order - always on top", fmt::ptr(hWnd));
|
||||||
graphics_update_z_order(hWnd, true);
|
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
|
// ensure spicetouch coordinates are initialized
|
||||||
update_spicetouch_window_dimensions(hWnd);
|
update_spicetouch_window_dimensions(hWnd);
|
||||||
@@ -182,6 +186,10 @@ void graphics_load_windowed_parameters() {
|
|||||||
cfg::SCREENRESIZE->window_always_on_top = true;
|
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");
|
log_debug("graphics-windowed", "graphics_load_windowed_parameters returned");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,11 +393,11 @@ void graphics_wm_sizing_aspect_ratio(int edge, RECT& rect) {
|
|||||||
log_debug("graphics-windowed", "graphics_wm_sizing_aspect_ratio returned");
|
log_debug("graphics-windowed", "graphics_wm_sizing_aspect_ratio returned");
|
||||||
}
|
}
|
||||||
|
|
||||||
void graphics_update_window_style(HWND hWnd) {
|
void graphics_update_window_style(HWND hWnd, bool force_update) {
|
||||||
if (!GRAPHICS_WINDOWED) {
|
if (!GRAPHICS_WINDOWED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (graphics_window_decoration_change_crashes_game()) {
|
if (!force_update && graphics_window_decoration_change_crashes_game()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,6 +478,27 @@ void graphics_update_z_order(HWND hWnd, bool always_on_top) {
|
|||||||
log_debug("graphics-windowed", "graphics_update_z_order returned");
|
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) {
|
void graphics_move_resize_window(HWND hWnd) {
|
||||||
if (!GRAPHICS_WINDOWED) {
|
if (!GRAPHICS_WINDOWED) {
|
||||||
return;
|
return;
|
||||||
@@ -503,8 +532,15 @@ bool graphics_window_decoration_change_crashes_game() {
|
|||||||
static std::once_flag flag;
|
static std::once_flag flag;
|
||||||
static bool result = false;
|
static bool result = false;
|
||||||
std::call_once(flag, []() {
|
std::call_once(flag, []() {
|
||||||
// ddr crashes when frame style changes
|
// ddr and ftt crash when frame style changes
|
||||||
result = avs::game::is_model("MDX");
|
if (avs::game::is_model("MDX") || avs::game::is_model("MMD")) {
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// arena model hangs immediately when decoration changes
|
||||||
|
if (games::gitadora::is_arena_model()) {
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
|
||||||
// changing window decoration also changes dimensions and position
|
// changing window decoration also changes dimensions and position
|
||||||
// so this must be prevented
|
// so this must be prevented
|
||||||
@@ -560,3 +596,56 @@ bool graphics_window_resize_breaks_game() {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void graphics_window_check_bounds_before_creation(int &x, int &y, const int width, const int height) {
|
||||||
|
RECT rect = {
|
||||||
|
.left = x,
|
||||||
|
.top = y,
|
||||||
|
.right = x + width,
|
||||||
|
.bottom = y + height
|
||||||
|
};
|
||||||
|
|
||||||
|
HMONITOR mon = MonitorFromRect(&rect, MONITOR_DEFAULTTONULL);
|
||||||
|
if (mon != nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warning(
|
||||||
|
"graphics-windowed",
|
||||||
|
"window is completely out of bounds, resetting position: ({}, {}) => (0, 0)",
|
||||||
|
x, y);
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
rect = {
|
||||||
|
.left = x,
|
||||||
|
.top = y,
|
||||||
|
.right = x + width,
|
||||||
|
.bottom = y + height
|
||||||
|
};
|
||||||
|
|
||||||
|
mon = MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST);
|
||||||
|
MONITORINFO mi = {};
|
||||||
|
mi.cbSize = sizeof(mi);
|
||||||
|
if (!GetMonitorInfo(mon, &mi)) {
|
||||||
|
log_warning(
|
||||||
|
"graphics-windowed",
|
||||||
|
"failed to get monitor info for out of bounds window");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_misc(
|
||||||
|
"graphics-windowed",
|
||||||
|
"nearest monitor for this window has work area ({}, {}),({}, {})",
|
||||||
|
mi.rcWork.left, mi.rcWork.top, mi.rcWork.right, mi.rcWork.bottom);
|
||||||
|
|
||||||
|
RECT bounds = mi.rcWork;
|
||||||
|
const bool fully_visible =
|
||||||
|
(rect.left >= bounds.left &&
|
||||||
|
rect.top >= bounds.top &&
|
||||||
|
rect.right <= bounds.right &&
|
||||||
|
rect.bottom <= bounds.bottom);
|
||||||
|
|
||||||
|
if (!fully_visible) {
|
||||||
|
log_warning("graphics-windowed", "window will not be fully visible on monitor");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ static decltype(GetLocaleInfoEx) *GetLocaleInfoEx_orig = nullptr;
|
|||||||
static decltype(GetSystemDefaultLCID) *GetSystemDefaultLCID_orig = nullptr;
|
static decltype(GetSystemDefaultLCID) *GetSystemDefaultLCID_orig = nullptr;
|
||||||
static decltype(IsDBCSLeadByte) *IsDBCSLeadByte_orig = nullptr;
|
static decltype(IsDBCSLeadByte) *IsDBCSLeadByte_orig = nullptr;
|
||||||
static decltype(WideCharToMultiByte) *WideCharToMultiByte_orig = nullptr;
|
static decltype(WideCharToMultiByte) *WideCharToMultiByte_orig = nullptr;
|
||||||
|
static decltype(GetLocaleInfoA) *GetLocaleInfoA_orig = nullptr;
|
||||||
|
static decltype(GetThreadLocale) *GetThreadLocale_orig = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static NTSTATUS NTAPI RtlMultiByteToUnicodeN_hook(
|
static NTSTATUS NTAPI RtlMultiByteToUnicodeN_hook(
|
||||||
@@ -94,6 +96,10 @@ static LCID WINAPI GetSystemDefaultLCID_hook() {
|
|||||||
return 0x411;
|
return 0x411;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static LCID WINAPI GetThreadLocale_hook() {
|
||||||
|
return 0x411;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int WINAPI MultiByteToWideChar_hook(
|
static int WINAPI MultiByteToWideChar_hook(
|
||||||
@@ -214,6 +220,31 @@ WideCharToMultiByte_hook(
|
|||||||
lpUsedDefaultChar);
|
lpUsedDefaultChar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
WINAPI
|
||||||
|
GetLocaleInfoA_hook(
|
||||||
|
LCID Locale,
|
||||||
|
LCTYPE LCType,
|
||||||
|
LPSTR lpLCData,
|
||||||
|
int cchData) {
|
||||||
|
|
||||||
|
if (LCType == LOCALE_SISO639LANGNAME && lpLCData != NULL && cchData >= 3) {
|
||||||
|
log_misc("hooks::lang", "GetLocaleInfoA_hook hit ({:#x}, LOCALE_SISO639LANGNAME), return `ja`", Locale);
|
||||||
|
strcpy(lpLCData, "ja");
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LCType == LOCALE_SISO3166CTRYNAME && lpLCData != NULL && cchData >= 3) {
|
||||||
|
log_misc("hooks::lang",
|
||||||
|
"GetLocaleInfoA_hook hit ({:#x}, LOCALE_SISO3166CTRYNAME), return `JP`", Locale);
|
||||||
|
strcpy(lpLCData, "JP");
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_misc("hooks::lang", "GetLocaleInfoA_hook hit, {:#x}, {:#x}", Locale, LCType);
|
||||||
|
return GetLocaleInfoA_orig(Locale, LCType, lpLCData, cchData);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void hooks::lang::early_init() {
|
void hooks::lang::early_init() {
|
||||||
@@ -244,6 +275,27 @@ void hooks::lang::early_init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SPICE64
|
#ifdef SPICE64
|
||||||
|
|
||||||
|
if (avs::game::is_model("UJK")) {
|
||||||
|
// CCJ build of Unity calls GetThreadLocale,
|
||||||
|
// and then GetLocaleInfoA to figure out the locale; eventually this is
|
||||||
|
// used to figure out the decimal separator, which is then used in parsing
|
||||||
|
// Double values - which fails carding in if it isn't "."
|
||||||
|
log_info("hooks::lang", "hooking GetThreadLocale");
|
||||||
|
detour::trampoline_try(
|
||||||
|
"kernel32.dll",
|
||||||
|
"GetThreadLocale",
|
||||||
|
GetThreadLocale_hook,
|
||||||
|
&GetThreadLocale_orig);
|
||||||
|
|
||||||
|
log_info("hooks::lang", "hooking GetLocaleInfoA");
|
||||||
|
detour::trampoline_try(
|
||||||
|
"kernel32.dll",
|
||||||
|
"GetLocaleInfoA",
|
||||||
|
GetLocaleInfoA_hook,
|
||||||
|
&GetLocaleInfoA_orig);
|
||||||
|
}
|
||||||
|
|
||||||
// for TDJ subscreen search keyboard
|
// for TDJ subscreen search keyboard
|
||||||
if (avs::game::is_model("LDJ") && games::iidx::TDJ_MODE) {
|
if (avs::game::is_model("LDJ") && games::iidx::TDJ_MODE) {
|
||||||
log_info("hooks::lang", "hooking IsDBCSLeadByte");
|
log_info("hooks::lang", "hooking IsDBCSLeadByte");
|
||||||
|
|||||||
@@ -473,6 +473,9 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::spice2x_SDVXSubRedraw].value_bool()) {
|
if (options[launcher::Options::spice2x_SDVXSubRedraw].value_bool()) {
|
||||||
SUBSCREEN_FORCE_REDRAW = true;
|
SUBSCREEN_FORCE_REDRAW = true;
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::SDVXSubMonitorOverride].is_active()) {
|
||||||
|
sysutils::SECOND_MONITOR_OVERRIDE = options[launcher::Options::SDVXSubMonitorOverride].value_text();
|
||||||
|
}
|
||||||
if (options[launcher::Options::LoadIIDXModule].value_bool()) {
|
if (options[launcher::Options::LoadIIDXModule].value_bool()) {
|
||||||
attach_iidx = true;
|
attach_iidx = true;
|
||||||
}
|
}
|
||||||
@@ -587,6 +590,16 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::PopnMusicForceSDMode].value_bool()) {
|
if (options[launcher::Options::PopnMusicForceSDMode].value_bool()) {
|
||||||
avs::ea3::PCB_TYPE = 0;
|
avs::ea3::PCB_TYPE = 0;
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::PopnNoSub].value_bool()) {
|
||||||
|
GRAPHICS_FORCE_SINGLE_ADAPTER = true;
|
||||||
|
GRAPHICS_PREVENT_SECONDARY_WINDOW = true;
|
||||||
|
}
|
||||||
|
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()) {
|
if (options[launcher::Options::LoadMetalGearArcadeModule].value_bool()) {
|
||||||
attach_mga = true;
|
attach_mga = true;
|
||||||
}
|
}
|
||||||
@@ -1107,6 +1120,7 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
GRAPHICS_WINDOW_ALWAYS_ON_TOP = options[launcher::Options::spice2x_WindowAlwaysOnTop].value_bool();
|
GRAPHICS_WINDOW_ALWAYS_ON_TOP = options[launcher::Options::spice2x_WindowAlwaysOnTop].value_bool();
|
||||||
GRAPHICS_WINDOW_BACKBUFFER_SCALE = options[launcher::Options::WindowForceScaling].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
|
// IIDX/SDVX Windowed Subscreen
|
||||||
if (options[launcher::Options::spice2x_IIDXWindowedSubscreenSize].is_active()) {
|
if (options[launcher::Options::spice2x_IIDXWindowedSubscreenSize].is_active()) {
|
||||||
@@ -1127,6 +1141,9 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
options[launcher::Options::SDVXWindowedSubscreenAlwaysOnTop].value_bool()) {
|
options[launcher::Options::SDVXWindowedSubscreenAlwaysOnTop].value_bool()) {
|
||||||
GRAPHICS_WSUB_ALWAYS_ON_TOP = true;
|
GRAPHICS_WSUB_ALWAYS_ON_TOP = true;
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::IIDXSubMonitorOverride].is_active()) {
|
||||||
|
sysutils::SECOND_MONITOR_OVERRIDE = options[launcher::Options::IIDXSubMonitorOverride].value_text();
|
||||||
|
}
|
||||||
|
|
||||||
if (options[launcher::Options::spice2x_JubeatLegacyTouch].value_bool()) {
|
if (options[launcher::Options::spice2x_JubeatLegacyTouch].value_bool()) {
|
||||||
games::jb::TOUCH_LEGACY_BOX = true;
|
games::jb::TOUCH_LEGACY_BOX = true;
|
||||||
@@ -1294,7 +1311,19 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// log
|
// log
|
||||||
#ifndef SPICE_LINUX
|
#if SPICE_LINUX
|
||||||
|
#ifdef SPICE64
|
||||||
|
log_info("launcher", "SpiceTools Bootstrap (x64) (spice2x) for Linux");
|
||||||
|
#else
|
||||||
|
log_info("launcher", "SpiceTools Bootstrap (x32) (spice2x) for Linux");
|
||||||
|
#endif
|
||||||
|
#elif SPICE_XP
|
||||||
|
#ifdef SPICE64
|
||||||
|
log_info("launcher", "SpiceTools Bootstrap (x64) (spice2x) for WinXP");
|
||||||
|
#else
|
||||||
|
log_info("launcher", "SpiceTools Bootstrap (x32) (spice2x) for WinXP");
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
#ifdef SPICE64
|
#ifdef SPICE64
|
||||||
log_info("launcher", "SpiceTools Bootstrap (x64) (spice2x)");
|
log_info("launcher", "SpiceTools Bootstrap (x64) (spice2x)");
|
||||||
#elif SPICE32_LARGE_ADDRESS_AWARE
|
#elif SPICE32_LARGE_ADDRESS_AWARE
|
||||||
@@ -1302,12 +1331,6 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
#else
|
#else
|
||||||
log_info("launcher", "SpiceTools Bootstrap (x32) (spice2x)");
|
log_info("launcher", "SpiceTools Bootstrap (x32) (spice2x)");
|
||||||
#endif
|
#endif
|
||||||
#else
|
|
||||||
#ifdef SPICE64
|
|
||||||
log_info("launcher", "SpiceTools Bootstrap (x64) (spice2x) for Linux");
|
|
||||||
#else
|
|
||||||
log_info("launcher", "SpiceTools Bootstrap (x32) (spice2x) for Linux");
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
log_info("launcher", "{}", VERSION_STRING);
|
log_info("launcher", "{}", VERSION_STRING);
|
||||||
@@ -1745,11 +1768,21 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// HELLO! Pop'n Music
|
|
||||||
if (check_dll("popn.dll")) {
|
if (check_dll("popn.dll")) {
|
||||||
avs::game::DLL_NAME = "popn.dll";
|
avs::game::DLL_NAME = "popn.dll";
|
||||||
attach_io = true;
|
attach_io = true;
|
||||||
|
|
||||||
|
if (check_dll("libaio-iob2_video.dll")) {
|
||||||
|
// pop'n music (pika cabinet)
|
||||||
|
attach_popn = true;
|
||||||
|
// automatically show cursor in windowed mode to interact with second window (sub)
|
||||||
|
if (GRAPHICS_WINDOWED) {
|
||||||
|
GRAPHICS_SHOW_CURSOR = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// HELLO! Pop'n Music
|
||||||
attach_hpm = true;
|
attach_hpm = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1802,9 +1835,7 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
attach_ftt = true;
|
attach_ftt = true;
|
||||||
|
|
||||||
// the game is windowed by default unless we set the env
|
// the game is windowed by default unless we set the env
|
||||||
if (GRAPHICS_WINDOWED) {
|
if (!GRAPHICS_WINDOWED) {
|
||||||
GRAPHICS_WINDOWED = false;
|
|
||||||
} else {
|
|
||||||
SetEnvironmentVariable("DAMAC_VIEWER_FULLSCREEN", "0");
|
SetEnvironmentVariable("DAMAC_VIEWER_FULLSCREEN", "0");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2226,7 +2257,7 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
game->attach();
|
game->attach();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SPICE64
|
#if SPICE64 && !SPICE_XP
|
||||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||||
if (games::iidx::TDJ_CAMERA) {
|
if (games::iidx::TDJ_CAMERA) {
|
||||||
games::iidx::init_camera_hooks();
|
games::iidx::init_camera_hooks();
|
||||||
@@ -2333,9 +2364,9 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
// layeredfs
|
// layeredfs
|
||||||
if (fileutils::dir_exists("data_mods") &&
|
if (fileutils::dir_exists("data_mods") &&
|
||||||
GetModuleHandleA("ifs_hook.dll") == nullptr) {
|
GetModuleHandleA("ifs_hook.dll") == nullptr) {
|
||||||
log_warning("launcher", "data_mods directory found, but ifs_hook.dll is not present; mods will not load");
|
log_warning("launcher", "data_mods directory found, but ifs_hook.dll is not loaded; mods will not load");
|
||||||
deferredlogs::defer_error_messages({
|
deferredlogs::defer_error_messages({
|
||||||
"data_mods directory was found, but ifs_hook.dll is not present",
|
"data_mods directory was found, but ifs_hook.dll is not loaded",
|
||||||
" your mods will not load",
|
" your mods will not load",
|
||||||
" to fix this, download ifs_layeredfs and add it as a DLL hook (-k)",
|
" to fix this, download ifs_layeredfs and add it as a DLL hook (-k)",
|
||||||
" https://github.com/mon/ifs_layeredfs"
|
" https://github.com/mon/ifs_layeredfs"
|
||||||
@@ -2507,7 +2538,7 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
// disable poke
|
// disable poke
|
||||||
games::iidx::poke::disable();
|
games::iidx::poke::disable();
|
||||||
|
|
||||||
#ifdef SPICE64
|
#if SPICE64 && !SPICE_XP
|
||||||
games::iidx::camera_release();
|
games::iidx::camera_release();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ static const std::vector<std::string> CATEGORY_ORDER_BASIC = {
|
|||||||
|
|
||||||
static const std::vector<std::string> CATEGORY_ORDER_ADVANCED = {
|
static const std::vector<std::string> CATEGORY_ORDER_ADVANCED = {
|
||||||
"Game Options (Advanced)",
|
"Game Options (Advanced)",
|
||||||
|
"Game Options (Peripherals)",
|
||||||
"Overlay",
|
"Overlay",
|
||||||
"Network (Advanced)",
|
"Network (Advanced)",
|
||||||
"Performance",
|
"Performance",
|
||||||
@@ -513,7 +514,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.desc = "Flip the camera order.",
|
.desc = "Flip the camera order.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "Beatmania IIDX",
|
.game_name = "Beatmania IIDX",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Peripherals)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// IIDXDisableCameras
|
// IIDXDisableCameras
|
||||||
@@ -523,7 +524,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.hidden = true,
|
.hidden = true,
|
||||||
.game_name = "Beatmania IIDX",
|
.game_name = "Beatmania IIDX",
|
||||||
.category = "Game Options",
|
.category = "Game Options (Peripherals)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// IIDXCabCamAccess
|
// 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.",
|
"Only turn this on if you have OFFICIAL arcade cameras connected to the correct USB ports. Default: auto.",
|
||||||
.type = OptionType::Enum,
|
.type = OptionType::Enum,
|
||||||
.game_name = "Beatmania IIDX",
|
.game_name = "Beatmania IIDX",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Peripherals)",
|
||||||
.elements = {
|
.elements = {
|
||||||
{"auto", ""},
|
{"auto", ""},
|
||||||
{"off", ""},
|
{"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.",
|
.desc = "Experimental camera support for IIDX 27+ via texture hooking. Requires camera(s) that support YUY2 or NV12 encoding.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "Beatmania IIDX",
|
.game_name = "Beatmania IIDX",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Peripherals)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// IIDXCamHookRatio
|
// IIDXCamHookRatio
|
||||||
@@ -561,7 +562,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.type = OptionType::Enum,
|
.type = OptionType::Enum,
|
||||||
.hidden = true,
|
.hidden = true,
|
||||||
.game_name = "Beatmania IIDX",
|
.game_name = "Beatmania IIDX",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Peripherals)",
|
||||||
.elements = {
|
.elements = {
|
||||||
{"43", "4:3"},
|
{"43", "4:3"},
|
||||||
{"169", "16:9"},
|
{"169", "16:9"},
|
||||||
@@ -579,7 +580,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.setting_name = "0x5817a0,0x6fffbd8,0xbbae40,0xe0,0x30",
|
.setting_name = "0x5817a0,0x6fffbd8,0xbbae40,0xe0,0x30",
|
||||||
.game_name = "Beatmania IIDX",
|
.game_name = "Beatmania IIDX",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Peripherals)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// IIDXCamHookTopId
|
// IIDXCamHookTopId
|
||||||
@@ -594,7 +595,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.setting_name = "vid_1234&pid_5678",
|
.setting_name = "vid_1234&pid_5678",
|
||||||
.game_name = "Beatmania IIDX",
|
.game_name = "Beatmania IIDX",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Peripherals)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// IIDXCamHookFrontId
|
// IIDXCamHookFrontId
|
||||||
@@ -609,7 +610,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.setting_name = "vid_90ab&pid_cdef",
|
.setting_name = "vid_90ab&pid_cdef",
|
||||||
.game_name = "Beatmania IIDX",
|
.game_name = "Beatmania IIDX",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Peripherals)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "IIDX Sound Output Device",
|
.title = "IIDX Sound Output Device",
|
||||||
@@ -772,11 +773,11 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Advanced)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "SDVX Force 720p",
|
.title = "SDVX Vivid Wave Force 720p Window (DEPRECATED - use -windowsize instead)",
|
||||||
.name = "sdvx720",
|
.name = "sdvx720",
|
||||||
.desc = "Force Sound Voltex 720p display mode, used by older games. "
|
.desc = "Old & deprecated option for launching Vivid Wave in 720p when using windowed mode.",
|
||||||
"For newer games, use window resize function instead.",
|
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
|
.hidden = true,
|
||||||
.game_name = "Sound Voltex",
|
.game_name = "Sound Voltex",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Advanced)",
|
||||||
},
|
},
|
||||||
@@ -786,7 +787,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.desc = "Enable Sound Voltex printer emulation.",
|
.desc = "Enable Sound Voltex printer emulation.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "Sound Voltex",
|
.game_name = "Sound Voltex",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Peripherals)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// SDVXPrinterOutputPath
|
// SDVXPrinterOutputPath
|
||||||
@@ -795,7 +796,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.desc = "Path to folder where images will be stored.",
|
.desc = "Path to folder where images will be stored.",
|
||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.game_name = "Sound Voltex",
|
.game_name = "Sound Voltex",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Peripherals)",
|
||||||
.picker = OptionPickerType::DirectoryPath,
|
.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.",
|
.desc = "Clean up saved images in the output directory on startup.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "Sound Voltex",
|
.game_name = "Sound Voltex",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Peripherals)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "SDVX Printer Output Overwrite",
|
.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.",
|
.desc = "Always overwrite the same file in output directory.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "Sound Voltex",
|
.game_name = "Sound Voltex",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Peripherals)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// SDVXPrinterOutputFormat
|
// SDVXPrinterOutputFormat
|
||||||
@@ -822,7 +823,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.setting_name = "(png/bmp/tga/jpg)",
|
.setting_name = "(png/bmp/tga/jpg)",
|
||||||
.game_name = "Sound Voltex",
|
.game_name = "Sound Voltex",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Peripherals)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "SDVX Printer JPG Quality",
|
.title = "SDVX Printer JPG Quality",
|
||||||
@@ -831,7 +832,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.type = OptionType::Integer,
|
.type = OptionType::Integer,
|
||||||
.setting_name = "(0-100)",
|
.setting_name = "(0-100)",
|
||||||
.game_name = "Sound Voltex",
|
.game_name = "Sound Voltex",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Peripherals)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "SDVX Disable Cameras (DEPRECATED - no longer needed)",
|
.title = "SDVX Disable Cameras (DEPRECATED - no longer needed)",
|
||||||
@@ -842,7 +843,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.hidden = true,
|
.hidden = true,
|
||||||
.game_name = "Sound Voltex",
|
.game_name = "Sound Voltex",
|
||||||
.category = "Game Options",
|
.category = "Game Options (Peripherals)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "SDVX FS Subscreen Native Touch Handling",
|
.title = "SDVX FS Subscreen Native Touch Handling",
|
||||||
@@ -928,6 +929,17 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
{"bottomright", "for landscape"},
|
{"bottomright", "for landscape"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// SDVXSubMonitorOverride
|
||||||
|
.title = "SDVX Subscreen Monitor Override",
|
||||||
|
.name = "sdvxsubmonitor",
|
||||||
|
.desc = "If you have three or more monitors, this option can be set to tell the game which monitor is the subscreen.",
|
||||||
|
.type = OptionType::Text,
|
||||||
|
.setting_name = "\\\\.\\DISPLAY3",
|
||||||
|
.game_name = "Sound Voltex",
|
||||||
|
.category = "Monitor",
|
||||||
|
.picker = OptionPickerType::Monitor,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.title = "Force Load DDR Module",
|
.title = "Force Load DDR Module",
|
||||||
.name = "ddr",
|
.name = "ddr",
|
||||||
@@ -977,6 +989,38 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.game_name = "Pop'n Music",
|
.game_name = "Pop'n Music",
|
||||||
.category = "Game Options",
|
.category = "Game Options",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// PopnNoSub
|
||||||
|
.title = "Pop'n Music PikaPika Subscreen Disable",
|
||||||
|
.name = "popnnosub",
|
||||||
|
.desc = "Prevents PikaPika model subscreen from launching a separate window.",
|
||||||
|
.type = OptionType::Bool,
|
||||||
|
.game_name = "Pop'n Music",
|
||||||
|
.category = "Game Options",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// PopnSubMonitorOverride
|
||||||
|
.title = "Pop'n Music PikaPika Subscreen Monitor Override",
|
||||||
|
.name = "popnsubmonitor",
|
||||||
|
.desc = "If you have three or more monitors, this option can be set to tell the game which monitor is the subscreen.",
|
||||||
|
.type = OptionType::Text,
|
||||||
|
.setting_name = "\\\\.\\DISPLAY3",
|
||||||
|
.game_name = "Pop'n Music",
|
||||||
|
.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",
|
.title = "Force Load HELLO! Pop'n Music Module",
|
||||||
.name = "hpm",
|
.name = "hpm",
|
||||||
@@ -2187,6 +2231,14 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.category = "Graphics (Windowed)",
|
.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
|
// spice2x_IIDXWindowedSubscreenSize
|
||||||
.title = "IIDX Windowed Subscreen Size",
|
.title = "IIDX Windowed Subscreen Size",
|
||||||
@@ -2316,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!",
|
"Note: this is NOT for the motion camera - you still need a real RealSense camera for that!",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "DANCERUSH",
|
.game_name = "DANCERUSH",
|
||||||
.category = "Game Options",
|
.category = "Game Options (Peripherals)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// spice2x_IIDXNativeTouch
|
// spice2x_IIDXNativeTouch
|
||||||
@@ -2343,6 +2395,17 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.game_name = "Beatmania IIDX",
|
.game_name = "Beatmania IIDX",
|
||||||
.category = "Game Options",
|
.category = "Game Options",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// IIDXSubMonitorOverride
|
||||||
|
.title = "IIDX TDJ Subscreen Monitor Override",
|
||||||
|
.name = "iidxsubmonitor",
|
||||||
|
.desc = "If you have three or more monitors, this option can be set to tell the game which monitor is the subscreen.",
|
||||||
|
.type = OptionType::Text,
|
||||||
|
.setting_name = "\\\\.\\DISPLAY3",
|
||||||
|
.game_name = "Beatmania IIDX",
|
||||||
|
.category = "Monitor",
|
||||||
|
.picker = OptionPickerType::Monitor,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// spice2x_IIDXEmulateSubscreenKeypadTouch
|
// spice2x_IIDXEmulateSubscreenKeypadTouch
|
||||||
.title = "IIDX TDJ Subscreen Keypad Touch Emulation",
|
.title = "IIDX TDJ Subscreen Keypad Touch Emulation",
|
||||||
@@ -2639,7 +2702,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.desc = "Allow game to access camera; camera must be compatible with game.",
|
.desc = "Allow game to access camera; camera must be compatible with game.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "LovePlus",
|
.game_name = "LovePlus",
|
||||||
.category = "Game Options",
|
.category = "Game Options (Peripherals)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// LovePlusPrinterOutputPath
|
// LovePlusPrinterOutputPath
|
||||||
@@ -2648,7 +2711,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.desc = "Path to folder where images will be stored.",
|
.desc = "Path to folder where images will be stored.",
|
||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.game_name = "LovePlus",
|
.game_name = "LovePlus",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Peripherals)",
|
||||||
.picker = OptionPickerType::DirectoryPath,
|
.picker = OptionPickerType::DirectoryPath,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2657,7 +2720,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.desc = "Clean up saved images in the output directory on startup.",
|
.desc = "Clean up saved images in the output directory on startup.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "LovePlus",
|
.game_name = "LovePlus",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Peripherals)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "LovePlus Printer Output Overwrite",
|
.title = "LovePlus Printer Output Overwrite",
|
||||||
@@ -2665,7 +2728,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.desc = "Always overwrite the same file in output directory.",
|
.desc = "Always overwrite the same file in output directory.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "LovePlus",
|
.game_name = "LovePlus",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Peripherals)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// LovePlusPrinterOutputFormat
|
// LovePlusPrinterOutputFormat
|
||||||
@@ -2675,7 +2738,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.type = OptionType::Text,
|
.type = OptionType::Text,
|
||||||
.setting_name = "(png/bmp/tga/jpg)",
|
.setting_name = "(png/bmp/tga/jpg)",
|
||||||
.game_name = "LovePlus",
|
.game_name = "LovePlus",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Peripherals)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "LovePlus Printer JPG Quality",
|
.title = "LovePlus Printer JPG Quality",
|
||||||
@@ -2684,7 +2747,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.type = OptionType::Integer,
|
.type = OptionType::Integer,
|
||||||
.setting_name = "(0-100)",
|
.setting_name = "(0-100)",
|
||||||
.game_name = "LovePlus",
|
.game_name = "LovePlus",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Peripherals)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// OptionConflictResolution
|
// OptionConflictResolution
|
||||||
@@ -2705,7 +2768,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
"option to bypass camera error during boot, allowing you to try out the game.",
|
"option to bypass camera error during boot, allowing you to try out the game.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "Otoca D'or",
|
.game_name = "Otoca D'or",
|
||||||
.category = "Game Options",
|
.category = "Game Options (Peripherals)",
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -92,12 +92,16 @@ namespace launcher {
|
|||||||
SDVXDigitalKnobSocd,
|
SDVXDigitalKnobSocd,
|
||||||
spice2x_SDVXAsioDriver,
|
spice2x_SDVXAsioDriver,
|
||||||
spice2x_SDVXSubPos,
|
spice2x_SDVXSubPos,
|
||||||
|
SDVXSubMonitorOverride,
|
||||||
LoadDDRModule,
|
LoadDDRModule,
|
||||||
DDR43Mode,
|
DDR43Mode,
|
||||||
DDRSkipCodecRegisteration,
|
DDRSkipCodecRegisteration,
|
||||||
LoadPopnMusicModule,
|
LoadPopnMusicModule,
|
||||||
PopnMusicForceHDMode,
|
PopnMusicForceHDMode,
|
||||||
PopnMusicForceSDMode,
|
PopnMusicForceSDMode,
|
||||||
|
PopnNoSub,
|
||||||
|
PopnSubMonitorOverride,
|
||||||
|
PopnNativeTouch,
|
||||||
LoadHelloPopnMusicModule,
|
LoadHelloPopnMusicModule,
|
||||||
LoadGitaDoraModule,
|
LoadGitaDoraModule,
|
||||||
GitaDoraTwoChannelAudio,
|
GitaDoraTwoChannelAudio,
|
||||||
@@ -229,6 +233,7 @@ namespace launcher {
|
|||||||
spice2x_WindowPosition,
|
spice2x_WindowPosition,
|
||||||
spice2x_WindowAlwaysOnTop,
|
spice2x_WindowAlwaysOnTop,
|
||||||
WindowForceScaling,
|
WindowForceScaling,
|
||||||
|
WindowDisableRoundedCorners,
|
||||||
spice2x_IIDXWindowedSubscreenSize,
|
spice2x_IIDXWindowedSubscreenSize,
|
||||||
spice2x_IIDXWindowedSubscreenPosition,
|
spice2x_IIDXWindowedSubscreenPosition,
|
||||||
IIDXWindowedSubscreenBorderless,
|
IIDXWindowedSubscreenBorderless,
|
||||||
@@ -243,6 +248,7 @@ namespace launcher {
|
|||||||
DRSRGBCameraHook,
|
DRSRGBCameraHook,
|
||||||
spice2x_IIDXNativeTouch,
|
spice2x_IIDXNativeTouch,
|
||||||
spice2x_IIDXNoSub,
|
spice2x_IIDXNoSub,
|
||||||
|
IIDXSubMonitorOverride,
|
||||||
spice2x_IIDXEmulateSubscreenKeypadTouch,
|
spice2x_IIDXEmulateSubscreenKeypadTouch,
|
||||||
spice2x_AutoCard,
|
spice2x_AutoCard,
|
||||||
spice2x_LowLatencySharedAudio,
|
spice2x_LowLatencySharedAudio,
|
||||||
|
|||||||
@@ -86,13 +86,12 @@ namespace launcher {
|
|||||||
if (LAUNCHER_ARGC > 0) {
|
if (LAUNCHER_ARGC > 0) {
|
||||||
|
|
||||||
// build cmd line
|
// build cmd line
|
||||||
std::stringstream cmd_line;
|
std::string cmd_line = "START \"\" ";
|
||||||
cmd_line << "START \"\" ";
|
|
||||||
for (int i = 0; i < LAUNCHER_ARGC; i++)
|
for (int i = 0; i < LAUNCHER_ARGC; i++)
|
||||||
cmd_line << " \"" << LAUNCHER_ARGV[i] << "\"";
|
cmd_line += " \"" + std::string(LAUNCHER_ARGV[i]) + "\"";
|
||||||
|
|
||||||
// run command
|
// run command
|
||||||
system(cmd_line.str().c_str());
|
system(cmd_line.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "cfg/screen_resize.h"
|
#include "cfg/screen_resize.h"
|
||||||
#include "games/gitadora/gitadora.h"
|
#include "games/gitadora/gitadora.h"
|
||||||
#include "games/iidx/iidx.h"
|
#include "games/iidx/iidx.h"
|
||||||
|
#include "games/popn/popn.h"
|
||||||
#include "hooks/graphics/graphics.h"
|
#include "hooks/graphics/graphics.h"
|
||||||
#include "overlay/overlay.h"
|
#include "overlay/overlay.h"
|
||||||
#include "overlay/windows/generic_sub.h"
|
#include "overlay/windows/generic_sub.h"
|
||||||
@@ -405,6 +406,10 @@ namespace wintouchemu {
|
|||||||
// use mouse position as ImGui overlay will block the touch window
|
// use mouse position as ImGui overlay will block the touch window
|
||||||
log_info("wintouchemu", "use mouse cursor API for ldj overlay subscreen");
|
log_info("wintouchemu", "use mouse cursor API for ldj overlay subscreen");
|
||||||
USE_MOUSE = true;
|
USE_MOUSE = true;
|
||||||
|
} else if (games::popn::is_pikapika_model()) {
|
||||||
|
// same as iidx case above
|
||||||
|
log_info("wintouchemu", "use mouse cursor API for popn overlay subscreen");
|
||||||
|
USE_MOUSE = true;
|
||||||
} else if (games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW) {
|
} else if (games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW) {
|
||||||
log_info("wintouchemu", "use mouse cursor API for gitadora overlay subscreen");
|
log_info("wintouchemu", "use mouse cursor API for gitadora overlay subscreen");
|
||||||
USE_MOUSE = true;
|
USE_MOUSE = true;
|
||||||
|
|||||||
@@ -169,8 +169,10 @@ namespace ImGui {
|
|||||||
ImGui::PushStyleColor(ImGuiCol_Header, 0);
|
ImGui::PushStyleColor(ImGuiCol_Header, 0);
|
||||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, 0);
|
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, 0);
|
||||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, 0);
|
ImGui::PushStyleColor(ImGuiCol_HeaderActive, 0);
|
||||||
|
ImGui::PushTabStop(false); // prevent tab navigation
|
||||||
ImGui::Selectable("##row", false,
|
ImGui::Selectable("##row", false,
|
||||||
ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap);
|
ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap);
|
||||||
|
ImGui::PopTabStop();
|
||||||
ImGui::PopStyleColor(3);
|
ImGui::PopStyleColor(3);
|
||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered()) {
|
||||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg1, IM_COL32(200, 200, 200, 24));
|
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg1, IM_COL32(200, 200, 200, 24));
|
||||||
|
|||||||
@@ -265,7 +265,8 @@ static void ImGui_ImplSpice_UpdateMousePos() {
|
|||||||
// trigger any overlay, don't process anything else
|
// trigger any overlay, don't process anything else
|
||||||
const auto is_windowed_subscreen =
|
const auto is_windowed_subscreen =
|
||||||
(GRAPHICS_IIDX_WSUB && active_window == TDJ_SUBSCREEN_WINDOW) ||
|
(GRAPHICS_IIDX_WSUB && active_window == TDJ_SUBSCREEN_WINDOW) ||
|
||||||
(active_window == SDVX_SUBSCREEN_WINDOW);
|
(active_window == SDVX_SUBSCREEN_WINDOW) ||
|
||||||
|
(active_window == POPN_SUBSCREEN_WINDOW);
|
||||||
if (is_windowed_subscreen) {
|
if (is_windowed_subscreen) {
|
||||||
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
|
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
|
||||||
return;
|
return;
|
||||||
@@ -348,6 +349,10 @@ void ImGui_ImplSpice_NewFrame() {
|
|||||||
io.DeltaTime = (float) (current_time - g_Time) / g_TicksPerSecond;
|
io.DeltaTime = (float) (current_time - g_Time) / g_TicksPerSecond;
|
||||||
g_Time = current_time;
|
g_Time = current_time;
|
||||||
|
|
||||||
|
// only process new input from devices if window is in focus
|
||||||
|
// (when not in focus, all mouse/keyboard events are treated as released)
|
||||||
|
const auto accept_new_input = superexit::has_focus() && !rawinput::OS_WINDOW_ACTIVE;
|
||||||
|
|
||||||
// remember old state
|
// remember old state
|
||||||
std::array<BYTE, VKEY_MAX> KeysDownOld;
|
std::array<BYTE, VKEY_MAX> KeysDownOld;
|
||||||
for (size_t i = 0; i < VKEY_MAX; i++) {
|
for (size_t i = 0; i < VKEY_MAX; i++) {
|
||||||
@@ -360,11 +365,6 @@ void ImGui_ImplSpice_NewFrame() {
|
|||||||
g_MouseDown.fill(false);
|
g_MouseDown.fill(false);
|
||||||
g_KeysDown.fill(false);
|
g_KeysDown.fill(false);
|
||||||
|
|
||||||
// early quit if window not in focus
|
|
||||||
if (!superexit::has_focus() || rawinput::OS_WINDOW_ACTIVE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// apply windows mouse buttons
|
// apply windows mouse buttons
|
||||||
g_MouseDown[ImGuiMouseButton_Left] |= (get_async_primary_mouse()) != 0;
|
g_MouseDown[ImGuiMouseButton_Left] |= (get_async_primary_mouse()) != 0;
|
||||||
g_MouseDown[ImGuiMouseButton_Right] |= (get_async_secondary_mouse()) != 0;
|
g_MouseDown[ImGuiMouseButton_Right] |= (get_async_secondary_mouse()) != 0;
|
||||||
@@ -373,7 +373,7 @@ void ImGui_ImplSpice_NewFrame() {
|
|||||||
// read new keys state
|
// read new keys state
|
||||||
static long mouse_wheel_last = 0;
|
static long mouse_wheel_last = 0;
|
||||||
long mouse_wheel = 0;
|
long mouse_wheel = 0;
|
||||||
if (RI_MGR != nullptr) {
|
if (RI_MGR != nullptr && accept_new_input) {
|
||||||
auto devices = RI_MGR->devices_get();
|
auto devices = RI_MGR->devices_get();
|
||||||
for (auto &device : devices) {
|
for (auto &device : devices) {
|
||||||
switch (device.type) {
|
switch (device.type) {
|
||||||
@@ -470,7 +470,9 @@ void ImGui_ImplSpice_NewFrame() {
|
|||||||
|
|
||||||
// update OS mouse position
|
// update OS mouse position
|
||||||
const auto old_mouse_pos = io.MousePos;
|
const auto old_mouse_pos = io.MousePos;
|
||||||
|
if (accept_new_input) {
|
||||||
ImGui_ImplSpice_UpdateMousePos();
|
ImGui_ImplSpice_UpdateMousePos();
|
||||||
|
}
|
||||||
const auto new_mouse_pos = io.MousePos;
|
const auto new_mouse_pos = io.MousePos;
|
||||||
|
|
||||||
// update mouse buttons
|
// update mouse buttons
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "games/io.h"
|
#include "games/io.h"
|
||||||
#include "games/gitadora/gitadora.h"
|
#include "games/gitadora/gitadora.h"
|
||||||
#include "games/iidx/iidx.h"
|
#include "games/iidx/iidx.h"
|
||||||
|
#include "games/popn/popn.h"
|
||||||
#include "hooks/graphics/graphics.h"
|
#include "hooks/graphics/graphics.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "touch/touch.h"
|
#include "touch/touch.h"
|
||||||
@@ -35,6 +36,7 @@
|
|||||||
#include "windows/iopanel_ddr.h"
|
#include "windows/iopanel_ddr.h"
|
||||||
#include "windows/iopanel_gfdm.h"
|
#include "windows/iopanel_gfdm.h"
|
||||||
#include "windows/iopanel_iidx.h"
|
#include "windows/iopanel_iidx.h"
|
||||||
|
#include "windows/popn_sub.h"
|
||||||
#include "windows/sdvx_sub.h"
|
#include "windows/sdvx_sub.h"
|
||||||
#include "windows/keypad.h"
|
#include "windows/keypad.h"
|
||||||
#include "windows/log.h"
|
#include "windows/log.h"
|
||||||
@@ -350,7 +352,7 @@ void overlay::SpiceOverlay::init() {
|
|||||||
this->window_add(window_config = new overlay::windows::Config(this));
|
this->window_add(window_config = new overlay::windows::Config(this));
|
||||||
this->window_add(window_control = new overlay::windows::Control(this));
|
this->window_add(window_control = new overlay::windows::Control(this));
|
||||||
this->window_add(window_log = new overlay::windows::Log(this));
|
this->window_add(window_log = new overlay::windows::Log(this));
|
||||||
#ifdef SPICE64
|
#if SPICE64 && !SPICE_XP
|
||||||
if (avs::game::is_model("LDJ")) {
|
if (avs::game::is_model("LDJ")) {
|
||||||
this->window_add(window_camera = new overlay::windows::CameraControl(this));
|
this->window_add(window_camera = new overlay::windows::CameraControl(this));
|
||||||
}
|
}
|
||||||
@@ -415,6 +417,8 @@ void overlay::SpiceOverlay::init() {
|
|||||||
window_sub = new overlay::windows::SDVXSubScreen(this);
|
window_sub = new overlay::windows::SDVXSubScreen(this);
|
||||||
} else if (games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW) {
|
} else if (games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW) {
|
||||||
window_sub = new overlay::windows::GitaDoraSubScreen(this);
|
window_sub = new overlay::windows::GitaDoraSubScreen(this);
|
||||||
|
} else if (games::popn::is_pikapika_model()) {
|
||||||
|
window_sub = new overlay::windows::PopnSubScreen(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window_sub) {
|
if (window_sub) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "camera_control.h"
|
#include "camera_control.h"
|
||||||
|
|
||||||
#if SPICE64
|
#if SPICE64 && !SPICE_XP
|
||||||
|
|
||||||
#include <games/io.h>
|
#include <games/io.h>
|
||||||
#include <strmif.h>
|
#include <strmif.h>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#if SPICE64
|
#if SPICE64 && !SPICE_XP
|
||||||
|
|
||||||
#include "overlay/window.h"
|
#include "overlay/window.h"
|
||||||
#include <strmif.h>
|
#include <strmif.h>
|
||||||
|
|||||||
@@ -152,8 +152,8 @@ namespace overlay::windows {
|
|||||||
void CardManager::open_card_editor() {
|
void CardManager::open_card_editor() {
|
||||||
if (this->current_card) {
|
if (this->current_card) {
|
||||||
const auto card = this->current_card;
|
const auto card = this->current_card;
|
||||||
strcpy_s(this->name_buffer, std::size(this->name_buffer), card->name.c_str());
|
snprintf(this->name_buffer, std::size(this->name_buffer), "%s", card->name.c_str());
|
||||||
strcpy_s(this->card_buffer, std::size(this->card_buffer), card->id.c_str());
|
snprintf(this->card_buffer, std::size(this->card_buffer), "%s", card->id.c_str());
|
||||||
this->color_buffer[0] = card->color[0];
|
this->color_buffer[0] = card->color[0];
|
||||||
this->color_buffer[1] = card->color[1];
|
this->color_buffer[1] = card->color[1];
|
||||||
this->color_buffer[2] = card->color[2];
|
this->color_buffer[2] = card->color[2];
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "external/imgui/misc/cpp/imgui_stdlib.h"
|
#include "external/imgui/misc/cpp/imgui_stdlib.h"
|
||||||
#include "games/io.h"
|
#include "games/io.h"
|
||||||
#include "games/sdvx/sdvx.h"
|
#include "games/sdvx/sdvx.h"
|
||||||
|
#include "games/popn/popn.h"
|
||||||
#include "avs/core.h"
|
#include "avs/core.h"
|
||||||
#include "avs/ea3.h"
|
#include "avs/ea3.h"
|
||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
@@ -114,13 +115,23 @@ namespace overlay::windows {
|
|||||||
if (cfg::CONFIGURATOR_STANDALONE) {
|
if (cfg::CONFIGURATOR_STANDALONE) {
|
||||||
const auto file_hints = games::get_game_file_hints(game_name);
|
const auto file_hints = games::get_game_file_hints(game_name);
|
||||||
if (file_hints) {
|
if (file_hints) {
|
||||||
for (auto &file_hint : *file_hints) {
|
for (auto &file_hint_list : *file_hints) {
|
||||||
if (fileutils::file_exists(file_hint) ||
|
bool matches_all_files = true;
|
||||||
|
for (auto &file_hint : file_hint_list) {
|
||||||
|
const auto matched =
|
||||||
|
(fileutils::file_exists(file_hint) ||
|
||||||
fileutils::dir_exists(file_hint) ||
|
fileutils::dir_exists(file_hint) ||
|
||||||
fileutils::file_exists(std::filesystem::path("modules") / file_hint) ||
|
fileutils::file_exists(std::filesystem::path("modules") / file_hint) ||
|
||||||
fileutils::file_exists(std::filesystem::path("contents") / file_hint) ||
|
fileutils::file_exists(std::filesystem::path("contents") / file_hint) ||
|
||||||
fileutils::file_exists(MODULE_PATH / file_hint))
|
fileutils::file_exists(MODULE_PATH / file_hint));
|
||||||
{
|
|
||||||
|
if (!matched) {
|
||||||
|
matches_all_files = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches_all_files) {
|
||||||
this->games_selected = games_list.size() - 1;
|
this->games_selected = games_list.size() - 1;
|
||||||
this->games_selected_name = game_name;
|
this->games_selected_name = game_name;
|
||||||
eamuse_set_game(game_name);
|
eamuse_set_game(game_name);
|
||||||
@@ -273,18 +284,8 @@ namespace overlay::windows {
|
|||||||
|
|
||||||
// keypad buttons
|
// keypad buttons
|
||||||
ImGui::TextUnformatted("");
|
ImGui::TextUnformatted("");
|
||||||
if (this->games_selected_name == "Beatmania IIDX") {
|
this->build_keypad_warning();
|
||||||
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("");
|
|
||||||
}
|
|
||||||
auto keypad_buttons = games::get_buttons_keypads(this->games_selected_name);
|
auto keypad_buttons = games::get_buttons_keypads(this->games_selected_name);
|
||||||
auto keypad_count = eamuse_get_game_keypads_name();
|
auto keypad_count = eamuse_get_game_keypads_name();
|
||||||
if (keypad_count == 1) {
|
if (keypad_count == 1) {
|
||||||
@@ -592,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) {
|
void Config::build_buttons(const std::string &name, std::vector<Button> *buttons, int min, int max) {
|
||||||
ImGui::AlignTextToFramePadding();
|
ImGui::AlignTextToFramePadding();
|
||||||
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "%s Buttons", name.c_str());
|
ImGui::TextColored(ImVec4(1.f, 0.7f, 0, 1), "%s Buttons", name.c_str());
|
||||||
@@ -663,7 +692,7 @@ namespace overlay::windows {
|
|||||||
// longest column is probably "Toggle Virtual Keypad P1" in Overlay tab
|
// longest column is probably "Toggle Virtual Keypad P1" in Overlay tab
|
||||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, overlay::apply_scaling(220));
|
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, overlay::apply_scaling(220));
|
||||||
ImGui::TableSetupColumn("Binding", ImGuiTableColumnFlags_WidthStretch);
|
ImGui::TableSetupColumn("Binding", ImGuiTableColumnFlags_WidthStretch);
|
||||||
ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed, overlay::apply_scaling(140));
|
ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed, overlay::apply_scaling(170));
|
||||||
|
|
||||||
// check if empty
|
// check if empty
|
||||||
if (!buttons || buttons->empty()) {
|
if (!buttons || buttons->empty()) {
|
||||||
@@ -903,8 +932,8 @@ namespace overlay::windows {
|
|||||||
|
|
||||||
// naive binding
|
// naive binding
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
std::string naive_string = "Naive " + button_name;
|
std::string naive_string = "Naive/XInput " + button_name;
|
||||||
if (ImGui::Button("Naive")
|
if (ImGui::Button("Naive/XInput")
|
||||||
|| (buttons_many_active && buttons_many_active_section == name && !buttons_bind_active
|
|| (buttons_many_active && buttons_many_active_section == name && !buttons_bind_active
|
||||||
&& alt_index == 0
|
&& alt_index == 0
|
||||||
&& buttons_many_naive && buttons_many_index == button_it
|
&& buttons_many_naive && buttons_many_index == button_it
|
||||||
@@ -924,11 +953,12 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
||||||
ImGui::HelpTooltip(
|
ImGui::HelpTooltip(
|
||||||
"Uses GetAsyncKeyState to check for any keyboard / mouse input. "
|
"Alternative to Bind, using GetAsyncKeyState for KB/Mouse and XInput for controllers.\n\n"
|
||||||
"For best performance, Bind should be preferred, but this can be used when:\n"
|
"This can be used when:\n"
|
||||||
" * you don't care about which device is used\n"
|
" * you want to use XInput for gamepads (for analog triggers)\n"
|
||||||
|
" * you don't care about which keyboard/mouse is used\n"
|
||||||
" * you want to use input remapping or automation software\n"
|
" * you want to use input remapping or automation software\n"
|
||||||
" * if you have NKRO issues with Bind");
|
" * you have NKRO issues with Bind");
|
||||||
}
|
}
|
||||||
|
|
||||||
naive_button_popup(naive_string, button, button_it_max, alt_index);
|
naive_button_popup(naive_string, button, button_it_max, alt_index);
|
||||||
@@ -988,7 +1018,7 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Naive Many
|
// Naive Many
|
||||||
if (ImGui::MenuItem("Naive (many)")) {
|
if (ImGui::MenuItem("Naive/XInput (many)")) {
|
||||||
buttons_many_active = true;
|
buttons_many_active = true;
|
||||||
buttons_many_index = button_it;
|
buttons_many_index = button_it;
|
||||||
buttons_many_naive = true;
|
buttons_many_naive = true;
|
||||||
@@ -1033,6 +1063,7 @@ namespace overlay::windows {
|
|||||||
button->setAnalogType(BAT_NONE);
|
button->setAnalogType(BAT_NONE);
|
||||||
button->setDebounceUp(0.0);
|
button->setDebounceUp(0.0);
|
||||||
button->setDebounceDown(0.0);
|
button->setDebounceDown(0.0);
|
||||||
|
button->setBatThreshold(0);
|
||||||
button->setVelocityThreshold(0);
|
button->setVelocityThreshold(0);
|
||||||
button->setInvert(false);
|
button->setInvert(false);
|
||||||
button->setLastState(GameAPI::Buttons::BUTTON_NOT_PRESSED);
|
button->setLastState(GameAPI::Buttons::BUTTON_NOT_PRESSED);
|
||||||
@@ -1235,6 +1266,7 @@ namespace overlay::windows {
|
|||||||
button->setAnalogType(bat);
|
button->setAnalogType(bat);
|
||||||
button->setDebounceUp(0.0);
|
button->setDebounceUp(0.0);
|
||||||
button->setDebounceDown(0.0);
|
button->setDebounceDown(0.0);
|
||||||
|
button->setBatThreshold(0);
|
||||||
button->setVelocityThreshold(0);
|
button->setVelocityThreshold(0);
|
||||||
::Config::getInstance().updateBinding(
|
::Config::getInstance().updateBinding(
|
||||||
games_list[games_selected], *button,
|
games_list[games_selected], *button,
|
||||||
@@ -1283,6 +1315,7 @@ namespace overlay::windows {
|
|||||||
button->setAnalogType(buffer[0]);
|
button->setAnalogType(buffer[0]);
|
||||||
button->setDebounceUp(0.0);
|
button->setDebounceUp(0.0);
|
||||||
button->setDebounceDown(0.0);
|
button->setDebounceDown(0.0);
|
||||||
|
button->setBatThreshold(0);
|
||||||
button->setVelocityThreshold(0);
|
button->setVelocityThreshold(0);
|
||||||
::Config::getInstance().updateBinding(
|
::Config::getInstance().updateBinding(
|
||||||
games_list[games_selected], *button,
|
games_list[games_selected], *button,
|
||||||
@@ -1330,6 +1363,7 @@ namespace overlay::windows {
|
|||||||
button->setAnalogType(BAT_NONE);
|
button->setAnalogType(BAT_NONE);
|
||||||
button->setDebounceUp(0.0);
|
button->setDebounceUp(0.0);
|
||||||
button->setDebounceDown(0.0);
|
button->setDebounceDown(0.0);
|
||||||
|
button->setBatThreshold(0);
|
||||||
// same idea as setMidiVKey - keep velocity threshold consistent
|
// same idea as setMidiVKey - keep velocity threshold consistent
|
||||||
button->setVelocityThreshold(
|
button->setVelocityThreshold(
|
||||||
device->midiInfo->v2_velocity_threshold[button->getVKey()]);
|
device->midiInfo->v2_velocity_threshold[button->getVKey()]);
|
||||||
@@ -1355,6 +1389,7 @@ namespace overlay::windows {
|
|||||||
button->setAnalogType(BAT_MIDI_CTRL_PRECISION);
|
button->setAnalogType(BAT_MIDI_CTRL_PRECISION);
|
||||||
button->setDebounceUp(0.0);
|
button->setDebounceUp(0.0);
|
||||||
button->setDebounceDown(0.0);
|
button->setDebounceDown(0.0);
|
||||||
|
button->setBatThreshold(0);
|
||||||
button->setVelocityThreshold(0);
|
button->setVelocityThreshold(0);
|
||||||
::Config::getInstance().updateBinding(
|
::Config::getInstance().updateBinding(
|
||||||
games_list[games_selected], *button,
|
games_list[games_selected], *button,
|
||||||
@@ -1381,6 +1416,7 @@ namespace overlay::windows {
|
|||||||
button->setAnalogType(BAT_MIDI_CTRL_SINGLE);
|
button->setAnalogType(BAT_MIDI_CTRL_SINGLE);
|
||||||
button->setDebounceUp(0.0);
|
button->setDebounceUp(0.0);
|
||||||
button->setDebounceDown(0.0);
|
button->setDebounceDown(0.0);
|
||||||
|
button->setBatThreshold(0);
|
||||||
button->setVelocityThreshold(0);
|
button->setVelocityThreshold(0);
|
||||||
::Config::getInstance().updateBinding(
|
::Config::getInstance().updateBinding(
|
||||||
games_list[games_selected], *button,
|
games_list[games_selected], *button,
|
||||||
@@ -1407,6 +1443,7 @@ namespace overlay::windows {
|
|||||||
button->setAnalogType(BAT_MIDI_CTRL_ONOFF);
|
button->setAnalogType(BAT_MIDI_CTRL_ONOFF);
|
||||||
button->setDebounceUp(0.0);
|
button->setDebounceUp(0.0);
|
||||||
button->setDebounceDown(0.0);
|
button->setDebounceDown(0.0);
|
||||||
|
button->setBatThreshold(0);
|
||||||
button->setVelocityThreshold(0);
|
button->setVelocityThreshold(0);
|
||||||
::Config::getInstance().updateBinding(
|
::Config::getInstance().updateBinding(
|
||||||
games_list[games_selected], *button,
|
games_list[games_selected], *button,
|
||||||
@@ -1432,6 +1469,7 @@ namespace overlay::windows {
|
|||||||
button->setAnalogType(BAT_MIDI_PITCH_DOWN);
|
button->setAnalogType(BAT_MIDI_PITCH_DOWN);
|
||||||
button->setDebounceUp(0.0);
|
button->setDebounceUp(0.0);
|
||||||
button->setDebounceDown(0.0);
|
button->setDebounceDown(0.0);
|
||||||
|
button->setBatThreshold(0);
|
||||||
button->setVelocityThreshold(0);
|
button->setVelocityThreshold(0);
|
||||||
::Config::getInstance().updateBinding(
|
::Config::getInstance().updateBinding(
|
||||||
games_list[games_selected], *button,
|
games_list[games_selected], *button,
|
||||||
@@ -1452,6 +1490,7 @@ namespace overlay::windows {
|
|||||||
button->setAnalogType(BAT_MIDI_PITCH_UP);
|
button->setAnalogType(BAT_MIDI_PITCH_UP);
|
||||||
button->setDebounceUp(0.0);
|
button->setDebounceUp(0.0);
|
||||||
button->setDebounceDown(0.0);
|
button->setDebounceDown(0.0);
|
||||||
|
button->setBatThreshold(0);
|
||||||
button->setVelocityThreshold(0);
|
button->setVelocityThreshold(0);
|
||||||
::Config::getInstance().updateBinding(
|
::Config::getInstance().updateBinding(
|
||||||
games_list[games_selected], *button,
|
games_list[games_selected], *button,
|
||||||
@@ -1481,6 +1520,7 @@ namespace overlay::windows {
|
|||||||
button->setAnalogType(BAT_NONE);
|
button->setAnalogType(BAT_NONE);
|
||||||
button->setDebounceUp(0.0);
|
button->setDebounceUp(0.0);
|
||||||
button->setDebounceDown(0.0);
|
button->setDebounceDown(0.0);
|
||||||
|
button->setBatThreshold(0);
|
||||||
button->setVelocityThreshold(0);
|
button->setVelocityThreshold(0);
|
||||||
::Config::getInstance().updateBinding(
|
::Config::getInstance().updateBinding(
|
||||||
games_list[games_selected], *button,
|
games_list[games_selected], *button,
|
||||||
@@ -1545,6 +1585,22 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check xinput
|
||||||
|
if (check_devices) {
|
||||||
|
xinput::XINPUT_NEW_BUTTON xinput;
|
||||||
|
if (RI_MGR->XINPUT_MGR->get_any_button_pressed(xinput)) {
|
||||||
|
button->setDeviceIdentifier(xinput::get_device_desc(xinput.player));
|
||||||
|
button->setVKey(static_cast<uint16_t>(xinput.button));
|
||||||
|
::Config::getInstance().updateBinding(
|
||||||
|
games_list[games_selected], *button, alt_index - 1);
|
||||||
|
inc_buttons_many_index(button_it_max);
|
||||||
|
buttons_bind_active = false;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
check_devices = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check GetAsyncKeyState
|
||||||
if (check_devices) {
|
if (check_devices) {
|
||||||
// get new keyboard state
|
// get new keyboard state
|
||||||
// these are async, and some keys generate multiple vKeys (e.g., VK_SHIFT, VK_LSHIFT)
|
// these are async, and some keys generate multiple vKeys (e.g., VK_SHIFT, VK_LSHIFT)
|
||||||
@@ -1589,6 +1645,7 @@ namespace overlay::windows {
|
|||||||
button->setVKey(vKey);
|
button->setVKey(vKey);
|
||||||
button->setDebounceUp(0.0);
|
button->setDebounceUp(0.0);
|
||||||
button->setDebounceDown(0.0);
|
button->setDebounceDown(0.0);
|
||||||
|
button->setBatThreshold(0);
|
||||||
button->setVelocityThreshold(0);
|
button->setVelocityThreshold(0);
|
||||||
::Config::getInstance().updateBinding(
|
::Config::getInstance().updateBinding(
|
||||||
games_list[games_selected], *button,
|
games_list[games_selected], *button,
|
||||||
@@ -1640,6 +1697,9 @@ namespace overlay::windows {
|
|||||||
button->setDeviceIdentifier(device.name);
|
button->setDeviceIdentifier(device.name);
|
||||||
// reset controls when switching devices
|
// reset controls when switching devices
|
||||||
button->setAnalogType(ButtonAnalogType::BAT_NONE);
|
button->setAnalogType(ButtonAnalogType::BAT_NONE);
|
||||||
|
button->setDebounceUp(0.0);
|
||||||
|
button->setDebounceDown(0.0);
|
||||||
|
button->setBatThreshold(0);
|
||||||
button->setVelocityThreshold(0);
|
button->setVelocityThreshold(0);
|
||||||
button->setVKey(0);
|
button->setVKey(0);
|
||||||
button->setInvert(false);
|
button->setInvert(false);
|
||||||
@@ -2004,11 +2064,48 @@ namespace overlay::windows {
|
|||||||
|
|
||||||
// invert
|
// invert
|
||||||
bool invert = button->getInvert();
|
bool invert = button->getInvert();
|
||||||
if (ImGui::Checkbox("Invert", &invert)) {
|
if (ImGui::Checkbox("Invert Resulting Value", &invert)) {
|
||||||
button->setInvert(invert);
|
button->setInvert(invert);
|
||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bat threshold
|
||||||
|
if (device->type == rawinput::HID &&
|
||||||
|
(button->getAnalogType() == BAT_POSITIVE || button->getAnalogType() == BAT_NEGATIVE)) {
|
||||||
|
int bat_threshold = button->getBatThreshold();
|
||||||
|
|
||||||
|
bool is_active = (bat_threshold > 0);
|
||||||
|
if (ImGui::Checkbox("Custom Analog Threshold", &is_active)) {
|
||||||
|
if (!is_active) {
|
||||||
|
button->setBatThreshold(0);
|
||||||
|
} else if (bat_threshold == 0) {
|
||||||
|
button->setBatThreshold(60);
|
||||||
|
}
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::HelpMarker(
|
||||||
|
"Assuming that 50% is the center, value exceeding 60% triggers the button by default. "
|
||||||
|
"With this option, you can customize the trigger threshold.\n\n"
|
||||||
|
"Setting this to 51% means tiny movements will trigger the button (small deadzone), "
|
||||||
|
"while setting this to 99% means the button will only trigger at maximum value (large deadzone).\n\n"
|
||||||
|
"Setting this to below 50% adjusts the neutral / center position.");
|
||||||
|
|
||||||
|
if (is_active) {
|
||||||
|
if (ImGui::SliderInt(
|
||||||
|
"Analog Threshold",
|
||||||
|
&bat_threshold,
|
||||||
|
1, 99,
|
||||||
|
"%d%%",
|
||||||
|
ImGuiSliderFlags_AlwaysClamp)) {
|
||||||
|
button->setBatThreshold(bat_threshold);
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// state display
|
// state display
|
||||||
ImGui::TextUnformatted("");
|
ImGui::TextUnformatted("");
|
||||||
if (device != nullptr && device->type == rawinput::MIDI) {
|
if (device != nullptr && device->type == rawinput::MIDI) {
|
||||||
@@ -2165,6 +2262,9 @@ namespace overlay::windows {
|
|||||||
case rawinput::MIDI:
|
case rawinput::MIDI:
|
||||||
this->analogs_devices.emplace_back(&device);
|
this->analogs_devices.emplace_back(&device);
|
||||||
break;
|
break;
|
||||||
|
case rawinput::XINPUT_GAMEPAD:
|
||||||
|
this->analogs_devices.emplace_back(&device);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -2289,6 +2389,15 @@ namespace overlay::windows {
|
|||||||
onoff.size());
|
onoff.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case rawinput::XINPUT_GAMEPAD: {
|
||||||
|
for (int i = 0; i < static_cast<int>(xinput::XInputAnalogEnum::COUNT); i++) {
|
||||||
|
const auto v = static_cast<xinput::XInputAnalogEnum>(i);
|
||||||
|
control_names.push_back(xinput::get_analog_string(v));
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -2725,6 +2834,7 @@ namespace overlay::windows {
|
|||||||
case rawinput::PIUIO_DEVICE:
|
case rawinput::PIUIO_DEVICE:
|
||||||
case rawinput::SMX_STAGE:
|
case rawinput::SMX_STAGE:
|
||||||
case rawinput::SMX_DEDICAB:
|
case rawinput::SMX_DEDICAB:
|
||||||
|
case rawinput::XINPUT_GAMEPAD:
|
||||||
this->auto_match_devices.emplace_back(&device);
|
this->auto_match_devices.emplace_back(&device);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -2891,6 +3001,7 @@ namespace overlay::windows {
|
|||||||
case rawinput::PIUIO_DEVICE:
|
case rawinput::PIUIO_DEVICE:
|
||||||
case rawinput::SMX_STAGE:
|
case rawinput::SMX_STAGE:
|
||||||
case rawinput::SMX_DEDICAB:
|
case rawinput::SMX_DEDICAB:
|
||||||
|
case rawinput::XINPUT_GAMEPAD:
|
||||||
this->lights_devices.emplace_back(&device);
|
this->lights_devices.emplace_back(&device);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -3044,6 +3155,15 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case rawinput::XINPUT_GAMEPAD: {
|
||||||
|
|
||||||
|
// add all names of XInput gamepad device
|
||||||
|
for (int i = 0; i < static_cast<int>(xinput::XInputOutputEnum::COUNT); i++) {
|
||||||
|
control_names.emplace_back(
|
||||||
|
xinput::get_output_string(static_cast<xinput::XInputOutputEnum>(i)));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3589,6 +3709,13 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case rawinput::XINPUT_GAMEPAD: {
|
||||||
|
for (int i = 0; i < static_cast<int>(xinput::XInputOutputEnum::COUNT); i++) {
|
||||||
|
raw_names.emplace_back(
|
||||||
|
xinput::get_output_string(static_cast<xinput::XInputOutputEnum>(i)));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -4289,7 +4416,9 @@ namespace overlay::windows {
|
|||||||
ImGui::TextUnformatted("Requires restart! Showing all procs in Group 0.");
|
ImGui::TextUnformatted("Requires restart! Showing all procs in Group 0.");
|
||||||
ImGui::TextUnformatted("");
|
ImGui::TextUnformatted("");
|
||||||
ImGui::TextUnformatted("Pick CPU cores to use:");
|
ImGui::TextUnformatted("Pick CPU cores to use:");
|
||||||
const uint64_t cpu_count = GetActiveProcessorCount(0);
|
SYSTEM_INFO info;
|
||||||
|
GetSystemInfo(&info);
|
||||||
|
const uint64_t cpu_count = info.dwNumberOfProcessors;
|
||||||
uint64_t affinity = 0;
|
uint64_t affinity = 0;
|
||||||
if (!option.value.empty()) {
|
if (!option.value.empty()) {
|
||||||
try {
|
try {
|
||||||
@@ -4481,9 +4610,11 @@ namespace overlay::windows {
|
|||||||
for (const auto &monitor : monitors) {
|
for (const auto &monitor : monitors) {
|
||||||
const bool is_selected = option.value == monitor.display_name;
|
const bool is_selected = option.value == monitor.display_name;
|
||||||
auto friendly = wchar_to_u8(monitor.friendly_name.c_str());
|
auto friendly = wchar_to_u8(monitor.friendly_name.c_str());
|
||||||
if (ImGui::Selectable(
|
auto str = fmt::format("{} ({})", monitor.display_name, friendly);
|
||||||
fmt::format("{} ({})", monitor.display_name, friendly).c_str(),
|
if (monitor.is_primary) {
|
||||||
is_selected)) {
|
str += " [Primary]";
|
||||||
|
}
|
||||||
|
if (ImGui::Selectable(str.c_str(), is_selected)) {
|
||||||
option.value = monitor.display_name;
|
option.value = monitor.display_name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4580,7 +4711,7 @@ namespace overlay::windows {
|
|||||||
if (definition.category.empty()) {
|
if (definition.category.empty()) {
|
||||||
ImGui::TextDisabled("-");
|
ImGui::TextDisabled("-");
|
||||||
} else {
|
} else {
|
||||||
ImGui::TextDisabled(definition.category.c_str());
|
ImGui::TextDisabled("%s", definition.category.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4872,12 +5003,24 @@ namespace overlay::windows {
|
|||||||
// check if empty
|
// check if empty
|
||||||
if (options_count == 0) {
|
if (options_count == 0) {
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
|
|
||||||
|
// option category
|
||||||
|
if (filter != nullptr) {
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::TextDisabled("-");
|
||||||
|
}
|
||||||
|
|
||||||
|
// name of option
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
if (filter == nullptr) {
|
||||||
ImGui::Indent(INDENT);
|
ImGui::Indent(INDENT);
|
||||||
|
}
|
||||||
ImGui::TextDisabled("-");
|
ImGui::TextDisabled("-");
|
||||||
|
if (filter == nullptr) {
|
||||||
ImGui::Unindent(INDENT);
|
ImGui::Unindent(INDENT);
|
||||||
ImGui::TableNextColumn();
|
}
|
||||||
ImGui::TextDisabled("-");
|
|
||||||
|
// widget
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::TextDisabled("-");
|
ImGui::TextDisabled("-");
|
||||||
}
|
}
|
||||||
@@ -4888,14 +5031,19 @@ namespace overlay::windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Config::build_about() {
|
void Config::build_about() {
|
||||||
#ifndef SPICE_LINUX
|
#if SPICE_LINUX
|
||||||
ImGui::TextUnformatted(std::string(
|
ImGui::TextUnformatted(std::string(
|
||||||
"spice2x (a fork of SpiceTools)\r\n"
|
"spice2x (a fork of SpiceTools) for Linux\r\n"
|
||||||
|
"=========================\r\n" +
|
||||||
|
to_string(VERSION_STRING)).c_str());
|
||||||
|
#elif SPICE_XP
|
||||||
|
ImGui::TextUnformatted(std::string(
|
||||||
|
"spice2x (a fork of SpiceTools) for WinXP\r\n"
|
||||||
"=========================\r\n" +
|
"=========================\r\n" +
|
||||||
to_string(VERSION_STRING)).c_str());
|
to_string(VERSION_STRING)).c_str());
|
||||||
#else
|
#else
|
||||||
ImGui::TextUnformatted(std::string(
|
ImGui::TextUnformatted(std::string(
|
||||||
"spice2x (a fork of SpiceTools) for Linux\r\n"
|
"spice2x (a fork of SpiceTools)\r\n"
|
||||||
"=========================\r\n" +
|
"=========================\r\n" +
|
||||||
to_string(VERSION_STRING)).c_str());
|
to_string(VERSION_STRING)).c_str());
|
||||||
#endif
|
#endif
|
||||||
@@ -5186,6 +5334,7 @@ namespace overlay::windows {
|
|||||||
btn.setInvert(entry->invert);
|
btn.setInvert(entry->invert);
|
||||||
btn.setDebounceUp(entry->debounce_up);
|
btn.setDebounceUp(entry->debounce_up);
|
||||||
btn.setDebounceDown(entry->debounce_down);
|
btn.setDebounceDown(entry->debounce_down);
|
||||||
|
btn.setBatThreshold(entry->bat_threshold);
|
||||||
btn.setVelocityThreshold(entry->velocity_threshold);
|
btn.setVelocityThreshold(entry->velocity_threshold);
|
||||||
::Config::getInstance().updateBinding(game, btn, -1);
|
::Config::getInstance().updateBinding(game, btn, -1);
|
||||||
} else {
|
} else {
|
||||||
@@ -5196,6 +5345,7 @@ namespace overlay::windows {
|
|||||||
alt_btn.setInvert(entry->invert);
|
alt_btn.setInvert(entry->invert);
|
||||||
alt_btn.setDebounceUp(entry->debounce_up);
|
alt_btn.setDebounceUp(entry->debounce_up);
|
||||||
alt_btn.setDebounceDown(entry->debounce_down);
|
alt_btn.setDebounceDown(entry->debounce_down);
|
||||||
|
alt_btn.setBatThreshold(entry->bat_threshold);
|
||||||
alt_btn.setVelocityThreshold(entry->velocity_threshold);
|
alt_btn.setVelocityThreshold(entry->velocity_threshold);
|
||||||
alt_btn.setTemporary(true);
|
alt_btn.setTemporary(true);
|
||||||
btn.getAlternatives().push_back(alt_btn);
|
btn.getAlternatives().push_back(alt_btn);
|
||||||
|
|||||||
@@ -193,6 +193,8 @@ namespace overlay::windows {
|
|||||||
std::vector<Option> *options, const std::string &category, const std::string *filter=nullptr);
|
std::vector<Option> *options, const std::string &category, const std::string *filter=nullptr);
|
||||||
void build_about();
|
void build_about();
|
||||||
void build_launcher();
|
void build_launcher();
|
||||||
|
void build_keypad_warning();
|
||||||
|
|
||||||
void launch_shell(LPCSTR app, LPCSTR file=nullptr);
|
void launch_shell(LPCSTR app, LPCSTR file=nullptr);
|
||||||
|
|
||||||
void build_menu(int *game_selected);
|
void build_menu(int *game_selected);
|
||||||
|
|||||||
@@ -628,13 +628,12 @@ namespace overlay::windows {
|
|||||||
ImGui::Text("Wheel: %ld", mouse->pos_wheel);
|
ImGui::Text("Wheel: %ld", mouse->pos_wheel);
|
||||||
|
|
||||||
// keys
|
// keys
|
||||||
std::stringstream keys;
|
std::string keys = "[";
|
||||||
keys << "[";
|
|
||||||
for (auto key : mouse->key_states) {
|
for (auto key : mouse->key_states) {
|
||||||
keys << (key ? "1," : "0,");
|
keys += (key ? "1," : "0,");
|
||||||
}
|
}
|
||||||
keys << "]";
|
keys += "]";
|
||||||
ImGui::Text("Keys: %s", keys.str().c_str());
|
ImGui::Text("Keys: %s", keys.c_str());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -643,15 +642,14 @@ namespace overlay::windows {
|
|||||||
ImGui::Text("Type: Keyboard");
|
ImGui::Text("Type: Keyboard");
|
||||||
|
|
||||||
// keys
|
// keys
|
||||||
std::stringstream keys;
|
std::string keys = "[";
|
||||||
keys << "[";
|
|
||||||
for (size_t i = 0; i < std::size(keyboard->key_states); i++) {
|
for (size_t i = 0; i < std::size(keyboard->key_states); i++) {
|
||||||
if (keyboard->key_states[i]) {
|
if (keyboard->key_states[i]) {
|
||||||
keys << i << ",";
|
keys += std::to_string(i) + ",";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
keys << "]";
|
keys += "]";
|
||||||
ImGui::Text("Keys: %s", keys.str().c_str());
|
ImGui::Text("Keys: %s", keys.c_str());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -732,6 +730,10 @@ namespace overlay::windows {
|
|||||||
ImGui::Text("Type: PIUIO");
|
ImGui::Text("Type: PIUIO");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case rawinput::XINPUT_GAMEPAD: {
|
||||||
|
ImGui::Text("Type: XInput");
|
||||||
|
break;
|
||||||
|
}
|
||||||
case rawinput::DESTROYED: {
|
case rawinput::DESTROYED: {
|
||||||
ImGui::Text("Disconnected.");
|
ImGui::Text("Disconnected.");
|
||||||
break;
|
break;
|
||||||
@@ -739,6 +741,7 @@ namespace overlay::windows {
|
|||||||
case rawinput::UNKNOWN:
|
case rawinput::UNKNOWN:
|
||||||
default:
|
default:
|
||||||
ImGui::Text("Type: Unknown");
|
ImGui::Text("Type: Unknown");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ namespace overlay::windows {
|
|||||||
bool invert = false;
|
bool invert = false;
|
||||||
double debounce_up = 0.0;
|
double debounce_up = 0.0;
|
||||||
double debounce_down = 0.0;
|
double debounce_down = 0.0;
|
||||||
|
int bat_threshold = 0;
|
||||||
unsigned short velocity_threshold = 0;
|
unsigned short velocity_threshold = 0;
|
||||||
|
|
||||||
bool is_naive() const { return device_identifier.empty() && vKey != INVALID_VKEY; }
|
bool is_naive() const { return device_identifier.empty() && vKey != INVALID_VKEY; }
|
||||||
@@ -31,6 +32,7 @@ namespace overlay::windows {
|
|||||||
e.invert = btn.getInvert();
|
e.invert = btn.getInvert();
|
||||||
e.debounce_up = btn.getDebounceUp();
|
e.debounce_up = btn.getDebounceUp();
|
||||||
e.debounce_down = btn.getDebounceDown();
|
e.debounce_down = btn.getDebounceDown();
|
||||||
|
e.bat_threshold = btn.getBatThreshold();
|
||||||
e.velocity_threshold = btn.getVelocityThreshold();
|
e.velocity_threshold = btn.getVelocityThreshold();
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "games/iidx/iidx.h"
|
#include "games/iidx/iidx.h"
|
||||||
#include "games/gitadora/gitadora.h"
|
#include "games/gitadora/gitadora.h"
|
||||||
|
#include "games/popn/popn.h"
|
||||||
|
|
||||||
namespace overlay::windows {
|
namespace overlay::windows {
|
||||||
|
|
||||||
@@ -72,6 +73,8 @@ namespace overlay::windows {
|
|||||||
sub = "Show DRS Dance Floor";
|
sub = "Show DRS Dance Floor";
|
||||||
} else if (games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW) {
|
} else if (games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW) {
|
||||||
sub = "Show GITADORA Subscreen";
|
sub = "Show GITADORA Subscreen";
|
||||||
|
} else if (games::popn::is_pikapika_model()) {
|
||||||
|
sub = "Show Pop'n Subscreen";
|
||||||
}
|
}
|
||||||
|
|
||||||
build_button(this->overlay->window_sub, sub, size, NextItem::NEW_LINE, false);
|
build_button(this->overlay->window_sub, sub, size, NextItem::NEW_LINE, false);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
#include "games/iidx/iidx.h"
|
#include "games/iidx/iidx.h"
|
||||||
|
#include "games/popn/popn.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "overlay/imgui/extensions.h"
|
#include "overlay/imgui/extensions.h"
|
||||||
@@ -42,6 +43,8 @@ namespace overlay::windows {
|
|||||||
void Keypad::build_content() {
|
void Keypad::build_content() {
|
||||||
if (avs::game::is_model("LDJ") && games::iidx::TDJ_MODE) {
|
if (avs::game::is_model("LDJ") && games::iidx::TDJ_MODE) {
|
||||||
build_tdj_keypad();
|
build_tdj_keypad();
|
||||||
|
} else if (games::popn::is_pikapika_model()) {
|
||||||
|
build_popn_pika_keypad();
|
||||||
} else {
|
} else {
|
||||||
build_keypad();
|
build_keypad();
|
||||||
}
|
}
|
||||||
@@ -132,4 +135,26 @@ namespace overlay::windows {
|
|||||||
eamuse_set_keypad_overrides_overlay(this->unit, 0);
|
eamuse_set_keypad_overrides_overlay(this->unit, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Keypad::build_popn_pika_keypad() {
|
||||||
|
ImGui::AlignTextToFramePadding();
|
||||||
|
ImGui::TextDisabled("Keypad disabled in Pop'n Pikapika model!\nUse subscreen overlay.");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::WarnMarker(
|
||||||
|
nullptr,
|
||||||
|
"Pop'n Music Pikapika cabinets do not have any keypads; they use the subscreen.\n\n"
|
||||||
|
"Fullscreen mode: bind a key in Overlay tab, and press it in game to show the subscreen, "
|
||||||
|
"then use your mouse to click. Page Up button is the default binding.\n\n"
|
||||||
|
"Windowed mode: look for the second window in the taskbar (or ALT+TAB).\n\n"
|
||||||
|
"Windowed mode with -popnnosub: bring up the subscreen overlay (default Page Up).\n\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
ImGui::Spacing();
|
||||||
|
|
||||||
|
if (ImGui::Button("Insert Card")) {
|
||||||
|
eamuse_set_keypad_overrides_overlay(this->unit, 1 << EAM_IO_INSERT);
|
||||||
|
} else {
|
||||||
|
eamuse_set_keypad_overrides_overlay(this->unit, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ namespace overlay::windows {
|
|||||||
size_t unit = 0;
|
size_t unit = 0;
|
||||||
void build_keypad();
|
void build_keypad();
|
||||||
void build_tdj_keypad();
|
void build_tdj_keypad();
|
||||||
|
void build_popn_pika_keypad();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
#undef CINTERFACE
|
||||||
|
|
||||||
|
#include "avs/game.h"
|
||||||
|
#include "popn_sub.h"
|
||||||
|
#include "games/popn/popn.h"
|
||||||
|
#include "hooks/graphics/graphics.h"
|
||||||
|
#include "touch/touch.h"
|
||||||
|
|
||||||
|
namespace overlay::windows {
|
||||||
|
|
||||||
|
PopnSubScreen::PopnSubScreen(SpiceOverlay *overlay) : GenericSubScreen(overlay) {
|
||||||
|
this->title = "Pop'n Subscreen";
|
||||||
|
|
||||||
|
if (!games::popn::is_pikapika_model()) {
|
||||||
|
this->disabled_message = "Game did not launch as Pikapika Pop-kun (invalid <spec>)!";
|
||||||
|
} else if (games::popn::SHOW_PIKA_MONITOR_WARNING) {
|
||||||
|
this->disabled_message = "Subscreen overlay is not compatible with -dxmainadapter option, use -mainmonitor instead";
|
||||||
|
} else if (GRAPHICS_WINDOWED && !GRAPHICS_PREVENT_SECONDARY_WINDOW) {
|
||||||
|
this->disabled_message = "Subscren overlay was not enabled in spicecfg. Use the second window (ALT+TAB).";
|
||||||
|
}
|
||||||
|
|
||||||
|
this->resize_callback = keep_16_by_10;
|
||||||
|
float size = 0.5f;
|
||||||
|
this->init_size = ImVec2(
|
||||||
|
ImGui::GetIO().DisplaySize.x * size,
|
||||||
|
(ImGui::GetIO().DisplaySize.x * size * 10 / 16) + ImGui::GetFrameHeight());
|
||||||
|
|
||||||
|
this->size_max = ImVec2(
|
||||||
|
ImGui::GetIO().DisplaySize.x - ImGui::GetFrameHeight() * 2,
|
||||||
|
ImGui::GetIO().DisplaySize.y - ImGui::GetFrameHeight() * 2);
|
||||||
|
|
||||||
|
// middle / bottom
|
||||||
|
this->init_pos = ImVec2(
|
||||||
|
ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2,
|
||||||
|
ImGui::GetIO().DisplaySize.y - this->init_size.y - (ImGui::GetFrameHeight() / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PopnSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {
|
||||||
|
if (!this->get_active()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GRAPHICS_WINDOWED) {
|
||||||
|
// Touch needs to be registered on global coords
|
||||||
|
*x_out = SPICETOUCH_TOUCH_X + xy_in.x * SPICETOUCH_TOUCH_WIDTH;
|
||||||
|
*y_out = SPICETOUCH_TOUCH_Y + xy_in.y * SPICETOUCH_TOUCH_HEIGHT;
|
||||||
|
} else {
|
||||||
|
// Fullscreen mode, scale to game coords
|
||||||
|
*x_out = xy_in.x * ImGui::GetIO().DisplaySize.x;
|
||||||
|
*y_out = xy_in.y * ImGui::GetIO().DisplaySize.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "overlay/window.h"
|
||||||
|
#include "overlay/windows/generic_sub.h"
|
||||||
|
|
||||||
|
namespace overlay::windows {
|
||||||
|
|
||||||
|
class PopnSubScreen : public GenericSubScreen {
|
||||||
|
public:
|
||||||
|
PopnSubScreen(SpiceOverlay *overlay);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void keep_16_by_10(ImGuiSizeCallbackData* data) {
|
||||||
|
data->DesiredSize.y = (data->DesiredSize.x * 10.f / 16.f) + ImGui::GetFrameHeight();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -213,7 +213,7 @@ namespace overlay::windows {
|
|||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::HelpMarker(
|
ImGui::HelpMarker(
|
||||||
"Change window decoration. Resizable Window may not cause your mouse cursor to change, "
|
"Change window decoration. Resizable Window may not cause your mouse cursor to change, "
|
||||||
"but you can still drag to resize. Disabled for some games due to incompatibility.");
|
"but you can still drag to resize. Disabled for some games due to incompatibility; change in spicecfg instead.");
|
||||||
|
|
||||||
if (ImGui::Checkbox("Always On Top", &cfg::SCREENRESIZE->window_always_on_top) ) {
|
if (ImGui::Checkbox("Always On Top", &cfg::SCREENRESIZE->window_always_on_top) ) {
|
||||||
graphics_update_z_order(window, cfg::SCREENRESIZE->window_always_on_top);
|
graphics_update_z_order(window, cfg::SCREENRESIZE->window_always_on_top);
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ namespace rawinput {
|
|||||||
SEXTET_OUTPUT,
|
SEXTET_OUTPUT,
|
||||||
PIUIO_DEVICE,
|
PIUIO_DEVICE,
|
||||||
SMX_STAGE,
|
SMX_STAGE,
|
||||||
SMX_DEDICAB
|
SMX_DEDICAB,
|
||||||
|
XINPUT_GAMEPAD,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum MouseKeys {
|
enum MouseKeys {
|
||||||
|
|||||||
@@ -119,8 +119,10 @@ namespace rawinput {
|
|||||||
// success
|
// success
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
case 7: { // windows 10 reports this for MIDI?
|
case DBT_DEVNODES_CHANGED: {
|
||||||
|
// TODO: this can be a little noisy as it gets called on every device
|
||||||
this->ri_mgr->devices_scan_midi();
|
this->ri_mgr->devices_scan_midi();
|
||||||
|
this->ri_mgr->devices_scan_xinput();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <setupapi.h>
|
#include <setupapi.h>
|
||||||
|
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "external/robin_hood.h"
|
||||||
#include "util/time.h"
|
#include "util/time.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
@@ -70,6 +71,8 @@ void rawinput::set_midi_algorithm(rawinput::MidiNoteAlgorithm new_algo) {
|
|||||||
|
|
||||||
rawinput::RawInputManager::RawInputManager() {
|
rawinput::RawInputManager::RawInputManager() {
|
||||||
|
|
||||||
|
XINPUT_MGR = std::make_unique<xinput::XInputManager>();
|
||||||
|
|
||||||
// create input window and load in devices
|
// create input window and load in devices
|
||||||
this->input_hwnd_create();
|
this->input_hwnd_create();
|
||||||
this->devices_reload();
|
this->devices_reload();
|
||||||
@@ -106,6 +109,8 @@ void rawinput::RawInputManager::stop() {
|
|||||||
// destruct all devices and input window
|
// destruct all devices and input window
|
||||||
this->devices_destruct();
|
this->devices_destruct();
|
||||||
this->input_hwnd_destroy();
|
this->input_hwnd_destroy();
|
||||||
|
|
||||||
|
XINPUT_MGR.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void rawinput::RawInputManager::input_hwnd_create() {
|
void rawinput::RawInputManager::input_hwnd_create() {
|
||||||
@@ -191,6 +196,8 @@ void rawinput::RawInputManager::devices_reload() {
|
|||||||
this->devices_scan_smxdedicab();
|
this->devices_scan_smxdedicab();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->devices_scan_xinput();
|
||||||
|
|
||||||
// check for LIT Board
|
// check for LIT Board
|
||||||
sextet_register("COM54", "LIT Board", false);
|
sextet_register("COM54", "LIT Board", false);
|
||||||
|
|
||||||
@@ -598,16 +605,6 @@ void rawinput::RawInputManager::devices_scan_rawinput(RAWINPUTDEVICELIST *device
|
|||||||
value_caps.LogicalMax = 255;
|
value_caps.LogicalMax = 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fix min and max values
|
|
||||||
if (value_caps.BitSize > 0 && value_caps.BitSize <= sizeof(value_caps.LogicalMin) * 8) {
|
|
||||||
auto shift_size = sizeof(value_caps.LogicalMin) * 8 - value_caps.BitSize + 1;
|
|
||||||
auto mask = ((uint64_t) 1 << value_caps.BitSize) - 1;
|
|
||||||
value_caps.LogicalMin &= mask;
|
|
||||||
value_caps.LogicalMin <<= shift_size;
|
|
||||||
value_caps.LogicalMin >>= shift_size;
|
|
||||||
value_caps.LogicalMax &= mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fix up hat switch to initially report as neutral position
|
// fix up hat switch to initially report as neutral position
|
||||||
if (value_caps.UsagePage == 0x1 && value_caps.Range.UsageMin == 0x39) {
|
if (value_caps.UsagePage == 0x1 && value_caps.Range.UsageMin == 0x39) {
|
||||||
value_states[value_cap_num] = -1.f;
|
value_states[value_cap_num] = -1.f;
|
||||||
@@ -931,6 +928,7 @@ void rawinput::RawInputManager::devices_scan_piuio() {
|
|||||||
|
|
||||||
// add device to vector first so pointer is valid
|
// add device to vector first so pointer is valid
|
||||||
auto *new_piuio_device = new Device();
|
auto *new_piuio_device = new Device();
|
||||||
|
new_piuio_device->id = this->devices.size() + 1;
|
||||||
new_piuio_device->type = PIUIO_DEVICE;
|
new_piuio_device->type = PIUIO_DEVICE;
|
||||||
new_piuio_device->name = "piuio";
|
new_piuio_device->name = "piuio";
|
||||||
new_piuio_device->desc = "PIUIO";
|
new_piuio_device->desc = "PIUIO";
|
||||||
@@ -961,6 +959,7 @@ void rawinput::RawInputManager::devices_scan_smxstage() {
|
|||||||
log_misc("rawinput", "scan SMX Stage devices...");
|
log_misc("rawinput", "scan SMX Stage devices...");
|
||||||
|
|
||||||
auto *new_smxstage_device = new Device();
|
auto *new_smxstage_device = new Device();
|
||||||
|
new_smxstage_device->id = this->devices.size() + 1;
|
||||||
new_smxstage_device->type = SMX_STAGE;
|
new_smxstage_device->type = SMX_STAGE;
|
||||||
new_smxstage_device->name = "smxstage";
|
new_smxstage_device->name = "smxstage";
|
||||||
new_smxstage_device->desc = "SMX Stage";
|
new_smxstage_device->desc = "SMX Stage";
|
||||||
@@ -987,6 +986,7 @@ void rawinput::RawInputManager::devices_scan_smxdedicab() {
|
|||||||
log_misc("rawinput", "scan SMX Dedicated Cabinet devices...");
|
log_misc("rawinput", "scan SMX Dedicated Cabinet devices...");
|
||||||
|
|
||||||
auto *new_smxdedicab_device = new Device();
|
auto *new_smxdedicab_device = new Device();
|
||||||
|
new_smxdedicab_device->id = this->devices.size() + 1;
|
||||||
new_smxdedicab_device->type = SMX_DEDICAB;
|
new_smxdedicab_device->type = SMX_DEDICAB;
|
||||||
new_smxdedicab_device->name = "smxdedicab";
|
new_smxdedicab_device->name = "smxdedicab";
|
||||||
new_smxdedicab_device->desc = "SMX Dedicated Cabinet";
|
new_smxdedicab_device->desc = "SMX Dedicated Cabinet";
|
||||||
@@ -1009,6 +1009,76 @@ void rawinput::RawInputManager::devices_scan_smxdedicab() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rawinput::RawInputManager::devices_scan_xinput() {
|
||||||
|
log_misc("rawinput", "scan XInput devices...");
|
||||||
|
|
||||||
|
const auto connected_players = XINPUT_MGR->get_available_players();
|
||||||
|
|
||||||
|
// first, destroy missing devices
|
||||||
|
std::vector<std::string> devices_to_remove;
|
||||||
|
for (auto &device : this->devices) {
|
||||||
|
if (device.type != XINPUT_GAMEPAD) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const uint8_t player = static_cast<uint8_t>(reinterpret_cast<uintptr_t>(device.handle));
|
||||||
|
if (std::find(connected_players.begin(), connected_players.end(), player) == connected_players.end()) {
|
||||||
|
devices_to_remove.push_back(device.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto &name : devices_to_remove) {
|
||||||
|
this->devices_remove(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto create_device = [](const uint8_t player) -> Device {
|
||||||
|
Device device = {};
|
||||||
|
device.type = XINPUT_GAMEPAD;
|
||||||
|
device.name = xinput::get_device_desc(player);
|
||||||
|
device.desc = fmt::format("XInput Gamepad P{}", player + 1);
|
||||||
|
device.handle = reinterpret_cast<HANDLE>(player);
|
||||||
|
device.mutex = new std::mutex();
|
||||||
|
device.mutex_out = new std::mutex();
|
||||||
|
return device;
|
||||||
|
};
|
||||||
|
|
||||||
|
// add new devices
|
||||||
|
for (const auto player : connected_players) {
|
||||||
|
bool duplicate_found = false;
|
||||||
|
|
||||||
|
// check for duplicates first
|
||||||
|
for (auto &prev_device : this->devices) {
|
||||||
|
if (prev_device.name != xinput::get_device_desc(player)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (prev_device.type == DESTROYED) {
|
||||||
|
log_info("rawinput", "overwriting previously destroyed XInput device: {}", prev_device.name);
|
||||||
|
const auto old_id = prev_device.id;
|
||||||
|
prev_device = create_device(player);
|
||||||
|
prev_device.id = old_id;
|
||||||
|
|
||||||
|
// notify change
|
||||||
|
for (auto &cb : this->callback_change) {
|
||||||
|
cb.f(cb.data, &prev_device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
duplicate_found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!duplicate_found) {
|
||||||
|
// add new device
|
||||||
|
log_info("rawinput", "adding new XInput device: player {}", player + 1);
|
||||||
|
auto new_xinput_device = create_device(player);
|
||||||
|
new_xinput_device.id = this->devices.size() + 1;
|
||||||
|
auto &device = this->devices.emplace_back(new_xinput_device);
|
||||||
|
|
||||||
|
// notify add
|
||||||
|
for (auto &cb : this->callback_add) {
|
||||||
|
cb.f(cb.data, &device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void rawinput::RawInputManager::flush_start() {
|
void rawinput::RawInputManager::flush_start() {
|
||||||
|
|
||||||
// start flush thread
|
// start flush thread
|
||||||
@@ -1834,15 +1904,6 @@ LRESULT CALLBACK rawinput::RawInputManager::input_wnd_proc(
|
|||||||
LONG value_min = value_caps.LogicalMin;
|
LONG value_min = value_caps.LogicalMin;
|
||||||
LONG value_max = value_caps.LogicalMax;
|
LONG value_max = value_caps.LogicalMax;
|
||||||
|
|
||||||
// fix sign bits for signed values
|
|
||||||
if (value_caps.LogicalMin < 0 &&
|
|
||||||
value_caps.BitSize > 0 &&
|
|
||||||
value_caps.BitSize <= sizeof(value_caps.LogicalMin) * 8) {
|
|
||||||
auto shift_size = sizeof(value_caps.LogicalMin) * 8 - value_caps.BitSize + 1;
|
|
||||||
value_raw <<= shift_size;
|
|
||||||
value_raw >>= shift_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
float value;
|
float value;
|
||||||
// 0x1 == generic desktop, 0x39 == hat switch
|
// 0x1 == generic desktop, 0x39 == hat switch
|
||||||
if (value_caps.UsagePage == 0x1 && value_caps.Range.UsageMin == 0x39) {
|
if (value_caps.UsagePage == 0x1 && value_caps.Range.UsageMin == 0x39) {
|
||||||
@@ -1855,6 +1916,16 @@ LRESULT CALLBACK rawinput::RawInputManager::input_wnd_proc(
|
|||||||
value = -1.f;
|
value = -1.f;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
// fix sign bits for signed values
|
||||||
|
if (value_caps.LogicalMin < 0 &&
|
||||||
|
0 < value_caps.BitSize && value_caps.BitSize < 32) {
|
||||||
|
|
||||||
|
ULONG raw = static_cast<ULONG>(value_raw) & ((1u << value_caps.BitSize) - 1u);
|
||||||
|
const ULONG sign_bit = 1u << (value_caps.BitSize - 1);
|
||||||
|
value_raw = static_cast<LONG>((raw ^ sign_bit) - sign_bit);
|
||||||
|
}
|
||||||
|
|
||||||
// automatic calibration
|
// automatic calibration
|
||||||
if (value_raw < value_min) {
|
if (value_raw < value_min) {
|
||||||
value_caps.LogicalMin = value_raw;
|
value_caps.LogicalMin = value_raw;
|
||||||
@@ -2567,6 +2638,10 @@ void rawinput::RawInputManager::device_write_output(Device *device, bool only_up
|
|||||||
device->smxdedicabInfo->Update();
|
device->smxdedicabInfo->Update();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case XINPUT_GAMEPAD: {
|
||||||
|
// nothing - updates to these are instant
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2734,6 +2809,10 @@ void rawinput::RawInputManager::devices_print() {
|
|||||||
log_misc("rawinput", "device type: SMX_DEDICAB");
|
log_misc("rawinput", "device type: SMX_DEDICAB");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case XINPUT_GAMEPAD: {
|
||||||
|
log_misc("rawinput", "device type: XINPUT");
|
||||||
|
break;
|
||||||
|
}
|
||||||
case UNKNOWN:
|
case UNKNOWN:
|
||||||
default:
|
default:
|
||||||
log_warning("rawinput", "device type: UNKNOWN");
|
log_warning("rawinput", "device type: UNKNOWN");
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "hotplug.h"
|
#include "hotplug.h"
|
||||||
|
#include "rawinput/xinput.h"
|
||||||
#include "util/scope_guard.h"
|
#include "util/scope_guard.h"
|
||||||
|
|
||||||
namespace rawinput {
|
namespace rawinput {
|
||||||
@@ -101,6 +102,7 @@ namespace rawinput {
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
HWND input_hwnd = nullptr;
|
HWND input_hwnd = nullptr;
|
||||||
|
std::unique_ptr<xinput::XInputManager> XINPUT_MGR = nullptr;
|
||||||
|
|
||||||
RawInputManager();
|
RawInputManager();
|
||||||
~RawInputManager();
|
~RawInputManager();
|
||||||
@@ -114,6 +116,7 @@ namespace rawinput {
|
|||||||
|
|
||||||
void devices_scan_rawinput(const std::string &device_name = "");
|
void devices_scan_rawinput(const std::string &device_name = "");
|
||||||
void devices_scan_midi();
|
void devices_scan_midi();
|
||||||
|
void devices_scan_xinput();
|
||||||
void devices_remove(const std::string &name);
|
void devices_remove(const std::string &name);
|
||||||
|
|
||||||
void devices_register();
|
void devices_register();
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ namespace rawinput::touch {
|
|||||||
|
|
||||||
// check type
|
// check type
|
||||||
if (device->type != HID) {
|
if (device->type != HID) {
|
||||||
log_fatal("rawinput", "touch update called on non HID device");
|
log_warning("rawinput", "touch update called on non HID device");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,8 +192,10 @@ namespace rawinput::touch {
|
|||||||
contact_count = std::max(contact_count, (size_t) hid->value_states_raw[index]);
|
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
|
// hybrid mode devices will report a contact count of 0 for subsequent reports that are
|
||||||
// part of the same initial frame
|
// 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 > 0) {
|
||||||
if (contact_count > touch_report_count) {
|
if (contact_count > touch_report_count) {
|
||||||
touch.remaining_contact_count = 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());
|
touch_points.reserve(touch.elements_x.size());
|
||||||
for (size_t i = 0; i < touch.elements_x.size(); i++) {
|
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
|
// build touch point
|
||||||
HIDTouchPoint hid_tp{};
|
HIDTouchPoint hid_tp{};
|
||||||
auto pos_x = hid->value_states[touch.elements_x[i]];
|
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;
|
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)
|
// generate ID (hopefully unique)
|
||||||
hid_tp.id = (DWORD) hid->value_states_raw[touch.elements_contact_identifier[i]];
|
hid_tp.id = (DWORD) hid->value_states_raw[touch.elements_contact_identifier[i]];
|
||||||
hid_tp.id += (DWORD) (0xFFFFFF + device->id * 512);
|
hid_tp.id += (DWORD) (0xFFFFFF + device->id * 512);
|
||||||
|
|||||||
@@ -0,0 +1,407 @@
|
|||||||
|
#include "rawinput/xinput.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
|
||||||
|
namespace xinput {
|
||||||
|
|
||||||
|
// this is all we need to emulate xinput.h which we avoid including here...
|
||||||
|
|
||||||
|
#define XINPUT_GAMEPAD_TRIGGER_THRESHOLD (30 / 255.0f)
|
||||||
|
#define XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE 7849
|
||||||
|
#define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689
|
||||||
|
|
||||||
|
#define XINPUT_GAMEPAD_DPAD_UP 0x0001
|
||||||
|
#define XINPUT_GAMEPAD_DPAD_DOWN 0x0002
|
||||||
|
#define XINPUT_GAMEPAD_DPAD_LEFT 0x0004
|
||||||
|
#define XINPUT_GAMEPAD_DPAD_RIGHT 0x0008
|
||||||
|
#define XINPUT_GAMEPAD_START 0x0010
|
||||||
|
#define XINPUT_GAMEPAD_BACK 0x0020
|
||||||
|
#define XINPUT_GAMEPAD_LEFT_THUMB 0x0040
|
||||||
|
#define XINPUT_GAMEPAD_RIGHT_THUMB 0x0080
|
||||||
|
#define XINPUT_GAMEPAD_LEFT_SHOULDER 0x0100
|
||||||
|
#define XINPUT_GAMEPAD_RIGHT_SHOULDER 0x0200
|
||||||
|
#define XINPUT_GAMEPAD_A 0x1000
|
||||||
|
#define XINPUT_GAMEPAD_B 0x2000
|
||||||
|
#define XINPUT_GAMEPAD_X 0x4000
|
||||||
|
#define XINPUT_GAMEPAD_Y 0x8000
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t dwPacketNumber;
|
||||||
|
XINPUT_GAMEPAD_STATE Gamepad;
|
||||||
|
} XINPUT_STATE;
|
||||||
|
|
||||||
|
DWORD
|
||||||
|
WINAPI
|
||||||
|
XInputGetState(
|
||||||
|
DWORD dwUserIndex,
|
||||||
|
XINPUT_STATE *pState
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t wLeftMotorSpeed;
|
||||||
|
uint16_t wRightMotorSpeed;
|
||||||
|
} XINPUT_VIBRATION;
|
||||||
|
|
||||||
|
DWORD
|
||||||
|
WINAPI
|
||||||
|
XInputSetState(
|
||||||
|
DWORD dwUserIndex,
|
||||||
|
XINPUT_VIBRATION* pVibration
|
||||||
|
);
|
||||||
|
|
||||||
|
// end xinput definitions
|
||||||
|
|
||||||
|
std::string get_button_string(XInputButtonEnum button) {
|
||||||
|
switch (button) {
|
||||||
|
case XInputButtonEnum::DPAD_UP:
|
||||||
|
return "Dpad Up";
|
||||||
|
case XInputButtonEnum::DPAD_DOWN:
|
||||||
|
return "Dpad Down";
|
||||||
|
case XInputButtonEnum::DPAD_LEFT:
|
||||||
|
return "Dpad Left";
|
||||||
|
case XInputButtonEnum::DPAD_RIGHT:
|
||||||
|
return "Dpad Right";
|
||||||
|
case XInputButtonEnum::START:
|
||||||
|
return "Start";
|
||||||
|
case XInputButtonEnum::BACK:
|
||||||
|
return "Back";
|
||||||
|
case XInputButtonEnum::LEFT_STICK:
|
||||||
|
return "Left Stick Click";
|
||||||
|
case XInputButtonEnum::RIGHT_STICK:
|
||||||
|
return "Right Stick Click";
|
||||||
|
case XInputButtonEnum::LEFT_SHOULDER:
|
||||||
|
return "Left Bumper";
|
||||||
|
case XInputButtonEnum::RIGHT_SHOULDER:
|
||||||
|
return "Right Bumper";
|
||||||
|
case XInputButtonEnum::BUTTON_A:
|
||||||
|
return "Button A";
|
||||||
|
case XInputButtonEnum::BUTTON_B:
|
||||||
|
return "Button B";
|
||||||
|
case XInputButtonEnum::BUTTON_X:
|
||||||
|
return "Button X";
|
||||||
|
case XInputButtonEnum::BUTTON_Y:
|
||||||
|
return "Button Y";
|
||||||
|
case XInputButtonEnum::LEFT_TRIGGER:
|
||||||
|
return "Left Trigger";
|
||||||
|
case XInputButtonEnum::RIGHT_TRIGGER:
|
||||||
|
return "Right Trigger";
|
||||||
|
case XInputButtonEnum::LEFT_STICK_UP:
|
||||||
|
return "Left Stick, Up";
|
||||||
|
case XInputButtonEnum::LEFT_STICK_DOWN:
|
||||||
|
return "Left Stick, Down";
|
||||||
|
case XInputButtonEnum::LEFT_STICK_LEFT:
|
||||||
|
return "Left Stick, Left";
|
||||||
|
case XInputButtonEnum::LEFT_STICK_RIGHT:
|
||||||
|
return "Left Stick, Right";
|
||||||
|
case XInputButtonEnum::RIGHT_STICK_UP:
|
||||||
|
return "Right Stick, Up";
|
||||||
|
case XInputButtonEnum::RIGHT_STICK_DOWN:
|
||||||
|
return "Right Stick, Down";
|
||||||
|
case XInputButtonEnum::RIGHT_STICK_LEFT:
|
||||||
|
return "Right Stick, Left";
|
||||||
|
case XInputButtonEnum::RIGHT_STICK_RIGHT:
|
||||||
|
return "Right Stick, Right";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return fmt::format("Unknown Button ({})", static_cast<int>(button));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_analog_string(XInputAnalogEnum analog) {
|
||||||
|
switch (analog) {
|
||||||
|
case XInputAnalogEnum::LEFT_TRIGGER:
|
||||||
|
return "Left Trigger";
|
||||||
|
case XInputAnalogEnum::RIGHT_TRIGGER:
|
||||||
|
return "Right Trigger";
|
||||||
|
case XInputAnalogEnum::LEFT_STICK_X:
|
||||||
|
return "Left Stick X";
|
||||||
|
case XInputAnalogEnum::LEFT_STICK_Y:
|
||||||
|
return "Left Stick Y";
|
||||||
|
case XInputAnalogEnum::RIGHT_STICK_X:
|
||||||
|
return "Right Stick X";
|
||||||
|
case XInputAnalogEnum::RIGHT_STICK_Y:
|
||||||
|
return "Right Stick Y";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return fmt::format("Unknown Analog ({})", static_cast<int>(analog));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_output_string(XInputOutputEnum output) {
|
||||||
|
switch (output) {
|
||||||
|
case XInputOutputEnum::LEFT_RUMBLE:
|
||||||
|
return "Left Rumble";
|
||||||
|
case XInputOutputEnum::RIGHT_RUMBLE:
|
||||||
|
return "Right Rumble";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return fmt::format("Unknown Output ({})", static_cast<int>(output));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_device_desc(uint8_t player) {
|
||||||
|
return fmt::format(";XINPUT;{}", player);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(SPICE_XP)
|
||||||
|
|
||||||
|
XInputManager::XInputManager() {}
|
||||||
|
XInputManager::~XInputManager() {}
|
||||||
|
void XInputManager::stop() {}
|
||||||
|
std::vector<uint8_t> XInputManager::get_available_players() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
float XInputManager::get_analog_state(uint8_t player, XInputAnalogEnum analog) {
|
||||||
|
return 0.5f;
|
||||||
|
}
|
||||||
|
bool XInputManager::is_button_pressed(uint8_t player, XInputButtonEnum button) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool XInputManager::get_any_button_pressed(XINPUT_NEW_BUTTON &button) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void XInputManager::set_output_state(uint8_t player, XInputOutputEnum output, float value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static decltype(XInputGetState) *XInputGetState_addr = nullptr;
|
||||||
|
static decltype(XInputSetState) *XInputSetState_addr = nullptr;
|
||||||
|
|
||||||
|
XInputManager::XInputManager() {
|
||||||
|
log_info("xinput", "initialize...");
|
||||||
|
this->xinput_lib = LoadLibraryA("xinput1_3.dll");
|
||||||
|
if (!this->xinput_lib) {
|
||||||
|
log_warning("xinput", "failed to load xinput1_3.dll");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
XInputGetState_addr = reinterpret_cast<decltype(XInputGetState) *>(
|
||||||
|
GetProcAddress(this->xinput_lib, "XInputGetState"));
|
||||||
|
if (!XInputGetState_addr) {
|
||||||
|
log_warning("xinput", "failed to get XInputGetState address");
|
||||||
|
this->stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
XInputSetState_addr = reinterpret_cast<decltype(XInputSetState) *>(
|
||||||
|
GetProcAddress(this->xinput_lib, "XInputSetState"));
|
||||||
|
if (!XInputSetState_addr) {
|
||||||
|
log_warning("xinput", "failed to get XInputSetState address");
|
||||||
|
this->stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
initialized = true;
|
||||||
|
log_info("xinput", "initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
XInputManager::~XInputManager() {
|
||||||
|
this->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void XInputManager::stop() {
|
||||||
|
this->initialized = false;
|
||||||
|
if (this->xinput_lib) {
|
||||||
|
FreeLibrary(this->xinput_lib);
|
||||||
|
this->xinput_lib = nullptr;
|
||||||
|
}
|
||||||
|
XInputGetState_addr = nullptr;
|
||||||
|
XInputSetState_addr = nullptr;
|
||||||
|
log_info("xinput", "destroyed");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> XInputManager::get_available_players() {
|
||||||
|
std::vector<uint8_t> players;
|
||||||
|
if (!this->initialized) {
|
||||||
|
return players;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < XUSER_MAX_COUNT; i++) {
|
||||||
|
XINPUT_STATE x;
|
||||||
|
if (XInputGetState_addr(i, &x) == ERROR_SUCCESS) {
|
||||||
|
players.push_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return players;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XInputManager::get_state(uint8_t player, XINPUT_GAMEPAD_STATE_NORMALIZED &state) {
|
||||||
|
state = {};
|
||||||
|
state.sThumbLX = 0.5f;
|
||||||
|
state.sThumbLY = 0.5f;
|
||||||
|
state.sThumbRX = 0.5f;
|
||||||
|
state.sThumbRY = 0.5f;
|
||||||
|
if (!this->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
XINPUT_STATE x;
|
||||||
|
if (XInputGetState_addr(player, &x) != ERROR_SUCCESS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.wButtons = x.Gamepad.wButtons;
|
||||||
|
state.bLeftTrigger = x.Gamepad.bLeftTrigger / 255.0f;
|
||||||
|
state.bRightTrigger = x.Gamepad.bRightTrigger / 255.0f;
|
||||||
|
|
||||||
|
// left stick circular dead zone
|
||||||
|
{
|
||||||
|
const auto x_raw = x.Gamepad.sThumbLX;
|
||||||
|
const auto y_raw = -x.Gamepad.sThumbLY; // flip to make [down = positive]
|
||||||
|
const float magnitude = sqrtf(x_raw * x_raw + y_raw * y_raw);
|
||||||
|
if (magnitude > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) {
|
||||||
|
const float scaled =
|
||||||
|
(magnitude - XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) /
|
||||||
|
(32767.0f - XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
|
||||||
|
|
||||||
|
state.sThumbLX = (x_raw / magnitude) * scaled;
|
||||||
|
state.sThumbLY = (y_raw / magnitude) * scaled;
|
||||||
|
|
||||||
|
// normalize to [0, 1]
|
||||||
|
state.sThumbLX = std::clamp((state.sThumbLX + 1.f) / 2.f, 0.f, 1.f);
|
||||||
|
state.sThumbLY = std::clamp((state.sThumbLY + 1.f) / 2.f, 0.f, 1.f);
|
||||||
|
} else {
|
||||||
|
// within deadzone
|
||||||
|
state.sThumbLX = 0.5f;
|
||||||
|
state.sThumbLY = 0.5f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// right stick circular dead zone
|
||||||
|
{
|
||||||
|
const auto x_raw = x.Gamepad.sThumbRX;
|
||||||
|
const auto y_raw = -x.Gamepad.sThumbRY; // flip to make [down = positive]
|
||||||
|
const float magnitude = sqrtf(x_raw * x_raw + y_raw * y_raw);
|
||||||
|
if (magnitude > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) {
|
||||||
|
const float scaled =
|
||||||
|
(magnitude - XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) /
|
||||||
|
(32767.0f - XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
|
||||||
|
|
||||||
|
state.sThumbRX = (x_raw / magnitude) * scaled;
|
||||||
|
state.sThumbRY = (y_raw / magnitude) * scaled;
|
||||||
|
|
||||||
|
// normalize to [0, 1]
|
||||||
|
state.sThumbRX = std::clamp((state.sThumbRX + 1.f) / 2.f, 0.f, 1.f);
|
||||||
|
state.sThumbRY = std::clamp((state.sThumbRY + 1.f) / 2.f, 0.f, 1.f);
|
||||||
|
} else {
|
||||||
|
// within deadzone
|
||||||
|
state.sThumbRX = 0.5f;
|
||||||
|
state.sThumbRY = 0.5f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XInputManager::is_button_pressed(uint8_t player, XInputButtonEnum button) {
|
||||||
|
XINPUT_GAMEPAD_STATE_NORMALIZED state;
|
||||||
|
|
||||||
|
get_state(player, state);
|
||||||
|
switch (button) {
|
||||||
|
case XInputButtonEnum::DPAD_UP:
|
||||||
|
return (state.wButtons & XINPUT_GAMEPAD_DPAD_UP) != 0;
|
||||||
|
case XInputButtonEnum::DPAD_DOWN:
|
||||||
|
return (state.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) != 0;
|
||||||
|
case XInputButtonEnum::DPAD_LEFT:
|
||||||
|
return (state.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) != 0;
|
||||||
|
case XInputButtonEnum::DPAD_RIGHT:
|
||||||
|
return (state.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) != 0;
|
||||||
|
case XInputButtonEnum::START:
|
||||||
|
return (state.wButtons & XINPUT_GAMEPAD_START) != 0;
|
||||||
|
case XInputButtonEnum::BACK:
|
||||||
|
return (state.wButtons & XINPUT_GAMEPAD_BACK) != 0;
|
||||||
|
case XInputButtonEnum::LEFT_STICK:
|
||||||
|
return (state.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) != 0;
|
||||||
|
case XInputButtonEnum::RIGHT_STICK:
|
||||||
|
return (state.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) != 0;
|
||||||
|
case XInputButtonEnum::LEFT_SHOULDER:
|
||||||
|
return (state.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) != 0;
|
||||||
|
case XInputButtonEnum::RIGHT_SHOULDER:
|
||||||
|
return (state.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) != 0;
|
||||||
|
case XInputButtonEnum::BUTTON_A:
|
||||||
|
return (state.wButtons & XINPUT_GAMEPAD_A) != 0;
|
||||||
|
case XInputButtonEnum::BUTTON_B:
|
||||||
|
return (state.wButtons & XINPUT_GAMEPAD_B) != 0;
|
||||||
|
case XInputButtonEnum::BUTTON_X:
|
||||||
|
return (state.wButtons & XINPUT_GAMEPAD_X) != 0;
|
||||||
|
case XInputButtonEnum::BUTTON_Y:
|
||||||
|
return (state.wButtons & XINPUT_GAMEPAD_Y) != 0;
|
||||||
|
case XInputButtonEnum::LEFT_TRIGGER:
|
||||||
|
return state.bLeftTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
|
||||||
|
case XInputButtonEnum::RIGHT_TRIGGER:
|
||||||
|
return state.bRightTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
|
||||||
|
case XInputButtonEnum::LEFT_STICK_UP:
|
||||||
|
return state.sThumbLY > 0.6f;
|
||||||
|
case XInputButtonEnum::LEFT_STICK_DOWN:
|
||||||
|
return state.sThumbLY < 0.4f;
|
||||||
|
case XInputButtonEnum::LEFT_STICK_LEFT:
|
||||||
|
return state.sThumbLX < 0.4f;
|
||||||
|
case XInputButtonEnum::LEFT_STICK_RIGHT:
|
||||||
|
return state.sThumbLX > 0.6f;
|
||||||
|
case XInputButtonEnum::RIGHT_STICK_UP:
|
||||||
|
return state.sThumbRY > 0.6f;
|
||||||
|
case XInputButtonEnum::RIGHT_STICK_DOWN:
|
||||||
|
return state.sThumbRY < 0.4f;
|
||||||
|
case XInputButtonEnum::RIGHT_STICK_LEFT:
|
||||||
|
return state.sThumbRX < 0.4f;
|
||||||
|
case XInputButtonEnum::RIGHT_STICK_RIGHT:
|
||||||
|
return state.sThumbRX > 0.6f;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float XInputManager::get_analog_state(uint8_t player, XInputAnalogEnum analog) {
|
||||||
|
XINPUT_GAMEPAD_STATE_NORMALIZED state;
|
||||||
|
|
||||||
|
get_state(player, state);
|
||||||
|
switch (analog) {
|
||||||
|
case XInputAnalogEnum::LEFT_TRIGGER:
|
||||||
|
return state.bLeftTrigger;
|
||||||
|
case XInputAnalogEnum::RIGHT_TRIGGER:
|
||||||
|
return state.bRightTrigger;
|
||||||
|
case XInputAnalogEnum::LEFT_STICK_X:
|
||||||
|
return state.sThumbLX;
|
||||||
|
case XInputAnalogEnum::LEFT_STICK_Y:
|
||||||
|
return state.sThumbLY;
|
||||||
|
case XInputAnalogEnum::RIGHT_STICK_X:
|
||||||
|
return state.sThumbRX;
|
||||||
|
case XInputAnalogEnum::RIGHT_STICK_Y:
|
||||||
|
return state.sThumbRY;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XInputManager::get_any_button_pressed(XINPUT_NEW_BUTTON &button) {
|
||||||
|
for (uint8_t player = 0; player < XUSER_MAX_COUNT; player++) {
|
||||||
|
for (uint16_t b = 0; b < static_cast<uint16_t>(XInputButtonEnum::COUNT); b++) {
|
||||||
|
if (is_button_pressed(player, static_cast<XInputButtonEnum>(b))) {
|
||||||
|
button.player = player;
|
||||||
|
button.button = static_cast<XInputButtonEnum>(b);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XInputManager::set_output_state(uint8_t player, XInputOutputEnum output, float value) {
|
||||||
|
if (!this->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (player >= XUSER_MAX_COUNT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
XINPUT_VIBRATION vibration = {};
|
||||||
|
if (output == XInputOutputEnum::LEFT_RUMBLE) {
|
||||||
|
vibration.wLeftMotorSpeed = static_cast<uint16_t>(value * 65535);
|
||||||
|
} else if (output == XInputOutputEnum::RIGHT_RUMBLE) {
|
||||||
|
vibration.wRightMotorSpeed = static_cast<uint16_t>(value * 65535);
|
||||||
|
}
|
||||||
|
XInputSetState_addr(player, &vibration);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
namespace xinput {
|
||||||
|
|
||||||
|
#define XUSER_MAX_COUNT 4
|
||||||
|
|
||||||
|
struct XINPUT_GAMEPAD_STATE {
|
||||||
|
uint16_t wButtons;
|
||||||
|
uint8_t bLeftTrigger;
|
||||||
|
uint8_t bRightTrigger;
|
||||||
|
int16_t sThumbLX;
|
||||||
|
int16_t sThumbLY;
|
||||||
|
int16_t sThumbRX;
|
||||||
|
int16_t sThumbRY;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct XINPUT_GAMEPAD_STATE_NORMALIZED {
|
||||||
|
uint16_t wButtons;
|
||||||
|
float bLeftTrigger;
|
||||||
|
float bRightTrigger;
|
||||||
|
float sThumbLX;
|
||||||
|
float sThumbLY;
|
||||||
|
float sThumbRX;
|
||||||
|
float sThumbRY;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class XInputButtonEnum : uint16_t {
|
||||||
|
// actual buttons
|
||||||
|
DPAD_UP,
|
||||||
|
DPAD_DOWN,
|
||||||
|
DPAD_LEFT,
|
||||||
|
DPAD_RIGHT,
|
||||||
|
START,
|
||||||
|
BACK,
|
||||||
|
LEFT_STICK,
|
||||||
|
RIGHT_STICK,
|
||||||
|
LEFT_SHOULDER,
|
||||||
|
RIGHT_SHOULDER,
|
||||||
|
BUTTON_A,
|
||||||
|
BUTTON_B,
|
||||||
|
BUTTON_X,
|
||||||
|
BUTTON_Y,
|
||||||
|
|
||||||
|
// analog values that can be used as buttons
|
||||||
|
LEFT_TRIGGER,
|
||||||
|
RIGHT_TRIGGER,
|
||||||
|
LEFT_STICK_UP,
|
||||||
|
LEFT_STICK_DOWN,
|
||||||
|
LEFT_STICK_LEFT,
|
||||||
|
LEFT_STICK_RIGHT,
|
||||||
|
RIGHT_STICK_UP,
|
||||||
|
RIGHT_STICK_DOWN,
|
||||||
|
RIGHT_STICK_LEFT,
|
||||||
|
RIGHT_STICK_RIGHT,
|
||||||
|
|
||||||
|
COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class XInputAnalogEnum {
|
||||||
|
// analog values
|
||||||
|
LEFT_TRIGGER,
|
||||||
|
RIGHT_TRIGGER,
|
||||||
|
LEFT_STICK_X,
|
||||||
|
LEFT_STICK_Y,
|
||||||
|
RIGHT_STICK_X,
|
||||||
|
RIGHT_STICK_Y,
|
||||||
|
|
||||||
|
COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class XInputOutputEnum : uint16_t {
|
||||||
|
LEFT_RUMBLE,
|
||||||
|
RIGHT_RUMBLE,
|
||||||
|
|
||||||
|
COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
struct XINPUT_NEW_BUTTON {
|
||||||
|
uint8_t player;
|
||||||
|
XInputButtonEnum button;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string get_button_string(XInputButtonEnum button);
|
||||||
|
std::string get_analog_string(XInputAnalogEnum analog);
|
||||||
|
std::string get_output_string(XInputOutputEnum output);
|
||||||
|
std::string get_device_desc(uint8_t player);
|
||||||
|
|
||||||
|
class XInputManager {
|
||||||
|
private:
|
||||||
|
bool initialized = false;
|
||||||
|
HMODULE xinput_lib = nullptr;
|
||||||
|
void get_state(uint8_t player, XINPUT_GAMEPAD_STATE_NORMALIZED &state);
|
||||||
|
public:
|
||||||
|
XInputManager();
|
||||||
|
~XInputManager();
|
||||||
|
void stop();
|
||||||
|
std::vector<uint8_t> get_available_players();
|
||||||
|
bool is_button_pressed(uint8_t player, XInputButtonEnum button);
|
||||||
|
float get_analog_state(uint8_t player, XInputAnalogEnum analog);
|
||||||
|
bool get_any_button_pressed(XINPUT_NEW_BUTTON &button);
|
||||||
|
void set_output_state(uint8_t player, XInputOutputEnum output, float value);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -243,6 +243,7 @@ static LRESULT CALLBACK SpiceTouchWndProc(HWND hWnd, UINT msg, WPARAM wParam, LP
|
|||||||
const auto is_windowed_sub =
|
const auto is_windowed_sub =
|
||||||
(GRAPHICS_IIDX_WSUB && hWnd == TDJ_SUBSCREEN_WINDOW) ||
|
(GRAPHICS_IIDX_WSUB && hWnd == TDJ_SUBSCREEN_WINDOW) ||
|
||||||
(hWnd == SDVX_SUBSCREEN_WINDOW) ||
|
(hWnd == SDVX_SUBSCREEN_WINDOW) ||
|
||||||
|
(hWnd == POPN_SUBSCREEN_WINDOW) ||
|
||||||
(hWnd == GFDM_SUBSCREEN_WINDOW);
|
(hWnd == GFDM_SUBSCREEN_WINDOW);
|
||||||
|
|
||||||
if (msg == WM_CLOSE) {
|
if (msg == WM_CLOSE) {
|
||||||
|
|||||||
@@ -219,20 +219,21 @@ intptr_t replace_pattern(HMODULE module, const std::string &signature,
|
|||||||
strreplace(pattern_str, "??", "00");
|
strreplace(pattern_str, "??", "00");
|
||||||
auto pattern_bin = std::make_unique<uint8_t[]>(signature.length() / 2);
|
auto pattern_bin = std::make_unique<uint8_t[]>(signature.length() / 2);
|
||||||
if (!hex2bin(pattern_str.c_str(), pattern_bin.get())) {
|
if (!hex2bin(pattern_str.c_str(), pattern_bin.get())) {
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// build signature mask
|
// 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) {
|
for (size_t i = 0; i < signature.length(); i += 2) {
|
||||||
if (signature[i] == '?') {
|
if (signature[i] == '?') {
|
||||||
if (signature[i + 1] == '?') {
|
if (signature[i + 1] == '?') {
|
||||||
signature_mask << '?';
|
signature_mask += '?';
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
} 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");
|
strreplace(replace_data_str, "??", "00");
|
||||||
auto replace_data_bin = std::make_unique<uint8_t[]>(replacement.length() / 2);
|
auto replace_data_bin = std::make_unique<uint8_t[]>(replacement.length() / 2);
|
||||||
if (!hex2bin(replace_data_str.c_str(), replace_data_bin.get())) {
|
if (!hex2bin(replace_data_str.c_str(), replace_data_bin.get())) {
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// build replace mask
|
// 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) {
|
for (size_t i = 0; i < replacement.length(); i += 2) {
|
||||||
if (replacement[i] == '?') {
|
if (replacement[i] == '?') {
|
||||||
if (replacement[i + 1] == '?') {
|
if (replacement[i + 1] == '?') {
|
||||||
replace_mask << '?';
|
replace_mask += '?';
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
replace_mask << 'X';
|
replace_mask += 'X';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,11 +264,11 @@ intptr_t replace_pattern(HMODULE module, const std::string &signature,
|
|||||||
return replace_pattern(
|
return replace_pattern(
|
||||||
module,
|
module,
|
||||||
pattern_bin.get(),
|
pattern_bin.get(),
|
||||||
signature_mask.str().c_str(),
|
signature_mask.c_str(),
|
||||||
offset,
|
offset,
|
||||||
usage,
|
usage,
|
||||||
replace_data_bin.get(),
|
replace_data_bin.get(),
|
||||||
replace_mask.str().c_str()
|
replace_mask.c_str()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+204
-25
@@ -1,3 +1,6 @@
|
|||||||
|
// GetDisplayConfigBufferSizes etc is Vista+
|
||||||
|
#define _WIN32_WINNT 0x0601
|
||||||
|
|
||||||
#include "sysutils.h"
|
#include "sysutils.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
@@ -7,6 +10,10 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#undef WIN32_NO_STATUS
|
#undef WIN32_NO_STATUS
|
||||||
|
|
||||||
|
#include "hooks/graphics/graphics.h"
|
||||||
|
#include "avs/game.h"
|
||||||
|
#include "util/detour.h"
|
||||||
|
#include "util/flags_helper.h"
|
||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
@@ -83,6 +90,9 @@ namespace sysutils {
|
|||||||
);
|
);
|
||||||
static GetSystemFirmwareTable_t GetSystemFirmwareTable = nullptr;
|
static GetSystemFirmwareTable_t GetSystemFirmwareTable = nullptr;
|
||||||
|
|
||||||
|
std::string SECOND_MONITOR_OVERRIDE = "";
|
||||||
|
static decltype(EnumDisplayDevicesA) *EnumDisplayDevicesA_orig = nullptr;
|
||||||
|
|
||||||
void print_smbios() {
|
void print_smbios() {
|
||||||
DWORD bytes_written = 0;
|
DWORD bytes_written = 0;
|
||||||
DWORD table_size = 0;
|
DWORD table_size = 0;
|
||||||
@@ -209,58 +219,87 @@ namespace sysutils {
|
|||||||
return strings;
|
return strings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string device_flags_to_string(DWORD flags) {
|
||||||
|
FLAGS_START(flags);
|
||||||
|
FLAG(flags, DISPLAY_DEVICE_ATTACHED_TO_DESKTOP);
|
||||||
|
FLAG(flags, DISPLAY_DEVICE_MULTI_DRIVER);
|
||||||
|
FLAG(flags, DISPLAY_DEVICE_PRIMARY_DEVICE);
|
||||||
|
FLAG(flags, DISPLAY_DEVICE_MIRRORING_DRIVER);
|
||||||
|
FLAG(flags, DISPLAY_DEVICE_VGA_COMPATIBLE);
|
||||||
|
FLAG(flags, DISPLAY_DEVICE_REMOVABLE);
|
||||||
|
|
||||||
|
#if !defined(DISPLAY_DEVICE_ACC_DRIVER)
|
||||||
|
#define DISPLAY_DEVICE_ACC_DRIVER 0x00000040
|
||||||
|
#endif
|
||||||
|
FLAG(flags, DISPLAY_DEVICE_ACC_DRIVER);
|
||||||
|
|
||||||
|
FLAG(flags, DISPLAY_DEVICE_MODESPRUNED);
|
||||||
|
FLAG(flags, DISPLAY_DEVICE_RDPUDD);
|
||||||
|
FLAG(flags, DISPLAY_DEVICE_REMOTE);
|
||||||
|
FLAG(flags, DISPLAY_DEVICE_DISCONNECT);
|
||||||
|
FLAG(flags, DISPLAY_DEVICE_TS_COMPATIBLE);
|
||||||
|
|
||||||
|
#if !defined(DISPLAY_DEVICE_UNSAFE_MODES_ON)
|
||||||
|
#define DISPLAY_DEVICE_UNSAFE_MODES_ON 0x00080000
|
||||||
|
#endif
|
||||||
|
FLAG(flags, DISPLAY_DEVICE_UNSAFE_MODES_ON);
|
||||||
|
FLAGS_END(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string monitor_flags_to_string(DWORD flags) {
|
||||||
|
FLAGS_START(flags);
|
||||||
|
FLAG(flags, DISPLAY_DEVICE_ACTIVE);
|
||||||
|
FLAG(flags, DISPLAY_DEVICE_ATTACHED);
|
||||||
|
FLAGS_END(flags);
|
||||||
|
}
|
||||||
|
|
||||||
static void print_adapter(DWORD index, PDISPLAY_DEVICEA adapter, bool is_monitor) {
|
static void print_adapter(DWORD index, PDISPLAY_DEVICEA adapter, bool is_monitor) {
|
||||||
if (adapter->StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) {
|
if (!is_monitor) {
|
||||||
return;
|
log_misc("gpuinfo", "-------- device {} --------", index);
|
||||||
}
|
|
||||||
if (!(adapter->StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string prefix("device");
|
std::string prefix("device");
|
||||||
if (is_monitor) {
|
if (is_monitor) {
|
||||||
prefix = " adapter";
|
prefix = " adapter";
|
||||||
}
|
}
|
||||||
log_misc("gpuinfo", "{} {} device name : {}", prefix.c_str(), index, adapter->DeviceName);
|
|
||||||
log_misc("gpuinfo", "{} {} device string : {}", prefix.c_str(), index, adapter->DeviceString);
|
|
||||||
log_dbug("gpuinfo", "{} {} flags : 0x{:x}", prefix.c_str(), index, adapter->StateFlags);
|
|
||||||
|
|
||||||
if (!is_monitor) {
|
if (!is_monitor) {
|
||||||
// get extended info for better friendly name of monitors
|
// get extended info for better friendly name of monitors
|
||||||
|
std::string friendly_name = "unknown";
|
||||||
const auto &monitors = enumerate_monitors();
|
const auto &monitors = enumerate_monitors();
|
||||||
for (const auto& monitor : monitors) {
|
for (const auto& monitor : monitors) {
|
||||||
if (monitor.display_name == adapter->DeviceName) {
|
if (monitor.display_name == adapter->DeviceName) {
|
||||||
const auto friendly = ws2s(monitor.friendly_name.c_str());
|
friendly_name = ws2s(monitor.friendly_name.c_str());
|
||||||
log_misc(
|
|
||||||
"gpuinfo", "{} {} friendly name : {}",
|
|
||||||
prefix.c_str(),
|
|
||||||
index,
|
|
||||||
friendly);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log_misc("gpuinfo", "{} {} name : {} ({}) @ {}",
|
||||||
|
prefix.c_str(), index,
|
||||||
|
adapter->DeviceName, friendly_name, adapter->DeviceString);
|
||||||
|
|
||||||
// resolution, refresh rate
|
// resolution, refresh rate
|
||||||
DEVMODEA devmode = {};
|
DEVMODEA devmode = {};
|
||||||
devmode.dmSize = sizeof(devmode);
|
devmode.dmSize = sizeof(devmode);
|
||||||
if (EnumDisplaySettingsA(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &devmode)) {
|
if (EnumDisplaySettingsA(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &devmode)) {
|
||||||
log_misc(
|
log_misc(
|
||||||
"gpuinfo",
|
"gpuinfo", "{} {} resolution : {}px * {}px @ {}Hz",
|
||||||
"{} {} resolution : {}px * {}px @ {}Hz",
|
|
||||||
prefix.c_str(),
|
prefix.c_str(),
|
||||||
index,
|
index,
|
||||||
devmode.dmPelsWidth, devmode.dmPelsHeight,
|
devmode.dmPelsWidth, devmode.dmPelsHeight,
|
||||||
devmode.dmDisplayFrequency);
|
devmode.dmDisplayFrequency);
|
||||||
} else {
|
|
||||||
log_misc("gpuinfo", "EnumDisplaySettingsA failed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// primary?
|
log_misc("gpuinfo", "{} {} flags : {}",
|
||||||
log_misc(
|
prefix.c_str(), index, device_flags_to_string(adapter->StateFlags));
|
||||||
"gpuinfo", "{} {} is primary : {}",
|
|
||||||
prefix.c_str(),
|
|
||||||
index,
|
|
||||||
(adapter->StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) ? "yes" : "no");
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
log_misc("gpuinfo", "{} {} name : {} ({})",
|
||||||
|
prefix.c_str(), index,
|
||||||
|
adapter->DeviceName, adapter->DeviceString);
|
||||||
|
|
||||||
|
log_misc("gpuinfo", "{} {} flags : {}",
|
||||||
|
prefix.c_str(), index, monitor_flags_to_string(adapter->StateFlags));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,7 +409,26 @@ namespace sysutils {
|
|||||||
|
|
||||||
std::vector<MonitorEntry> result;
|
std::vector<MonitorEntry> result;
|
||||||
for (const auto& path : paths) {
|
for (const auto& path : paths) {
|
||||||
MonitorEntry entry;
|
MonitorEntry entry = {};
|
||||||
|
entry.adapter_id_LowPart = path.targetInfo.adapterId.LowPart;
|
||||||
|
entry.adapter_id_HighPart = path.targetInfo.adapterId.HighPart;
|
||||||
|
entry.id = path.targetInfo.id;
|
||||||
|
|
||||||
|
// primary monitor?
|
||||||
|
for (const auto& mode : modes) {
|
||||||
|
if (mode.infoType != DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode.adapterId.HighPart == path.sourceInfo.adapterId.HighPart &&
|
||||||
|
mode.adapterId.LowPart == path.sourceInfo.adapterId.LowPart &&
|
||||||
|
mode.id == path.sourceInfo.id) {
|
||||||
|
if (mode.sourceMode.position.x == 0 && mode.sourceMode.position.y == 0) {
|
||||||
|
entry.is_primary = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// device ID (\\.\DISPLAYn)
|
// device ID (\\.\DISPLAYn)
|
||||||
DISPLAYCONFIG_SOURCE_DEVICE_NAME source_name = {};
|
DISPLAYCONFIG_SOURCE_DEVICE_NAME source_name = {};
|
||||||
@@ -406,4 +464,125 @@ namespace sysutils {
|
|||||||
static const std::vector<MonitorEntry> monitors = enumerate_monitors_internal();
|
static const std::vector<MonitorEntry> monitors = enumerate_monitors_internal();
|
||||||
return monitors;
|
return monitors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
BOOL
|
||||||
|
__stdcall
|
||||||
|
EnumDisplayDevicesA_hook(
|
||||||
|
LPCSTR lpDevice,
|
||||||
|
DWORD iDevNum,
|
||||||
|
PDISPLAY_DEVICEA lpDisplayDevice,
|
||||||
|
DWORD dwFlags
|
||||||
|
) {
|
||||||
|
if (EnumDisplayDevicesA_orig == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// caller is enumerating monitors (adapters), not devices
|
||||||
|
if (lpDevice != nullptr) {
|
||||||
|
log_misc("sysutils", "EnumDisplayDevicesA: returning original results for device {} [{}]", lpDevice, iDevNum);
|
||||||
|
return EnumDisplayDevicesA_orig(lpDevice, iDevNum, lpDisplayDevice, dwFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// call the original first
|
||||||
|
const auto result_orig = EnumDisplayDevicesA_orig(nullptr, iDevNum, lpDisplayDevice, dwFlags);
|
||||||
|
if (!result_orig) {
|
||||||
|
return result_orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_misc(
|
||||||
|
"sysutils",
|
||||||
|
"EnumDisplayDevicesA_orig: {} [{}], StateFlags={:#x}",
|
||||||
|
lpDisplayDevice->DeviceName,
|
||||||
|
iDevNum,
|
||||||
|
lpDisplayDevice->StateFlags);
|
||||||
|
|
||||||
|
// this one is not relevant
|
||||||
|
if ((lpDisplayDevice->StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == 0) {
|
||||||
|
return result_orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this is for the primary device, just return it
|
||||||
|
if (lpDisplayDevice->StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
|
||||||
|
log_misc(
|
||||||
|
"sysutils",
|
||||||
|
"EnumDisplayDevicesA: returning original results for primary monitor [{}], {}, result={}",
|
||||||
|
iDevNum,
|
||||||
|
lpDisplayDevice->DeviceName,
|
||||||
|
result_orig);
|
||||||
|
return result_orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is not 100% confirmed, but hope that this helps in cases where second monitors get used
|
||||||
|
// even though NumberOfAdaptersInGroup was set to 1 on hybrid laptops
|
||||||
|
if (GRAPHICS_FORCE_SINGLE_ADAPTER) {
|
||||||
|
lpDisplayDevice->StateFlags &= ~DISPLAY_DEVICE_ATTACHED_TO_DESKTOP;
|
||||||
|
log_info(
|
||||||
|
"sysutils",
|
||||||
|
"hiding this monitor, {} @ index {} (-graphics-single-adapter)",
|
||||||
|
lpDisplayDevice->DeviceName,
|
||||||
|
iDevNum);
|
||||||
|
return result_orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for the second device (subscreen)...
|
||||||
|
if (SECOND_MONITOR_OVERRIDE.empty()) {
|
||||||
|
// if there is no user override, we find the device with lowest index (that isn't primary)
|
||||||
|
for (DWORD i = 0;; i++) {
|
||||||
|
DISPLAY_DEVICEA device = {};
|
||||||
|
device.cb = sizeof(device);
|
||||||
|
const auto result = EnumDisplayDevicesA_orig(nullptr, i, &device, 0);
|
||||||
|
if (!result) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
|
||||||
|
// primary, skip this
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == 0) {
|
||||||
|
// disconnected, skip this as well
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (std::string(lpDisplayDevice->DeviceName) == device.DeviceName) {
|
||||||
|
log_info(
|
||||||
|
"sysutils",
|
||||||
|
"returning second monitor {} @ index {}",
|
||||||
|
lpDisplayDevice->DeviceName,
|
||||||
|
iDevNum);
|
||||||
|
return result_orig;
|
||||||
|
} else {
|
||||||
|
// otherwise, fall through and hide this monitor
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (SECOND_MONITOR_OVERRIDE == lpDisplayDevice->DeviceName) {
|
||||||
|
// if there is one preferred by the user, use this one, and hide others
|
||||||
|
log_info(
|
||||||
|
"sysutils",
|
||||||
|
"returning second monitor, {} @ index {} (-iidxsubmonitor / -sdvxsubmonitor)",
|
||||||
|
lpDisplayDevice->DeviceName,
|
||||||
|
iDevNum);
|
||||||
|
return result_orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this device should not be used; pretend that it's not connected
|
||||||
|
log_info(
|
||||||
|
"sysutils",
|
||||||
|
"hiding this monitor, {} @ index {}",
|
||||||
|
lpDisplayDevice->DeviceName,
|
||||||
|
iDevNum);
|
||||||
|
lpDisplayDevice->StateFlags &= ~DISPLAY_DEVICE_ATTACHED_TO_DESKTOP;
|
||||||
|
return result_orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hook_EnumDisplayDevicesA() {
|
||||||
|
// the goal is to trick DX9 into thinking that some monitors are not attached
|
||||||
|
// so we need to trampoline hook at user32 instead of IAT at game level
|
||||||
|
log_misc("sysutils", "hooking EnumDisplayDevicesA to hide if there are more than 3 monitors...");
|
||||||
|
detour::trampoline_try(
|
||||||
|
"user32.dll",
|
||||||
|
"EnumDisplayDevicesA",
|
||||||
|
(void*)EnumDisplayDevicesA_hook,
|
||||||
|
(void**)&EnumDisplayDevicesA_orig);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,14 @@ namespace sysutils {
|
|||||||
struct MonitorEntry {
|
struct MonitorEntry {
|
||||||
std::string display_name; // \\.\DISPLAY1
|
std::string display_name; // \\.\DISPLAY1
|
||||||
std::wstring friendly_name; // Dell S2204T; wstring so that it can be converted to utf8 or string
|
std::wstring friendly_name; // Dell S2204T; wstring so that it can be converted to utf8 or string
|
||||||
|
bool is_primary;
|
||||||
|
uint32_t adapter_id_LowPart;
|
||||||
|
int32_t adapter_id_HighPart;
|
||||||
|
uint32_t id;
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::vector<MonitorEntry> &enumerate_monitors();
|
const std::vector<MonitorEntry> &enumerate_monitors();
|
||||||
|
|
||||||
|
extern std::string SECOND_MONITOR_OVERRIDE;
|
||||||
|
void hook_EnumDisplayDevicesA();
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ namespace tapeledutils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// for bi2x-style byte array of all colors and LEDs at once
|
// 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};
|
rgb_float3_t result = {0.f, 0.f, 0.f};
|
||||||
if (TAPE_LED_ALGORITHM == TAPE_LED_USE_AVERAGE) {
|
if (TAPE_LED_ALGORITHM == TAPE_LED_USE_AVERAGE) {
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ namespace tapeledutils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// normalize
|
// 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.r = avg_ri * avg_mult;
|
||||||
result.g = avg_gi * avg_mult;
|
result.g = avg_gi * avg_mult;
|
||||||
result.b = avg_bi * avg_mult;
|
result.b = avg_bi * avg_mult;
|
||||||
@@ -50,7 +50,7 @@ namespace tapeledutils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// normalize
|
// normalize
|
||||||
const float single_mult = 1.f / 255;
|
const float single_mult = 1.f / divisor;
|
||||||
result.r = color[0] * single_mult;
|
result.r = color[0] * single_mult;
|
||||||
result.g = color[1] * single_mult;
|
result.g = color[1] * single_mult;
|
||||||
result.b = color[2] * single_mult;
|
result.b = color[2] * single_mult;
|
||||||
@@ -58,6 +58,15 @@ namespace tapeledutils {
|
|||||||
return result;
|
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
|
// for bi2a-style that calls for each individual LED
|
||||||
size_t get_led_index_using_avg_algo(size_t data_size) {
|
size_t get_led_index_using_avg_algo(size_t data_size) {
|
||||||
size_t index_to_use;
|
size_t index_to_use;
|
||||||
|
|||||||
@@ -25,13 +25,33 @@ namespace tapeledutils {
|
|||||||
struct tape_led {
|
struct tape_led {
|
||||||
std::vector<rgb_float3_t> data;
|
std::vector<rgb_float3_t> data;
|
||||||
int index_r, index_g, index_b; // Averaged RGB light output indexes
|
int index_r, index_g, index_b; // Averaged RGB light output indexes
|
||||||
|
uint8_t max_value;
|
||||||
std::string lightName;
|
std::string lightName;
|
||||||
|
|
||||||
tape_led(size_t data_size, int index_r, int index_g, int index_b, std::string lightName)
|
tape_led(
|
||||||
: data(std::vector<rgb_float3_t>(data_size)), index_r(index_r), index_g(index_g), index_b(index_b), lightName(std::move(lightName)) {}
|
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();
|
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);
|
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);
|
size_t get_led_index_using_avg_algo(size_t data_size);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user