mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-02 06:40:42 -07:00
Compare commits
80 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2b930ad95f | |||
| 3c60f3966b | |||
| c59d399ab8 | |||
| f69e40fa26 | |||
| e9dcc5717e | |||
| 5bec3d5db7 | |||
| 6ec2b47345 | |||
| ebda16beae | |||
| c29a26f183 | |||
| 2014e9cf75 | |||
| 33f2e1d4b7 | |||
| eb037542b4 | |||
| 039b42ad53 | |||
| 33b4718744 | |||
| 71ba9b6b47 | |||
| 1957917270 | |||
| b9ccddf47d | |||
| cd0ba51b5a | |||
| 1f23d88c7a | |||
| 46e43ab09c | |||
| 96950b6b4e | |||
| 5f94ef4478 | |||
| c4193eddc5 | |||
| c8962a0e77 | |||
| b6a749e5db | |||
| 7dbbe9809e | |||
| 79a3e32d5d | |||
| c5a4e954f9 | |||
| 7b862768b0 | |||
| af8d8dae9f | |||
| d9d5823fdb | |||
| 82dda33800 | |||
| 3876e38636 | |||
| 6bc1357d6a | |||
| 37218e7fe0 | |||
| 678e11eade | |||
| f72313fe45 | |||
| 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
|
||||||
@@ -522,6 +523,8 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
hooks/lang.cpp
|
hooks/lang.cpp
|
||||||
hooks/libraryhook.cpp
|
hooks/libraryhook.cpp
|
||||||
hooks/networkhook.cpp
|
hooks/networkhook.cpp
|
||||||
|
hooks/icmphook_net.cpp
|
||||||
|
hooks/icmphook_iphlpapi.cpp
|
||||||
hooks/powrprof.cpp
|
hooks/powrprof.cpp
|
||||||
#hooks/rom.cpp
|
#hooks/rom.cpp
|
||||||
hooks/setupapihook.cpp
|
hooks/setupapihook.cpp
|
||||||
@@ -582,6 +585,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 +600,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
|
||||||
@@ -603,6 +609,9 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
reader/structuredmessage.cpp
|
reader/structuredmessage.cpp
|
||||||
reader/crypt.cpp
|
reader/crypt.cpp
|
||||||
|
|
||||||
|
# sdk
|
||||||
|
sdk/sdk.cpp
|
||||||
|
|
||||||
# stubs
|
# stubs
|
||||||
stubs/stubs.cpp
|
stubs/stubs.cpp
|
||||||
|
|
||||||
@@ -629,6 +638,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
util/time.cpp
|
util/time.cpp
|
||||||
util/cpuutils.cpp
|
util/cpuutils.cpp
|
||||||
util/netutils.cpp
|
util/netutils.cpp
|
||||||
|
util/precise_timer.cpp
|
||||||
util/sysutils.cpp
|
util/sysutils.cpp
|
||||||
util/lz77.cpp
|
util/lz77.cpp
|
||||||
util/tapeled.cpp
|
util/tapeled.cpp
|
||||||
@@ -856,6 +866,36 @@ if(NOT MSVC)
|
|||||||
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# sdk_sample_v0_flat_c.dll (32 bit)
|
||||||
|
set(SOURCE_FILES sdk/sample/v0/flat_c/v0_flat_c.c)
|
||||||
|
add_library(spicetools_sdk_sample_v0_flat_c_32 SHARED ${SOURCE_FILES} ${RESOURCE_FILES} sdk/sample/v0/flat_c/v0_flat_c.def)
|
||||||
|
set_target_properties(spicetools_sdk_sample_v0_flat_c_32 PROPERTIES PREFIX "")
|
||||||
|
set_target_properties(spicetools_sdk_sample_v0_flat_c_32 PROPERTIES OUTPUT_NAME "sdk_sample_v0_flat_c")
|
||||||
|
|
||||||
|
if(NOT MSVC)
|
||||||
|
set_target_properties(spicetools_sdk_sample_v0_flat_c_32 PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# sdk_sample_v0_flat_c.dll (64 bit)
|
||||||
|
set(SOURCE_FILES sdk/sample/v0/flat_c/v0_flat_c.c)
|
||||||
|
add_library(spicetools_sdk_sample_v0_flat_c_64 SHARED ${SOURCE_FILES} ${RESOURCE_FILES} sdk/sample/v0/flat_c/v0_flat_c.def)
|
||||||
|
set_target_properties(spicetools_sdk_sample_v0_flat_c_64 PROPERTIES PREFIX "")
|
||||||
|
set_target_properties(spicetools_sdk_sample_v0_flat_c_64 PROPERTIES OUTPUT_NAME "sdk_sample_v0_flat_c")
|
||||||
|
|
||||||
|
if(NOT MSVC)
|
||||||
|
set_target_properties(spicetools_sdk_sample_v0_flat_c_64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# sdk_sample_v0_cpp.dll (64 bit)
|
||||||
|
set(SOURCE_FILES sdk/sample/v0/cpp/v0_cpp.cpp)
|
||||||
|
add_library(spicetools_sdk_sample_v0_cpp_64 SHARED ${SOURCE_FILES} ${RESOURCE_FILES} sdk/sample/v0/cpp/v0_cpp.def)
|
||||||
|
set_target_properties(spicetools_sdk_sample_v0_cpp_64 PROPERTIES PREFIX "")
|
||||||
|
set_target_properties(spicetools_sdk_sample_v0_cpp_64 PROPERTIES OUTPUT_NAME "sdk_sample_v0_cpp")
|
||||||
|
|
||||||
|
if(NOT MSVC)
|
||||||
|
set_target_properties(spicetools_sdk_sample_v0_cpp_64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
|
||||||
|
endif()
|
||||||
|
|
||||||
# output directories
|
# output directories
|
||||||
####################
|
####################
|
||||||
|
|
||||||
@@ -865,14 +905,14 @@ set_target_properties(spicetools_cfg spicetools_cfg_linux
|
|||||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools")
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools")
|
||||||
|
|
||||||
# output 32bit
|
# output 32bit
|
||||||
set_target_properties(spicetools_spice spicetools_spice_laa spicetools_spice_linux spicetools_stubs_kbt spicetools_stubs_kld spicetools_stubs_cpusbxpkm
|
set_target_properties(spicetools_spice spicetools_spice_laa spicetools_spice_linux spicetools_stubs_kbt spicetools_stubs_kld spicetools_stubs_cpusbxpkm spicetools_sdk_sample_v0_flat_c_32
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive32"
|
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive32"
|
||||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32"
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32"
|
||||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32")
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32")
|
||||||
|
|
||||||
# output 64bit
|
# output 64bit
|
||||||
set_target_properties(spicetools_spice64 spicetools_spice64_linux spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvcuda spicetools_stubs_nvcuvid spicetools_stubs_nvEncodeAPI64
|
set_target_properties(spicetools_spice64 spicetools_spice64_linux spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvcuda spicetools_stubs_nvcuvid spicetools_stubs_nvEncodeAPI64 spicetools_sdk_sample_v0_flat_c_64 spicetools_sdk_sample_v0_cpp_64
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive64"
|
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive64"
|
||||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/64"
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/64"
|
||||||
|
|||||||
@@ -283,6 +283,16 @@ Python is the only one that is fully spec compliant.
|
|||||||
Other libraries may be missing features and contain bugs; please feel free to
|
Other libraries may be missing features and contain bugs; please feel free to
|
||||||
contribute code to fill the gaps if you work on a project using these libraries.
|
contribute code to fill the gaps if you work on a project using these libraries.
|
||||||
|
|
||||||
|
## Spice SDK (for DLL hooks / plugins)
|
||||||
|
|
||||||
|
As an alternative to Spice API, Spice SDK is available.
|
||||||
|
|
||||||
|
This is a flat C, header-only library that can be included in your DLL. Once
|
||||||
|
initialized, you can call directly into helper routines provided by Spicetools
|
||||||
|
executable, making low-latency and high-throughput interactions possible.
|
||||||
|
|
||||||
|
Check the documentation on the wiki for more details.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Unless otherwise noted, all files are licensed under the GPLv3.
|
Unless otherwise noted, all files are licensed under the GPLv3.
|
||||||
See the LICENSE file for the full license text.
|
See the LICENSE file for the full license text.
|
||||||
|
|||||||
+122
-20
@@ -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];
|
||||||
unit->status.status_code = unit->state;
|
if (avs::game::is_model("JDZ")) {
|
||||||
|
unit->status.status_code = unit->card_in ? ENGAGED : EMPTY;
|
||||||
|
} else {
|
||||||
|
unit->status.status_code = unit->state;
|
||||||
|
}
|
||||||
memcpy(status, &unit->status, sizeof(struct ICCA_STATUS));
|
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,16 +453,61 @@ 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 = STEP;
|
||||||
unit->state = SLEEP;
|
|
||||||
else
|
|
||||||
unit->state = STEP;
|
|
||||||
break;
|
break;
|
||||||
case SLEEP:
|
case SLEEP:
|
||||||
unit->state = SLEEP;
|
unit->state = 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);
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
#include "mdxf.h"
|
#include "mdxf.h"
|
||||||
#include "mdxf_poll.h"
|
#include "mdxf_poll.h"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
|
#include "games/ddr/ddr.h"
|
||||||
#include "games/ddr/io.h"
|
#include "games/ddr/io.h"
|
||||||
#include "launcher/launcher.h"
|
#include "launcher/launcher.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
#define MDFX_DEBUG_VERBOSE 0
|
#define MDFX_DEBUG_VERBOSE 0
|
||||||
|
|
||||||
@@ -108,9 +111,10 @@ static void mdxf_thread_start() {
|
|||||||
MDXF_THREAD = std::thread([] {
|
MDXF_THREAD = std::thread([] {
|
||||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
|
||||||
|
|
||||||
|
timeutils::PreciseSleepTimer timer;
|
||||||
while (MDXF_THREAD_RUNNING.load(std::memory_order_acquire)) {
|
while (MDXF_THREAD_RUNNING.load(std::memory_order_acquire)) {
|
||||||
mdxf_poll(false);
|
mdxf_poll(false);
|
||||||
std::this_thread::sleep_for(THREAD_PERIOD);
|
timer.sleep(THREAD_PERIOD);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -357,14 +361,17 @@ static bool __cdecl ac_io_mdxf_update_control_status_buffer_impl(int node, MDXFP
|
|||||||
|
|
||||||
// decide on button map
|
// decide on button map
|
||||||
const size_t *button_map = nullptr;
|
const size_t *button_map = nullptr;
|
||||||
|
int player = 0;
|
||||||
switch (node) {
|
switch (node) {
|
||||||
case 17:
|
case 17:
|
||||||
case 25:
|
case 25:
|
||||||
button_map = &buttons_p1[0];
|
button_map = &buttons_p1[0];
|
||||||
|
player = 1;
|
||||||
break;
|
break;
|
||||||
case 18:
|
case 18:
|
||||||
case 26:
|
case 26:
|
||||||
button_map = &buttons_p2[0];
|
button_map = &buttons_p2[0];
|
||||||
|
player = 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,19 +381,27 @@ static bool __cdecl ac_io_mdxf_update_control_status_buffer_impl(int node, MDXFP
|
|||||||
if (source == EXTERNAL_POLL) {
|
if (source == EXTERNAL_POLL) {
|
||||||
// get buttons
|
// get buttons
|
||||||
auto &buttons = games::ddr::get_buttons();
|
auto &buttons = games::ddr::get_buttons();
|
||||||
|
|
||||||
|
// get analogs
|
||||||
|
bool analog_left = false;
|
||||||
|
bool analog_right = false;
|
||||||
|
games::ddr::get_analog_x_axis(player, analog_left, analog_right);
|
||||||
|
bool analog_up = false;
|
||||||
|
bool analog_down = false;
|
||||||
|
games::ddr::get_analog_y_axis(player, analog_up, analog_down);
|
||||||
|
|
||||||
uint8_t up_down = 0;
|
uint8_t up_down = 0;
|
||||||
uint8_t left_right = 0;
|
uint8_t left_right = 0;
|
||||||
|
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[0])) || analog_up) {
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[0]))) {
|
|
||||||
up_down |= 0xF0;
|
up_down |= 0xF0;
|
||||||
}
|
}
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[1]))) {
|
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[1])) || analog_down) {
|
||||||
up_down |= 0x0F;
|
up_down |= 0x0F;
|
||||||
}
|
}
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[2]))) {
|
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[2])) || analog_left) {
|
||||||
left_right |= 0xF0;
|
left_right |= 0xF0;
|
||||||
}
|
}
|
||||||
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[3]))) {
|
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[3])) || analog_right) {
|
||||||
left_right |= 0x0F;
|
left_right |= 0x0F;
|
||||||
}
|
}
|
||||||
current_state = (uint16_t(up_down) << 8) | left_right;
|
current_state = (uint16_t(up_down) << 8) | left_right;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "util/circular_buffer.h"
|
#include "util/circular_buffer.h"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "games/sdvx/sdvx.h"
|
#include "games/sdvx/sdvx.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
using namespace acioemu;
|
using namespace acioemu;
|
||||||
@@ -46,11 +47,12 @@ ICCADevice::ICCADevice(bool flip_order, bool keypad_thread, uint8_t node_count)
|
|||||||
// keypad thread for faster polling
|
// keypad thread for faster polling
|
||||||
if (keypad_thread) {
|
if (keypad_thread) {
|
||||||
this->keypad_thread = new std::thread([this]() {
|
this->keypad_thread = new std::thread([this]() {
|
||||||
|
timeutils::PreciseSleepTimer timer;
|
||||||
while (this->cards) {
|
while (this->cards) {
|
||||||
for (int unit = 0; unit < this->node_count; unit++) {
|
for (int unit = 0; unit < this->node_count; unit++) {
|
||||||
this->update_keypad(unit, false);
|
this->update_keypad(unit, false);
|
||||||
}
|
}
|
||||||
Sleep(7);
|
timer.sleep(7);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "launcher/launcher.h"
|
#include "launcher/launcher.h"
|
||||||
#include "games/io.h"
|
#include "games/io.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
#include "acio/mdxf/mdxf_poll.h"
|
||||||
|
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
using namespace rapidjson;
|
using namespace rapidjson;
|
||||||
@@ -104,6 +105,7 @@ namespace api::modules {
|
|||||||
for (auto &analog : *this->analogs) {
|
for (auto &analog : *this->analogs) {
|
||||||
analog.override_enabled = false;
|
analog.override_enabled = false;
|
||||||
}
|
}
|
||||||
|
mdxf_poll(true);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -148,6 +150,7 @@ namespace api::modules {
|
|||||||
if (analog.getName() == name) {
|
if (analog.getName() == name) {
|
||||||
analog.override_state = CLAMP(state, 0.f, 1.f);
|
analog.override_state = CLAMP(state, 0.f, 1.f);
|
||||||
analog.override_enabled = true;
|
analog.override_enabled = true;
|
||||||
|
mdxf_poll(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,6 +170,7 @@ namespace api::modules {
|
|||||||
for (auto &analog : *this->analogs) {
|
for (auto &analog : *this->analogs) {
|
||||||
if (analog.getName() == name) {
|
if (analog.getName() == name) {
|
||||||
analog.override_enabled = false;
|
analog.override_enabled = false;
|
||||||
|
mdxf_poll(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "launcher/launcher.h"
|
#include "launcher/launcher.h"
|
||||||
#include "games/io.h"
|
#include "games/io.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
#include "acio/mdxf/mdxf_poll.h"
|
||||||
|
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
using namespace rapidjson;
|
using namespace rapidjson;
|
||||||
@@ -107,6 +108,7 @@ namespace api::modules {
|
|||||||
for (auto &button : *this->buttons) {
|
for (auto &button : *this->buttons) {
|
||||||
button.override_enabled = false;
|
button.override_enabled = false;
|
||||||
}
|
}
|
||||||
|
mdxf_poll(true);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -153,6 +155,7 @@ namespace api::modules {
|
|||||||
GameAPI::Buttons::BUTTON_PRESSED : GameAPI::Buttons::BUTTON_NOT_PRESSED;
|
GameAPI::Buttons::BUTTON_PRESSED : GameAPI::Buttons::BUTTON_NOT_PRESSED;
|
||||||
button.override_velocity = CLAMP(state, 0.f, 1.f);
|
button.override_velocity = CLAMP(state, 0.f, 1.f);
|
||||||
button.override_enabled = true;
|
button.override_enabled = true;
|
||||||
|
mdxf_poll(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,6 +175,7 @@ namespace api::modules {
|
|||||||
for (auto &button : *this->buttons) {
|
for (auto &button : *this->buttons) {
|
||||||
if (button.getName() == name) {
|
if (button.getName() == name) {
|
||||||
button.override_enabled = false;
|
button.override_enabled = false;
|
||||||
|
mdxf_poll(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "avs/game.h"
|
#include "avs/game.h"
|
||||||
#include "external/rapidjson/document.h"
|
#include "external/rapidjson/document.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
|
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
using namespace rapidjson;
|
using namespace rapidjson;
|
||||||
@@ -58,6 +59,7 @@ namespace api::modules {
|
|||||||
// get params
|
// get params
|
||||||
auto keypad = req.params[0].GetUint();
|
auto keypad = req.params[0].GetUint();
|
||||||
auto input = std::string(req.params[1].GetString());
|
auto input = std::string(req.params[1].GetString());
|
||||||
|
timeutils::PreciseSleepTimer timer;
|
||||||
|
|
||||||
// process all chars
|
// process all chars
|
||||||
for (auto c : input) {
|
for (auto c : input) {
|
||||||
@@ -91,11 +93,11 @@ namespace api::modules {
|
|||||||
|
|
||||||
// set
|
// set
|
||||||
eamuse_set_keypad_overrides(keypad, state);
|
eamuse_set_keypad_overrides(keypad, state);
|
||||||
Sleep(sleep_time);
|
timer.sleep(sleep_time);
|
||||||
|
|
||||||
// unset
|
// unset
|
||||||
eamuse_set_keypad_overrides(keypad, 0);
|
eamuse_set_keypad_overrides(keypad, 0);
|
||||||
Sleep(sleep_time);
|
timer.sleep(sleep_time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ namespace api::modules {
|
|||||||
|
|
||||||
// check if file exists in modules
|
// check if file exists in modules
|
||||||
if (!fileutils::file_exists(dll_path)) {
|
if (!fileutils::file_exists(dll_path)) {
|
||||||
return error(res, "Couldn't find " + dll_path.string());
|
return error(res, fmt::format("Couldn't find {}", dll_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
// get module
|
// get module
|
||||||
@@ -118,7 +118,7 @@ namespace api::modules {
|
|||||||
|
|
||||||
// check if file exists in modules
|
// check if file exists in modules
|
||||||
if (!fileutils::file_exists(dll_path)) {
|
if (!fileutils::file_exists(dll_path)) {
|
||||||
return error(res, "Couldn't find " + dll_path.string());
|
return error(res, fmt::format("Couldn't find {}", dll_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
// get module
|
// get module
|
||||||
@@ -196,7 +196,7 @@ namespace api::modules {
|
|||||||
|
|
||||||
// check if file exists in modules
|
// check if file exists in modules
|
||||||
if (!fileutils::file_exists(dll_path)) {
|
if (!fileutils::file_exists(dll_path)) {
|
||||||
return error(res, "Couldn't find " + dll_path.string());
|
return error(res, fmt::format("Couldn't find {}", dll_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
// get module
|
// get module
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ namespace api {
|
|||||||
controller->init_state(this->state);
|
controller->init_state(this->state);
|
||||||
this->thread = new std::thread([this] () {
|
this->thread = new std::thread([this] () {
|
||||||
log_warning("api::serial", "listening on {} (baud: {})", this->port, this->baud);
|
log_warning("api::serial", "listening on {} (baud: {})", this->port, this->baud);
|
||||||
|
timeutils::PreciseSleepTimer timer;
|
||||||
|
|
||||||
// read buffer
|
// read buffer
|
||||||
uint8_t read_buffer[16*1024];
|
uint8_t read_buffer[16*1024];
|
||||||
@@ -160,7 +162,7 @@ namespace api {
|
|||||||
|
|
||||||
// slow down on reconnect
|
// slow down on reconnect
|
||||||
if (this->running) {
|
if (this->running) {
|
||||||
Sleep(retry_time);
|
timer.sleep(retry_time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -1506,7 +1516,7 @@ namespace avs {
|
|||||||
" * It's also possible that you have incomplete game data\n"
|
" * It's also possible that you have incomplete game data\n"
|
||||||
" * Do NOT copy over random DLLs from another game installation; DLL must match game version\n"
|
" * Do NOT copy over random DLLs from another game installation; DLL must match game version\n"
|
||||||
"\n"
|
"\n"
|
||||||
, DLL_NAME, MODULE_PATH.string()) };
|
, DLL_NAME, MODULE_PATH) };
|
||||||
log_warning("avs-ea3", "{}", info_str);
|
log_warning("avs-ea3", "{}", info_str);
|
||||||
log_fatal("avs-ea3", "Failed to find critical avs DLL on disk (avs2-core.dll OR {})", DLL_NAME);
|
log_fatal("avs-ea3", "Failed to find critical avs DLL on disk (avs2-core.dll OR {})", DLL_NAME);
|
||||||
}
|
}
|
||||||
@@ -1747,8 +1757,8 @@ namespace avs {
|
|||||||
{
|
{
|
||||||
deferredlogs::defer_error_messages({
|
deferredlogs::defer_error_messages({
|
||||||
"AVS filesystem initialization failure was previously detected during boot!",
|
"AVS filesystem initialization failure was previously detected during boot!",
|
||||||
fmt::format(" ERROR: directory could not be created: {}", src_path.string().c_str()),
|
fmt::format(" ERROR: directory could not be created: {}", src_path),
|
||||||
fmt::format(" if you see a crash, it may have been caused by bad <mounttable> contents in {}", avs::core::CFG_PATH.c_str()),
|
fmt::format(" if you see a crash, it may have been caused by bad <mounttable> contents in {}", avs::core::CFG_PATH),
|
||||||
" fix the XML file and try again"
|
" fix the XML file and try again"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1770,14 +1780,14 @@ namespace avs {
|
|||||||
auto created = std::filesystem::create_directories(real_path, err);
|
auto created = std::filesystem::create_directories(real_path, err);
|
||||||
|
|
||||||
if (created) {
|
if (created) {
|
||||||
log_info("avs-core", "created '{}' at '{}'", avs_path, real_path.string());
|
log_info("avs-core", "created '{}' at '{}'", avs_path, real_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
avs_dir_err(real_path);
|
avs_dir_err(real_path);
|
||||||
log_warning("avs-core", "failed to create '{}' folder at '{}': {}",
|
log_warning("avs-core", "failed to create '{}' folder at '{}': {}",
|
||||||
avs_path,
|
avs_path,
|
||||||
real_path.string(),
|
real_path,
|
||||||
err.message());
|
err.message());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1911,6 +1921,15 @@ namespace avs {
|
|||||||
// create nvram and raw directories if possible for mounttable configurations
|
// create nvram and raw directories if possible for mounttable configurations
|
||||||
create_avs_config_fs_table(config, config_node);
|
create_avs_config_fs_table(config, config_node);
|
||||||
|
|
||||||
|
#if !SPICE64
|
||||||
|
// sdvx4 bad log config fix
|
||||||
|
if (avs::game::DLL_NAME == "soundvoltex.dll" && // it's too early for avs::game::is_model
|
||||||
|
property_search_safe(config, config_node, "/log/enable_console")) {
|
||||||
|
log_info("avs-core", "applying SDVX4 avs-config.xml fix for <log><enable_console>");
|
||||||
|
property_search_remove_safe(config, config_node, "/log/enable_console");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// set log level
|
// set log level
|
||||||
if (!LOG_LEVEL_CUSTOM.empty()) {
|
if (!LOG_LEVEL_CUSTOM.empty()) {
|
||||||
property_search_remove_safe(config, config_node, "/log/level");
|
property_search_remove_safe(config, config_node, "/log/level");
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ namespace avs {
|
|||||||
" * It's also possible that you have incomplete game data\n"
|
" * It's also possible that you have incomplete game data\n"
|
||||||
" * Do NOT copy over random DLLs from another game installation; DLL must match game version\n"
|
" * Do NOT copy over random DLLs from another game installation; DLL must match game version\n"
|
||||||
"\n"
|
"\n"
|
||||||
, DLL_NAME, MODULE_PATH.string()) };
|
, DLL_NAME, MODULE_PATH) };
|
||||||
|
|
||||||
log_warning("avs-ea3", "{}", info_str);
|
log_warning("avs-ea3", "{}", info_str);
|
||||||
log_fatal("avs-ea3", "Failed to find critical ea3 DLL on disk (avs2-ea3.dll OR {})", DLL_NAME);
|
log_fatal("avs-ea3", "Failed to find critical ea3 DLL on disk (avs2-ea3.dll OR {})", DLL_NAME);
|
||||||
@@ -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");
|
||||||
|
|||||||
@@ -73,11 +73,10 @@ namespace avs {
|
|||||||
|
|
||||||
// load game instance
|
// load game instance
|
||||||
const auto dll_path = MODULE_PATH / DLL_NAME;
|
const auto dll_path = MODULE_PATH / DLL_NAME;
|
||||||
const auto dll_path_s = dll_path.string();
|
log_info("avs-game", "DLL path: {}", dll_path);
|
||||||
log_info("avs-game", "DLL path: {}", dll_path_s.c_str());
|
|
||||||
|
|
||||||
// MAX_PATH is 260
|
// MAX_PATH is 260
|
||||||
if (130 <= dll_path_s.length()) {
|
if (const auto dll_path_len = dll_path.wstring().length(); 130 <= dll_path_len) {
|
||||||
log_warning(
|
log_warning(
|
||||||
"avs-game",
|
"avs-game",
|
||||||
"PATH TOO LONG WARNING\n\n"
|
"PATH TOO LONG WARNING\n\n"
|
||||||
@@ -93,7 +92,7 @@ namespace avs {
|
|||||||
"long, often resulting in random crashes. Move the game contents to\n"
|
"long, often resulting in random crashes. Move the game contents to\n"
|
||||||
"a directory with shorter path.\n"
|
"a directory with shorter path.\n"
|
||||||
"-------------------------------------------------------------------\n\n",
|
"-------------------------------------------------------------------\n\n",
|
||||||
dll_path_s, dll_path_s.length());
|
dll_path, dll_path_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ddr gamemdx.dll user error
|
// ddr gamemdx.dll user error
|
||||||
@@ -112,7 +111,7 @@ namespace avs {
|
|||||||
|
|
||||||
// file not found on disk
|
// file not found on disk
|
||||||
if (!fileutils::file_exists(dll_path)) {
|
if (!fileutils::file_exists(dll_path)) {
|
||||||
log_warning("avs-game", "game DLL could not be found on disk: {}", dll_path.string().c_str());
|
log_warning("avs-game", "game DLL could not be found on disk: {}", dll_path);
|
||||||
log_warning("avs-game", "double check -exec and -modules parameters; unless you know what you're doing, leave them blank");
|
log_warning("avs-game", "double check -exec and -modules parameters; unless you know what you're doing, leave them blank");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,8 @@
|
|||||||
<button name="VEFX" vkey="10" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
<button name="VEFX" vkey="10" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||||
</buttons>
|
</buttons>
|
||||||
<analogs>
|
<analogs>
|
||||||
<analog name="Turntable P1" devid="Player 1" index="4" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Turntable P1" devid="Player 1" index="4" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Turntable P2" devid="Player 2" index="4" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Turntable P2" devid="Player 2" index="4" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
</analogs>
|
</analogs>
|
||||||
<lights>
|
<lights>
|
||||||
<light name="P1 1" devid="Player 1" index="0"/>
|
<light name="P1 1" devid="Player 1" index="0"/>
|
||||||
@@ -117,8 +117,8 @@
|
|||||||
<keypad_buttons/>
|
<keypad_buttons/>
|
||||||
<!-- Assumes analog mode. Need to invert direction since this FW is designed for INF. -->
|
<!-- Assumes analog mode. Need to invert direction since this FW is designed for INF. -->
|
||||||
<analogs>
|
<analogs>
|
||||||
<analog name="Turntable P1" devid="arcin (1p)" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="true" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Turntable P1" devid="arcin (1p)" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="true" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Turntable P2" devid="arcin (2p)" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="true" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Turntable P2" devid="arcin (2p)" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="true" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
</analogs>
|
</analogs>
|
||||||
<lights>
|
<lights>
|
||||||
<light name="P1 1" devid="arcin (1p)" index="0"/>
|
<light name="P1 1" devid="arcin (1p)" index="0"/>
|
||||||
@@ -144,34 +144,34 @@
|
|||||||
<buttons/>
|
<buttons/>
|
||||||
<keypad_buttons/>
|
<keypad_buttons/>
|
||||||
<analogs>
|
<analogs>
|
||||||
<analog name="Key 1" devid="Analogs key 01-28" index="4" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 1" devid="Analogs key 01-28" index="4" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 2" devid="Analogs key 01-28" index="5" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 2" devid="Analogs key 01-28" index="5" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 3" devid="Analogs key 01-28" index="6" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 3" devid="Analogs key 01-28" index="6" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 4" devid="Analogs key 01-28" index="7" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 4" devid="Analogs key 01-28" index="7" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 5" devid="Analogs key 01-28" index="8" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 5" devid="Analogs key 01-28" index="8" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 6" devid="Analogs key 01-28" index="9" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 6" devid="Analogs key 01-28" index="9" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 7" devid="Analogs key 01-28" index="10" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 7" devid="Analogs key 01-28" index="10" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 8" devid="Analogs key 01-28" index="11" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 8" devid="Analogs key 01-28" index="11" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 9" devid="Analogs key 01-28" index="12" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 9" devid="Analogs key 01-28" index="12" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 10" devid="Analogs key 01-28" index="13" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 10" devid="Analogs key 01-28" index="13" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 11" devid="Analogs key 01-28" index="14" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 11" devid="Analogs key 01-28" index="14" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 12" devid="Analogs key 01-28" index="15" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 12" devid="Analogs key 01-28" index="15" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 13" devid="Analogs key 01-28" index="16" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 13" devid="Analogs key 01-28" index="16" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 14" devid="Analogs key 01-28" index="17" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 14" devid="Analogs key 01-28" index="17" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 15" devid="Analogs key 01-28" index="18" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 15" devid="Analogs key 01-28" index="18" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 16" devid="Analogs key 01-28" index="19" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 16" devid="Analogs key 01-28" index="19" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 17" devid="Analogs key 01-28" index="20" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 17" devid="Analogs key 01-28" index="20" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 18" devid="Analogs key 01-28" index="21" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 18" devid="Analogs key 01-28" index="21" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 19" devid="Analogs key 01-28" index="22" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 19" devid="Analogs key 01-28" index="22" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 20" devid="Analogs key 01-28" index="23" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 20" devid="Analogs key 01-28" index="23" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 21" devid="Analogs key 01-28" index="24" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 21" devid="Analogs key 01-28" index="24" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 22" devid="Analogs key 01-28" index="25" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 22" devid="Analogs key 01-28" index="25" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 23" devid="Analogs key 01-28" index="26" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 23" devid="Analogs key 01-28" index="26" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 24" devid="Analogs key 01-28" index="27" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 24" devid="Analogs key 01-28" index="27" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 25" devid="Analogs key 01-28" index="28" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 25" devid="Analogs key 01-28" index="28" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 26" devid="Analogs key 01-28" index="29" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 26" devid="Analogs key 01-28" index="29" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 27" devid="Analogs key 01-28" index="30" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 27" devid="Analogs key 01-28" index="30" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Key 28" devid="Analogs key 01-28" index="31" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Key 28" devid="Analogs key 01-28" index="31" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
</analogs>
|
</analogs>
|
||||||
<lights>
|
<lights>
|
||||||
<light name="Key 1 R" devid="Lights 01-14" index="0"/>
|
<light name="Key 1 R" devid="Lights 01-14" index="0"/>
|
||||||
@@ -277,9 +277,9 @@
|
|||||||
</buttons>
|
</buttons>
|
||||||
<keypad_buttons/>
|
<keypad_buttons/>
|
||||||
<analogs>
|
<analogs>
|
||||||
<analog name="Guitar P1 Wail X" devid="Gitaller P1" index="1" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Guitar P1 Wail X" devid="Gitaller P1" index="1" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Guitar P1 Wail Y" devid="Gitaller P1" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Guitar P1 Wail Y" devid="Gitaller P1" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="Guitar P1 Wail Z" devid="Gitaller P1" index="5" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="Guitar P1 Wail Z" devid="Gitaller P1" index="5" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
</analogs>
|
</analogs>
|
||||||
<lights/>
|
<lights/>
|
||||||
<!-- Gen 1 FAUCETWO in HID mode; should also work for later gens -->
|
<!-- Gen 1 FAUCETWO in HID mode; should also work for later gens -->
|
||||||
@@ -296,8 +296,8 @@
|
|||||||
</buttons>
|
</buttons>
|
||||||
<keypad_buttons/>
|
<keypad_buttons/>
|
||||||
<analogs>
|
<analogs>
|
||||||
<analog name="VOL-L" devid="Buttons (MI_01)" index="1" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="VOL-L" devid="Buttons (MI_01)" index="1" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
<analog name="VOL-R" devid="Buttons (MI_01)" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay="0"/>
|
<analog name="VOL-R" devid="Buttons (MI_01)" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||||
</analogs>
|
</analogs>
|
||||||
<lights>
|
<lights>
|
||||||
<light name="BT-A" devid="Lights (MI_02)" index="24"/>
|
<light name="BT-A" devid="Lights (MI_02)" index="24"/>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
<security>
|
<security>
|
||||||
<requestedPrivileges>
|
<requestedPrivileges>
|
||||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
|
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
|
||||||
</requestedPrivileges>
|
</requestedPrivileges>
|
||||||
</security>
|
</security>
|
||||||
</trustInfo>
|
</trustInfo>
|
||||||
|
|||||||
+102
-18
@@ -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"
|
||||||
@@ -62,21 +68,38 @@ DIST_FOLDER="./dist"
|
|||||||
DIST_NAME="spice2x-$(date +%y)-$(date +%m)-$(date +%d).zip"
|
DIST_NAME="spice2x-$(date +%y)-$(date +%m)-$(date +%d).zip"
|
||||||
DIST_NAME_EXTRAS="spice2x-$(date +%y)-$(date +%m)-$(date +%d)-full.zip"
|
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 spicetools_sdk_sample_v0_flat_c_32"
|
||||||
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 spicetools_sdk_sample_v0_flat_c_64 spicetools_sdk_sample_v0_cpp_64"
|
||||||
|
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,12 @@ 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
|
||||||
|
mkdir -p ${OUTDIR_EXTRAS}/sdk/samples/32
|
||||||
|
mkdir -p ${OUTDIR_EXTRAS}/sdk/samples/64
|
||||||
|
if ((BUILD_XP > 0))
|
||||||
|
then
|
||||||
|
mkdir -p ${OUTDIR_EXTRAS}/winxp
|
||||||
|
fi
|
||||||
|
|
||||||
if false # ((DEBUG > 0))
|
if false # ((DEBUG > 0))
|
||||||
then
|
then
|
||||||
@@ -199,16 +283,8 @@ then
|
|||||||
cp ${BUILDDIR_32}/spicetools/spicecfg-pdb.pdb ${OUTDIR} 2>/dev/null
|
cp ${BUILDDIR_32}/spicetools/spicecfg-pdb.pdb ${OUTDIR} 2>/dev/null
|
||||||
cp ${BUILDDIR_32}/spicetools/32/spice-pdb.exe ${OUTDIR} 2>/dev/null
|
cp ${BUILDDIR_32}/spicetools/32/spice-pdb.exe ${OUTDIR} 2>/dev/null
|
||||||
cp ${BUILDDIR_32}/spicetools/32/spice-pdb.pdb ${OUTDIR} 2>/dev/null
|
cp ${BUILDDIR_32}/spicetools/32/spice-pdb.pdb ${OUTDIR} 2>/dev/null
|
||||||
#cp ${BUILDDIR_32}/spicetools/32/kbt.dll ${OUTDIR}/stubs/32 2>/dev/null
|
|
||||||
#cp ${BUILDDIR_32}/spicetools/32/kld.dll ${OUTDIR}/stubs/32 2>/dev/null
|
|
||||||
cp ${BUILDDIR_64}/spicetools/64/spice64-pdb.exe ${OUTDIR} 2>/dev/null
|
cp ${BUILDDIR_64}/spicetools/64/spice64-pdb.exe ${OUTDIR} 2>/dev/null
|
||||||
cp ${BUILDDIR_64}/spicetools/64/spice64-pdb.pdb ${OUTDIR} 2>/dev/null
|
cp ${BUILDDIR_64}/spicetools/64/spice64-pdb.pdb ${OUTDIR} 2>/dev/null
|
||||||
#cp ${BUILDDIR_64}/spicetools/64/kbt.dll ${OUTDIR}/stubs/64 2>/dev/null
|
|
||||||
#cp ${BUILDDIR_64}/spicetools/64/kld.dll ${OUTDIR}/stubs/64 2>/dev/null
|
|
||||||
#cp ${BUILDDIR_64}/spicetools/64/nvEncodeAPI64.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_32}/spicetools/32/cpusbxpkm.dll ${OUTDIR}/stubs/32 2>/dev/null
|
|
||||||
else
|
else
|
||||||
# release files
|
# release files
|
||||||
cp ${BUILDDIR_32}/spicetools/spicecfg.exe ${OUTDIR} 2>/dev/null
|
cp ${BUILDDIR_32}/spicetools/spicecfg.exe ${OUTDIR} 2>/dev/null
|
||||||
@@ -216,16 +292,21 @@ else
|
|||||||
cp ${BUILDDIR_32}/spicetools/32/spice.exe ${OUTDIR} 2>/dev/null
|
cp ${BUILDDIR_32}/spicetools/32/spice.exe ${OUTDIR} 2>/dev/null
|
||||||
cp ${BUILDDIR_32}/spicetools/32/spice_laa.exe ${OUTDIR_EXTRAS}/largeaddressaware/spice.exe 2>/dev/null
|
cp ${BUILDDIR_32}/spicetools/32/spice_laa.exe ${OUTDIR_EXTRAS}/largeaddressaware/spice.exe 2>/dev/null
|
||||||
cp ${BUILDDIR_32}/spicetools/32/spice_linux.exe ${OUTDIR_EXTRAS}/linux/spice.exe 2>/dev/null
|
cp ${BUILDDIR_32}/spicetools/32/spice_linux.exe ${OUTDIR_EXTRAS}/linux/spice.exe 2>/dev/null
|
||||||
#cp ${BUILDDIR_32}/spicetools/32/kbt.dll ${OUTDIR}/stubs/32 2>/dev/null
|
|
||||||
#cp ${BUILDDIR_32}/spicetools/32/kld.dll ${OUTDIR}/stubs/32 2>/dev/null
|
|
||||||
cp ${BUILDDIR_64}/spicetools/64/spice64.exe ${OUTDIR} 2>/dev/null
|
cp ${BUILDDIR_64}/spicetools/64/spice64.exe ${OUTDIR} 2>/dev/null
|
||||||
cp ${BUILDDIR_64}/spicetools/64/spice64_linux.exe ${OUTDIR_EXTRAS}/linux/spice64.exe 2>/dev/null
|
cp ${BUILDDIR_64}/spicetools/64/spice64_linux.exe ${OUTDIR_EXTRAS}/linux/spice64.exe 2>/dev/null
|
||||||
#cp ${BUILDDIR_64}/spicetools/64/kbt.dll ${OUTDIR}/stubs/64 2>/dev/null
|
|
||||||
#cp ${BUILDDIR_64}/spicetools/64/kld.dll ${OUTDIR}/stubs/64 2>/dev/null
|
|
||||||
cp ${BUILDDIR_64}/spicetools/64/nvEncodeAPI64.dll ${OUTDIR}/stubs/64 2>/dev/null
|
cp ${BUILDDIR_64}/spicetools/64/nvEncodeAPI64.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/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
|
||||||
|
cp ${BUILDDIR_32}/spicetools/32/sdk_sample_v0_flat_c.dll ${OUTDIR_EXTRAS}/sdk/samples/32/v0_flat_c.dll 2>/dev/null
|
||||||
|
cp ${BUILDDIR_64}/spicetools/64/sdk_sample_v0_flat_c.dll ${OUTDIR_EXTRAS}/sdk/samples/64/v0_flat_c.dll 2>/dev/null
|
||||||
|
cp ${BUILDDIR_64}/spicetools/64/sdk_sample_v0_cpp.dll ${OUTDIR_EXTRAS}/sdk/samples/64/v0_cpp.dll 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
|
||||||
@@ -238,6 +319,9 @@ then
|
|||||||
echo "WARNING: Couldn't get git to create the archive. Is this a git repository?"
|
echo "WARNING: Couldn't get git to create the archive. Is this a git repository?"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# sdk headers
|
||||||
|
cp -r ./sdk/include ${OUTDIR_EXTRAS}/sdk
|
||||||
|
|
||||||
# copy resources
|
# copy resources
|
||||||
rm -rf ${OUTDIR_EXTRAS}/api
|
rm -rf ${OUTDIR_EXTRAS}/api
|
||||||
mkdir ${OUTDIR_EXTRAS}/api
|
mkdir ${OUTDIR_EXTRAS}/api
|
||||||
@@ -250,10 +334,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,6 +1,6 @@
|
|||||||
docker build --pull external/docker -t spicetools/deps
|
docker build --pull external/docker -t spicetools/deps
|
||||||
docker build --build-context gitroot=%cd%/../../.git . -t spicetools/spice
|
docker build --build-context gitroot=%cd%/../../.git . -t spicetools/spice
|
||||||
docker run --rm -it -v %cd%/dist:/src/src/spice2x/dist -v %cd%/bin:/src/src/spice2x/bin -v %cd%/.ccache:/src/src/spice2x/.ccache spicetools/spice %*
|
docker run --rm -i -v %cd%/dist:/src/src/spice2x/dist -v %cd%/bin:/src/src/spice2x/bin -v %cd%/.ccache:/src/src/spice2x/.ccache spicetools/spice %*
|
||||||
@REM to generate PDBs, set DEBUG to 1 in build_all.sh, place cv2pdb in external\cv2pdb, and run below
|
@REM to generate PDBs, set DEBUG to 1 in build_all.sh, place cv2pdb in external\cv2pdb, and run below
|
||||||
@REM external\cv2pdb\cv2pdb.exe bin\spice2x\spicecfg.exe bin\spice2x\spicecfg-pdb.exe bin\spice2x\spicecfg-pdb.pdb
|
@REM external\cv2pdb\cv2pdb.exe bin\spice2x\spicecfg.exe bin\spice2x\spicecfg-pdb.exe bin\spice2x\spicecfg-pdb.pdb
|
||||||
@REM external\cv2pdb\cv2pdb.exe bin\spice2x\spice.exe bin\spice2x\spice-pdb.exe bin\spice2x\spice-pdb.pdb
|
@REM external\cv2pdb\cv2pdb.exe bin\spice2x\spice.exe bin\spice2x\spice-pdb.exe bin\spice2x\spice-pdb.pdb
|
||||||
|
|||||||
@@ -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 "$@"
|
||||||
|
|||||||
+105
-14
@@ -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:
|
||||||
@@ -203,22 +207,38 @@ float Analog::applyMultiplier(float value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
float Analog::normalizeAnalogValue(float value) {
|
float Analog::normalizeAnalogValue(float value) {
|
||||||
// effectively the same as fmodf(value, 1.f)
|
if (getType() == GameAPI::Analogs::AnalogType::Circular) {
|
||||||
// for small values, this is MUCH faster than fmodf.
|
// effectively the same as fmodf(value, 1.f)
|
||||||
float new_value = value;
|
// for small values, this is MUCH faster than fmodf.
|
||||||
while (new_value > 1.f) {
|
float new_value = value;
|
||||||
new_value -= 1.f;
|
while (new_value > 1.f) {
|
||||||
|
new_value -= 1.f;
|
||||||
|
}
|
||||||
|
while (new_value < 0.f) {
|
||||||
|
new_value += 1.f;
|
||||||
|
}
|
||||||
|
return new_value;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// clamp to [0, 1] range
|
||||||
|
return std::clamp(value, 0.f, 1.f);
|
||||||
}
|
}
|
||||||
while (new_value < 0.f) {
|
|
||||||
new_value += 1.f;
|
|
||||||
}
|
|
||||||
return new_value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float Analog::applyDeadzone(float raw_value) {
|
float Analog::applyDeadzone(float raw_value) {
|
||||||
float value = raw_value;
|
float value = raw_value;
|
||||||
const auto deadzone = this->getDeadzone();
|
auto deadzone = this->getDeadzone();
|
||||||
if (deadzone > 0) {
|
|
||||||
|
// in the past, positive deadzone applied in the center, negative deadzone applied to 0
|
||||||
|
// after each analog value received a type (circular/linear) this has been simpliifed to
|
||||||
|
// positive values only since we can figure out where the rest value is
|
||||||
|
// for back compat, treat negative value as positive
|
||||||
|
if (deadzone < 0.f) {
|
||||||
|
deadzone = -deadzone;
|
||||||
|
}
|
||||||
|
|
||||||
|
// relative mode assumes that user is using a stick, so center is neutral regardless of analog type
|
||||||
|
if (getType() != GameAPI::Analogs::AnalogType::LinearPositive || isRelativeMode()) {
|
||||||
|
|
||||||
// calculate values
|
// calculate values
|
||||||
const auto delta = value - 0.5f;
|
const auto delta = value - 0.5f;
|
||||||
@@ -251,7 +271,7 @@ float Analog::applyDeadzone(float raw_value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (deadzone < 0) {
|
} else {
|
||||||
|
|
||||||
// invert for mirror
|
// invert for mirror
|
||||||
if (this->getDeadzoneMirror()) {
|
if (this->getDeadzoneMirror()) {
|
||||||
@@ -259,8 +279,8 @@ float Analog::applyDeadzone(float raw_value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// deadzone from minimum value
|
// deadzone from minimum value
|
||||||
if (deadzone > -1 && value > -deadzone) {
|
if (deadzone < 1.f && deadzone < value) {
|
||||||
value = std::min(1.f, (value + deadzone) / (1.f + deadzone));
|
value = std::max(0.f, (value - deadzone) / (1.f - deadzone));
|
||||||
} else {
|
} else {
|
||||||
value = 0.f;
|
value = 0.f;
|
||||||
}
|
}
|
||||||
@@ -272,3 +292,74 @@ float Analog::applyDeadzone(float raw_value) {
|
|||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float Analog::getRelativeModeValue(float raw_value) {
|
||||||
|
const auto now = get_performance_seconds();
|
||||||
|
auto delta_time = now - this->rel_mode_last_read_time_s;
|
||||||
|
|
||||||
|
if (this->rel_mode_last_read_time_s != 0.f) {
|
||||||
|
// some heuristics to prevent huge jumps:
|
||||||
|
// * if we went for more than 250ms without polls, discard it (e.g., during loading screens)
|
||||||
|
// * cap the delta at 100ms to prevent huge jumps in case of very infrequent polling
|
||||||
|
if (delta_time < 0.f || 0.25f < delta_time) {
|
||||||
|
delta_time = 0.f;
|
||||||
|
} else if (delta_time > 0.1f) {
|
||||||
|
delta_time = 0.1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// scale [0, 1] to [-1, 1] to simplify calculations
|
||||||
|
const auto delta_raw_value = (raw_value - 0.5f) * 2.f;
|
||||||
|
|
||||||
|
// target is one revolution per second at max speed at 1.0 sensitivity
|
||||||
|
auto adjusted_delta_value = delta_raw_value * delta_time;
|
||||||
|
|
||||||
|
// multiplier / divisor
|
||||||
|
if (this->getMultiplier() > 1) {
|
||||||
|
adjusted_delta_value *= this->getMultiplier();
|
||||||
|
} else if (this->getMultiplier() < -1) {
|
||||||
|
adjusted_delta_value /= -this->getMultiplier();
|
||||||
|
}
|
||||||
|
|
||||||
|
// sensitivity
|
||||||
|
if (this->isSensitivitySet()) {
|
||||||
|
adjusted_delta_value *= this->getSensitivity();
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate the new absolute value
|
||||||
|
this->rel_mode_absolute_value += adjusted_delta_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update for next poll
|
||||||
|
this->rel_mode_last_read_time_s = now;
|
||||||
|
this->rel_mode_absolute_value = normalizeAnalogValue(this->rel_mode_absolute_value);
|
||||||
|
return this->rel_mode_absolute_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Analog::getDelayedValue(float raw_value) {
|
||||||
|
const double delay_ms = static_cast<double>(this->getDelayMs());
|
||||||
|
if (delay_ms == 0.0) {
|
||||||
|
return raw_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// always push a new value
|
||||||
|
const auto now = get_performance_milliseconds();
|
||||||
|
this->delayed_inputs.emplace(now, raw_value);
|
||||||
|
|
||||||
|
// drain the queue down to reasonable length to prevent unconstrained growth
|
||||||
|
// this would accommodate 1 second at ~1000Hz which is overkill
|
||||||
|
// (UI only allows for 250ms of delay)
|
||||||
|
while (this->delayed_inputs.size() > 1024) {
|
||||||
|
this->delayed_inputs.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// pop until we find the oldest value still inside the delay window
|
||||||
|
while (this->delayed_inputs.size() > 1) {
|
||||||
|
const auto delta_t = now - this->delayed_inputs.front().time_in_ms;
|
||||||
|
if (delta_t <= delay_ms) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this->delayed_inputs.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->delayed_inputs.front().value;
|
||||||
|
}
|
||||||
|
|||||||
+46
-18
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@@ -13,12 +14,32 @@ namespace rawinput {
|
|||||||
class RawInputManager;
|
class RawInputManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace GameAPI::Analogs {
|
||||||
|
enum class AnalogType {
|
||||||
|
// default; values wrap around (below 0 turns into 1, over 1 is 0)
|
||||||
|
// knobs, turntables
|
||||||
|
Circular = 0,
|
||||||
|
|
||||||
|
// typical joystick that rests at the center and caps at [0, 1]
|
||||||
|
LinearCentered = 1,
|
||||||
|
|
||||||
|
// one-directional value (sliders and instruments like piano/drum velocity)
|
||||||
|
// starts at 0 and goes up to 1
|
||||||
|
LinearPositive = 2,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
struct AnalogMovingAverage {
|
struct AnalogMovingAverage {
|
||||||
double time_in_ms;
|
double time_in_ms;
|
||||||
float sine;
|
float sine;
|
||||||
float cosine;
|
float cosine;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AnalogDelayEntry {
|
||||||
|
double time_in_ms;
|
||||||
|
float value;
|
||||||
|
};
|
||||||
|
|
||||||
class Analog {
|
class Analog {
|
||||||
private:
|
private:
|
||||||
std::string name;
|
std::string name;
|
||||||
@@ -31,10 +52,11 @@ private:
|
|||||||
float last_state = 0.5f;
|
float last_state = 0.5f;
|
||||||
bool sensitivity_set = false;
|
bool sensitivity_set = false;
|
||||||
bool deadzone_set = false;
|
bool deadzone_set = false;
|
||||||
|
GameAPI::Analogs::AnalogType type = GameAPI::Analogs::AnalogType::Circular;
|
||||||
|
|
||||||
// smoothing function
|
// smoothing function
|
||||||
bool smoothing = false;
|
bool smoothing = false;
|
||||||
std::array<AnalogMovingAverage, ANALOG_HISTORY_CNT> vector_history;
|
std::array<AnalogMovingAverage, ANALOG_HISTORY_CNT> vector_history = {};
|
||||||
int vector_history_index = 0;
|
int vector_history_index = 0;
|
||||||
float smoothed_last_state = 0.f;
|
float smoothed_last_state = 0.f;
|
||||||
|
|
||||||
@@ -48,12 +70,13 @@ private:
|
|||||||
unsigned short divisor_region = 0;
|
unsigned short divisor_region = 0;
|
||||||
|
|
||||||
// relative input mode
|
// relative input mode
|
||||||
float absolute_value_for_rel_mode = 0.5f;
|
float rel_mode_absolute_value = 0.5f;
|
||||||
|
float rel_mode_last_read_time_s = 0.f;
|
||||||
bool relative_mode = false;
|
bool relative_mode = false;
|
||||||
|
|
||||||
// circular buffer (delayed input)
|
// delay
|
||||||
int delay_buffer_depth = 0;
|
uint32_t delay_ms = 0;
|
||||||
std::queue<float> delay_buffer;
|
std::queue<AnalogDelayEntry> delayed_inputs;
|
||||||
|
|
||||||
float calculateAngularDifference(float old_rads, float new_rads);
|
float calculateAngularDifference(float old_rads, float new_rads);
|
||||||
float normalizeAngle(float rads);
|
float normalizeAngle(float rads);
|
||||||
@@ -66,7 +89,8 @@ public:
|
|||||||
float override_state = 0.5f;
|
float override_state = 0.5f;
|
||||||
|
|
||||||
explicit Analog(std::string name) : name(std::move(name)) {
|
explicit Analog(std::string name) : name(std::move(name)) {
|
||||||
vector_history.fill({0.0, 0.f, 0.f});
|
};
|
||||||
|
explicit Analog(std::string name, GameAPI::Analogs::AnalogType type) : name(std::move(name)), type(type) {
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string getDisplayString(rawinput::RawInputManager* manager);
|
std::string getDisplayString(rawinput::RawInputManager* manager);
|
||||||
@@ -74,6 +98,8 @@ public:
|
|||||||
float applyAngularSensitivity(float raw_rads);
|
float applyAngularSensitivity(float raw_rads);
|
||||||
float applyMultiplier(float raw_value);
|
float applyMultiplier(float raw_value);
|
||||||
float applyDeadzone(float raw_value);
|
float applyDeadzone(float raw_value);
|
||||||
|
float getRelativeModeValue(float raw_value);
|
||||||
|
float getDelayedValue(float raw_value);
|
||||||
|
|
||||||
inline bool isSet() {
|
inline bool isSet() {
|
||||||
if (this->override_enabled) {
|
if (this->override_enabled) {
|
||||||
@@ -90,8 +116,8 @@ public:
|
|||||||
deadzone_mirror = false;
|
deadzone_mirror = false;
|
||||||
setMultiplier(1);
|
setMultiplier(1);
|
||||||
setRelativeMode(false);
|
setRelativeMode(false);
|
||||||
setDelayBufferDepth(0);
|
|
||||||
setLastState(0.5f);
|
setLastState(0.5f);
|
||||||
|
setDelayMs(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void clearBindings() {
|
inline void clearBindings() {
|
||||||
@@ -193,25 +219,27 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void setRelativeMode(bool relative_mode) {
|
inline void setRelativeMode(bool relative_mode) {
|
||||||
|
if (relative_mode) {
|
||||||
|
this->smoothing = false;
|
||||||
|
}
|
||||||
this->relative_mode = relative_mode;
|
this->relative_mode = relative_mode;
|
||||||
this->absolute_value_for_rel_mode = 0.5f;
|
this->rel_mode_absolute_value = 0.5f;
|
||||||
|
this->rel_mode_last_read_time_s = 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline float getAbsoluteValue(float relative_delta) {
|
inline GameAPI::Analogs::AnalogType getType() const {
|
||||||
this->absolute_value_for_rel_mode =
|
return this->type;
|
||||||
normalizeAnalogValue(this->absolute_value_for_rel_mode + relative_delta);
|
|
||||||
return this->absolute_value_for_rel_mode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int getDelayBufferDepth() const {
|
inline void setType(GameAPI::Analogs::AnalogType type) {
|
||||||
return this->delay_buffer_depth;
|
this->type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void setDelayBufferDepth(int depth) {
|
inline uint32_t getDelayMs() const {
|
||||||
this->delay_buffer_depth = depth;
|
return this->delay_ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::queue<float> &getDelayBuffer() {
|
inline void setDelayMs(uint32_t delay_ms) {
|
||||||
return this->delay_buffer;
|
this->delay_ms = delay_ms;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+161
-63
@@ -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;
|
||||||
@@ -658,81 +679,98 @@ float GameAPI::Analogs::getState(rawinput::Device *device, Analog &analog) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// deadzone
|
// deadzone
|
||||||
if (analog.isDeadzoneSet()) {
|
// do not apply deadzone to circular analogs since it doesn't make sense (except in relative mode)
|
||||||
|
if (analog.isDeadzoneSet() &&
|
||||||
|
(analog.getType() != AnalogType::Circular || analog.isRelativeMode())) {
|
||||||
value = analog.applyDeadzone(value);
|
value = analog.applyDeadzone(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (analog.isRelativeMode()) {
|
if (analog.getType() == AnalogType::Circular) {
|
||||||
float relative_delta = value - 0.5f;
|
|
||||||
// built-in scaling to make values reasonable
|
|
||||||
relative_delta /= 80.f;
|
|
||||||
|
|
||||||
// integer multiplier/divisor
|
if (analog.isRelativeMode()) {
|
||||||
const auto mult = analog.getMultiplier();
|
value = analog.getRelativeModeValue(value);
|
||||||
if (mult < -1) {
|
|
||||||
relative_delta /= -mult;
|
|
||||||
} else if (1 < mult) {
|
|
||||||
relative_delta *= mult;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sensitivity (ranges from 0.0 to 4.0)
|
} else {
|
||||||
if (analog.isSensitivitySet()) {
|
// integer multiplier
|
||||||
relative_delta *= analog.getSensitivity();
|
value = analog.applyMultiplier(value);
|
||||||
}
|
|
||||||
|
|
||||||
// translate relative movement to absolute value
|
// smoothing/sensitivity
|
||||||
value = analog.getAbsoluteValue(relative_delta);
|
if (analog.getSmoothing() || analog.isSensitivitySet()) {
|
||||||
|
float rads = value * (float) M_TAU;
|
||||||
|
|
||||||
} else {
|
// smoothing
|
||||||
// integer multiplier
|
if (analog.getSmoothing()) {
|
||||||
value = analog.applyMultiplier(value);
|
|
||||||
|
|
||||||
// smoothing/sensitivity
|
// preserve direction
|
||||||
if (analog.getSmoothing() || analog.isSensitivitySet()) {
|
if (rads >= M_TAU) {
|
||||||
float rads = value * (float) M_TAU;
|
rads -= 0.0001f;
|
||||||
|
}
|
||||||
|
|
||||||
// smoothing
|
// calculate angle
|
||||||
if (analog.getSmoothing()) {
|
rads = analog.getSmoothedValue(rads);
|
||||||
|
|
||||||
// preserve direction
|
|
||||||
if (rads >= M_TAU) {
|
|
||||||
rads -= 0.0001f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate angle
|
// sensitivity
|
||||||
rads = analog.getSmoothedValue(rads);
|
if (analog.isSensitivitySet()) {
|
||||||
|
rads = analog.applyAngularSensitivity(rads);
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply to value
|
||||||
|
value = rads * (float) M_1_TAU;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// sensitivity
|
||||||
|
if (analog.isSensitivitySet()) {
|
||||||
|
// adjust curve
|
||||||
|
// values < 1.f : less sensitive around neutral
|
||||||
|
// values > 1.f : more sensitive around neutral
|
||||||
|
float curve = analog.getSensitivity();
|
||||||
|
if (curve <= 0.f) {
|
||||||
|
curve = 0.01f;
|
||||||
|
}
|
||||||
|
curve = 1.f / curve;
|
||||||
|
|
||||||
|
if (analog.getType() == AnalogType::LinearCentered) {
|
||||||
|
// convert 0.0..1.0 to -1.0..+1.0
|
||||||
|
float signed_raw = (value - 0.5f) * 2.0f;
|
||||||
|
// apply curve
|
||||||
|
float sign = signed_raw < 0.0f ? -1.0f : 1.0f;
|
||||||
|
float magnitude = fabsf(signed_raw);
|
||||||
|
float curved = sign * powf(magnitude, curve);
|
||||||
|
// convert back to 0.0..1.0
|
||||||
|
value = curved * 0.5f + 0.5f;
|
||||||
|
} else {
|
||||||
|
value = powf(value, curve);
|
||||||
}
|
}
|
||||||
|
|
||||||
// sensitivity
|
value = std::clamp(value, 0.f, 1.f);
|
||||||
if (analog.isSensitivitySet()) {
|
}
|
||||||
rads = analog.applyAngularSensitivity(rads);
|
|
||||||
|
// multiplier / divisor
|
||||||
|
if (analog.getMultiplier() < -1) {
|
||||||
|
if (analog.getType() == AnalogType::LinearCentered) {
|
||||||
|
value = (value - 0.5f) / (-analog.getMultiplier()) + 0.5f;
|
||||||
|
} else {
|
||||||
|
value /= -analog.getMultiplier();
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply to value
|
value = std::clamp(value, 0.f, 1.f);
|
||||||
value = rads * (float) M_1_TAU;
|
|
||||||
|
} else if (analog.getMultiplier() > 1) {
|
||||||
|
if (analog.getType() == AnalogType::LinearCentered) {
|
||||||
|
value = (value - 0.5f) * analog.getMultiplier() + 0.5f;
|
||||||
|
} else {
|
||||||
|
value *= analog.getMultiplier();
|
||||||
|
}
|
||||||
|
|
||||||
|
value = std::clamp(value, 0.f, 1.f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// delay
|
// delay
|
||||||
if (0 < analog.getDelayBufferDepth()) {
|
if (analog.getDelayMs() > 0) {
|
||||||
auto& queue = analog.getDelayBuffer();
|
value = analog.getDelayedValue(value);
|
||||||
|
|
||||||
// ensure the queue isn't too long; drop old values
|
|
||||||
while (analog.getDelayBufferDepth() <= (int)queue.size()) {
|
|
||||||
queue.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
// always push new value
|
|
||||||
queue.push(value);
|
|
||||||
|
|
||||||
// get a new value to return
|
|
||||||
if ((int)queue.size() < analog.getDelayBufferDepth()) {
|
|
||||||
// not enough in the queue, stall for now, shouldn't happen often
|
|
||||||
value = analog.getLastState();
|
|
||||||
} else {
|
|
||||||
value = queue.front();
|
|
||||||
queue.pop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -767,6 +805,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;
|
||||||
}
|
}
|
||||||
@@ -806,6 +856,42 @@ std::vector<Analog> GameAPI::Analogs::sortAnalogs(
|
|||||||
return sorted;
|
return sorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<Analog> sortAnalogsWithTypeInternal(
|
||||||
|
std::vector<Analog> &analogs,
|
||||||
|
const std::initializer_list<GameAPI::Analogs::AnalogWithType> list) {
|
||||||
|
|
||||||
|
std::vector<Analog> sorted;
|
||||||
|
|
||||||
|
bool analog_found;
|
||||||
|
for (auto &a : list) {
|
||||||
|
analog_found = false;
|
||||||
|
|
||||||
|
for (auto &analog : analogs) {
|
||||||
|
if (a.name == analog.getName()) {
|
||||||
|
analog_found = true;
|
||||||
|
analog.setType(a.type);
|
||||||
|
sorted.push_back(analog);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!analog_found) {
|
||||||
|
sorted.emplace_back(a.name, a.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sorted;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameAPI::Analogs::sortAnalogsWithType(
|
||||||
|
std::vector<Analog> *analogs,
|
||||||
|
const std::initializer_list<AnalogWithType> list) {
|
||||||
|
|
||||||
|
if (analogs) {
|
||||||
|
*analogs = sortAnalogsWithTypeInternal(*analogs, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
float GameAPI::Analogs::getState(rawinput::RawInputManager *manager, Analog &analog) {
|
float GameAPI::Analogs::getState(rawinput::RawInputManager *manager, Analog &analog) {
|
||||||
|
|
||||||
// check override
|
// check override
|
||||||
@@ -822,7 +908,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 +990,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 +1077,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 +1114,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-9
@@ -85,14 +85,15 @@ namespace GameAPI {
|
|||||||
const std::vector<Analog> &analogs,
|
const std::vector<Analog> &analogs,
|
||||||
const std::vector<std::string> &analog_names);
|
const std::vector<std::string> &analog_names);
|
||||||
|
|
||||||
template<typename T>
|
struct AnalogWithType {
|
||||||
void sortAnalogs(std::vector<Analog> *analogs, T t) {
|
const std::string name;
|
||||||
const std::vector<std::string> analog_names { t };
|
const AnalogType type;
|
||||||
|
|
||||||
if (analogs) {
|
AnalogWithType(std::string name, AnalogType type) :
|
||||||
*analogs = GameAPI::Analogs::sortAnalogs(*analogs, analog_names);
|
name(std::move(name)), type(type) {}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
void sortAnalogsWithType(std::vector<Analog> *analogs, const std::initializer_list<AnalogWithType> list);
|
||||||
|
|
||||||
template<typename T, typename... Rest>
|
template<typename T, typename... Rest>
|
||||||
void sortAnalogs(std::vector<Analog> *analogs, T t, Rest... rest) {
|
void sortAnalogs(std::vector<Analog> *analogs, T t, Rest... rest) {
|
||||||
@@ -103,7 +104,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 +136,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);
|
||||||
|
|
||||||
|
|||||||
@@ -306,7 +306,10 @@ std::string Button::getDisplayString(rawinput::RawInputManager* manager) {
|
|||||||
std::string vKeyString = fmt::format("{:#x}", vKey);
|
std::string vKeyString = fmt::format("{:#x}", vKey);
|
||||||
|
|
||||||
// device must be existing
|
// device must be existing
|
||||||
if (this->device_identifier.empty() && vKey == INVALID_VKEY) {
|
if (this->isNaive() && vKey == INVALID_VKEY) {
|
||||||
|
if (this->getInvert()) {
|
||||||
|
return "(always on)";
|
||||||
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,6 +420,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;
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-15
@@ -28,7 +28,7 @@ Config::Config() {
|
|||||||
this->status = false;
|
this->status = false;
|
||||||
if (CONFIG_PATH_OVERRIDE.length() > 0) {
|
if (CONFIG_PATH_OVERRIDE.length() > 0) {
|
||||||
this->configLocation = CONFIG_PATH_OVERRIDE;
|
this->configLocation = CONFIG_PATH_OVERRIDE;
|
||||||
log_info("cfg", "using custom config file: {}", this->configLocation.string());
|
log_info("cfg", "using custom config file: {}", this->configLocation);
|
||||||
} else {
|
} else {
|
||||||
this->configLocation = std::filesystem::path(_wgetenv(L"APPDATA")) / L"spicetools.xml";
|
this->configLocation = std::filesystem::path(_wgetenv(L"APPDATA")) / L"spicetools.xml";
|
||||||
// avoids logging the expanded appdata path as it contains user name
|
// avoids logging the expanded appdata path as it contains user name
|
||||||
@@ -57,7 +57,7 @@ Config::Config() {
|
|||||||
this->firstFillConfigFile();
|
this->firstFillConfigFile();
|
||||||
break;
|
break;
|
||||||
case tinyxml2::XMLError::XML_ERROR_FILE_COULD_NOT_BE_OPENED:
|
case tinyxml2::XMLError::XML_ERROR_FILE_COULD_NOT_BE_OPENED:
|
||||||
log_fatal("cfg", "could not open config file: {}", this->configLocation.string());
|
log_fatal("cfg", "could not open config file: {}", this->configLocation);
|
||||||
break;
|
break;
|
||||||
case tinyxml2::XMLError::XML_ERROR_FILE_NOT_FOUND:
|
case tinyxml2::XMLError::XML_ERROR_FILE_NOT_FOUND:
|
||||||
this->createConfigFile();
|
this->createConfigFile();
|
||||||
@@ -72,7 +72,7 @@ Config::Config() {
|
|||||||
case tinyxml2::XMLError::XML_ERROR_PARSING_UNKNOWN:
|
case tinyxml2::XMLError::XML_ERROR_PARSING_UNKNOWN:
|
||||||
case tinyxml2::XMLError::XML_ERROR_MISMATCHED_ELEMENT:
|
case tinyxml2::XMLError::XML_ERROR_MISMATCHED_ELEMENT:
|
||||||
case tinyxml2::XMLError::XML_ERROR_PARSING:
|
case tinyxml2::XMLError::XML_ERROR_PARSING:
|
||||||
log_warning("cfg", "Couldn't read config file: {}", this->configLocation.string());
|
log_warning("cfg", "Couldn't read config file: {}", this->configLocation);
|
||||||
this->createConfigFile();
|
this->createConfigFile();
|
||||||
this->firstFillConfigFile();
|
this->firstFillConfigFile();
|
||||||
break;
|
break;
|
||||||
@@ -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());
|
||||||
@@ -248,7 +253,7 @@ bool Config::addGame(Game &game) {
|
|||||||
bool smoothing = false;
|
bool smoothing = false;
|
||||||
int multiplier = 1;
|
int multiplier = 1;
|
||||||
bool relative_mode = false;
|
bool relative_mode = false;
|
||||||
int delay_buffer_depth = 0;
|
uint32_t delay = 0;
|
||||||
tinyxml2::XMLError err1 = gameAnalogNode->QueryIntAttribute("index", &index);
|
tinyxml2::XMLError err1 = gameAnalogNode->QueryIntAttribute("index", &index);
|
||||||
gameAnalogNode->QueryFloatAttribute("sensivity", &sensitivity);
|
gameAnalogNode->QueryFloatAttribute("sensivity", &sensitivity);
|
||||||
gameAnalogNode->QueryFloatAttribute("deadzone", &deadzone);
|
gameAnalogNode->QueryFloatAttribute("deadzone", &deadzone);
|
||||||
@@ -257,7 +262,7 @@ bool Config::addGame(Game &game) {
|
|||||||
gameAnalogNode->QueryBoolAttribute("smoothing", &smoothing);
|
gameAnalogNode->QueryBoolAttribute("smoothing", &smoothing);
|
||||||
gameAnalogNode->QueryIntAttribute("multiplier", &multiplier);
|
gameAnalogNode->QueryIntAttribute("multiplier", &multiplier);
|
||||||
gameAnalogNode->QueryBoolAttribute("relative", &relative_mode);
|
gameAnalogNode->QueryBoolAttribute("relative", &relative_mode);
|
||||||
gameAnalogNode->QueryIntAttribute("delay", &delay_buffer_depth);
|
gameAnalogNode->QueryUnsignedAttribute("delay_ms", &delay);
|
||||||
const char *devid = gameAnalogNode->Attribute("devid");
|
const char *devid = gameAnalogNode->Attribute("devid");
|
||||||
|
|
||||||
if (err1 != tinyxml2::XMLError::XML_SUCCESS || !devid) {
|
if (err1 != tinyxml2::XMLError::XML_SUCCESS || !devid) {
|
||||||
@@ -273,7 +278,7 @@ bool Config::addGame(Game &game) {
|
|||||||
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
|
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
|
||||||
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
|
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
|
||||||
gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
|
gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
|
||||||
gameAnalogNode->SetAttribute("delay", it.getDelayBufferDepth());
|
gameAnalogNode->SetAttribute("delay_ms", it.getDelayMs());
|
||||||
gameAnalogsNode->InsertEndChild(gameAnalogNode);
|
gameAnalogsNode->InsertEndChild(gameAnalogNode);
|
||||||
} else {
|
} else {
|
||||||
it.setIndex(static_cast<unsigned short int>(index));
|
it.setIndex(static_cast<unsigned short int>(index));
|
||||||
@@ -285,7 +290,7 @@ bool Config::addGame(Game &game) {
|
|||||||
it.setSmoothing(smoothing);
|
it.setSmoothing(smoothing);
|
||||||
it.setMultiplier(multiplier);
|
it.setMultiplier(multiplier);
|
||||||
it.setRelativeMode(relative_mode);
|
it.setRelativeMode(relative_mode);
|
||||||
it.setDelayBufferDepth(delay_buffer_depth);
|
it.setDelayMs(delay);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
gameAnalogNode = this->configFile.NewElement("analog");
|
gameAnalogNode = this->configFile.NewElement("analog");
|
||||||
@@ -298,7 +303,7 @@ bool Config::addGame(Game &game) {
|
|||||||
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
|
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
|
||||||
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
|
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
|
||||||
gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
|
gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
|
||||||
gameAnalogNode->SetAttribute("delay", it.getDelayBufferDepth());
|
gameAnalogNode->SetAttribute("delay_ms", it.getDelayMs());
|
||||||
gameAnalogNode->SetAttribute("devid", it.getDeviceIdentifier().c_str());
|
gameAnalogNode->SetAttribute("devid", it.getDeviceIdentifier().c_str());
|
||||||
gameAnalogsNode->InsertEndChild(gameAnalogNode);
|
gameAnalogsNode->InsertEndChild(gameAnalogNode);
|
||||||
}
|
}
|
||||||
@@ -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());
|
||||||
@@ -452,7 +458,7 @@ bool Config::addGame(Game &game) {
|
|||||||
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
|
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
|
||||||
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
|
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
|
||||||
gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
|
gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
|
||||||
gameAnalogNode->SetAttribute("delay", it.getDelayBufferDepth());
|
gameAnalogNode->SetAttribute("delay_ms", it.getDelayMs());
|
||||||
gameAnalogsNode->InsertEndChild(gameAnalogNode);
|
gameAnalogsNode->InsertEndChild(gameAnalogNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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", "");
|
||||||
@@ -684,7 +692,7 @@ bool Config::updateBinding(const Game &game, const Analog &analog) {
|
|||||||
gameAnalogNode->SetAttribute("smoothing", analog.getSmoothing());
|
gameAnalogNode->SetAttribute("smoothing", analog.getSmoothing());
|
||||||
gameAnalogNode->SetAttribute("multiplier", analog.getMultiplier());
|
gameAnalogNode->SetAttribute("multiplier", analog.getMultiplier());
|
||||||
gameAnalogNode->SetAttribute("relative", analog.isRelativeMode());
|
gameAnalogNode->SetAttribute("relative", analog.isRelativeMode());
|
||||||
gameAnalogNode->SetAttribute("delay", analog.getDelayBufferDepth());
|
gameAnalogNode->SetAttribute("delay_ms", analog.getDelayMs());
|
||||||
gameAnalogNode->SetAttribute("devid", analog.getDeviceIdentifier().c_str());
|
gameAnalogNode->SetAttribute("devid", analog.getDeviceIdentifier().c_str());
|
||||||
} else {
|
} else {
|
||||||
gameAnalogNode = this->configFile.NewElement("analog");
|
gameAnalogNode = this->configFile.NewElement("analog");
|
||||||
@@ -696,7 +704,7 @@ bool Config::updateBinding(const Game &game, const Analog &analog) {
|
|||||||
gameAnalogNode->SetAttribute("smoothing", analog.getSmoothing());
|
gameAnalogNode->SetAttribute("smoothing", analog.getSmoothing());
|
||||||
gameAnalogNode->SetAttribute("multiplier", analog.getMultiplier());
|
gameAnalogNode->SetAttribute("multiplier", analog.getMultiplier());
|
||||||
gameAnalogNode->SetAttribute("relative", analog.isRelativeMode());
|
gameAnalogNode->SetAttribute("relative", analog.isRelativeMode());
|
||||||
gameAnalogNode->SetAttribute("delay", analog.getDelayBufferDepth());
|
gameAnalogNode->SetAttribute("delay_ms", analog.getDelayMs());
|
||||||
gameAnalogNode->SetAttribute("devid", analog.getDeviceIdentifier().c_str());
|
gameAnalogNode->SetAttribute("devid", analog.getDeviceIdentifier().c_str());
|
||||||
gameAnalogsNode->InsertEndChild(gameAnalogNode);
|
gameAnalogsNode->InsertEndChild(gameAnalogNode);
|
||||||
}
|
}
|
||||||
@@ -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) {
|
||||||
@@ -1130,7 +1142,7 @@ std::vector<Analog> Config::getAnalogs(const std::string &gameName) {
|
|||||||
bool smoothing = false;
|
bool smoothing = false;
|
||||||
int multiplier = 1;
|
int multiplier = 1;
|
||||||
bool relative_mode = false;
|
bool relative_mode = false;
|
||||||
int delay_buffer_depth = 0;
|
uint32_t delay = 0;
|
||||||
gameAnalogNode->QueryIntAttribute("index", &index);
|
gameAnalogNode->QueryIntAttribute("index", &index);
|
||||||
gameAnalogNode->QueryFloatAttribute("sensivity", &sensitivity);
|
gameAnalogNode->QueryFloatAttribute("sensivity", &sensitivity);
|
||||||
gameAnalogNode->QueryFloatAttribute("deadzone", &deadzone);
|
gameAnalogNode->QueryFloatAttribute("deadzone", &deadzone);
|
||||||
@@ -1139,7 +1151,7 @@ std::vector<Analog> Config::getAnalogs(const std::string &gameName) {
|
|||||||
gameAnalogNode->QueryBoolAttribute("smoothing", &smoothing);
|
gameAnalogNode->QueryBoolAttribute("smoothing", &smoothing);
|
||||||
gameAnalogNode->QueryIntAttribute("multiplier", &multiplier);
|
gameAnalogNode->QueryIntAttribute("multiplier", &multiplier);
|
||||||
gameAnalogNode->QueryBoolAttribute("relative", &relative_mode);
|
gameAnalogNode->QueryBoolAttribute("relative", &relative_mode);
|
||||||
gameAnalogNode->QueryIntAttribute("delay", &delay_buffer_depth);
|
gameAnalogNode->QueryUnsignedAttribute("delay_ms", &delay);
|
||||||
const char *devid = gameAnalogNode->Attribute("devid");
|
const char *devid = gameAnalogNode->Attribute("devid");
|
||||||
|
|
||||||
// create analog and add to list
|
// create analog and add to list
|
||||||
@@ -1152,7 +1164,7 @@ std::vector<Analog> Config::getAnalogs(const std::string &gameName) {
|
|||||||
analog.setSmoothing(smoothing);
|
analog.setSmoothing(smoothing);
|
||||||
analog.setMultiplier(multiplier);
|
analog.setMultiplier(multiplier);
|
||||||
analog.setRelativeMode(relative_mode);
|
analog.setRelativeMode(relative_mode);
|
||||||
analog.setDelayBufferDepth(delay_buffer_depth);
|
analog.setDelayMs(delay);
|
||||||
if (devid) {
|
if (devid) {
|
||||||
analog.setDeviceIdentifier(devid);
|
analog.setDeviceIdentifier(devid);
|
||||||
}
|
}
|
||||||
@@ -1315,7 +1327,7 @@ void Config::saveConfigFile() {
|
|||||||
// create a .tmp file and write to it...
|
// create a .tmp file and write to it...
|
||||||
const auto xml_result = this->configFile.SaveFile(this->configLocationTemp.c_str(), false);
|
const auto xml_result = this->configFile.SaveFile(this->configLocationTemp.c_str(), false);
|
||||||
if (xml_result != tinyxml2::XMLError::XML_SUCCESS) {
|
if (xml_result != tinyxml2::XMLError::XML_SUCCESS) {
|
||||||
log_info("cfg", "failed to write file: {}", this->configLocationTemp.string());
|
log_info("cfg", "failed to write file: {}", this->configLocationTemp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// copy the .tmp file to the main file...
|
// copy the .tmp file to the main file...
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +70,7 @@ namespace overlay::windows {
|
|||||||
el->SetAttribute("smoothing", analog.smoothing);
|
el->SetAttribute("smoothing", analog.smoothing);
|
||||||
el->SetAttribute("multiplier", analog.multiplier);
|
el->SetAttribute("multiplier", analog.multiplier);
|
||||||
el->SetAttribute("relative", analog.relative_mode);
|
el->SetAttribute("relative", analog.relative_mode);
|
||||||
el->SetAttribute("delay", analog.delay_buffer_depth);
|
el->SetAttribute("delay_ms", analog.delay_ms);
|
||||||
parent->InsertEndChild(el);
|
parent->InsertEndChild(el);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +93,7 @@ namespace overlay::windows {
|
|||||||
el->QueryBoolAttribute("smoothing", &a.smoothing);
|
el->QueryBoolAttribute("smoothing", &a.smoothing);
|
||||||
el->QueryIntAttribute("multiplier", &a.multiplier);
|
el->QueryIntAttribute("multiplier", &a.multiplier);
|
||||||
el->QueryBoolAttribute("relative", &a.relative_mode);
|
el->QueryBoolAttribute("relative", &a.relative_mode);
|
||||||
el->QueryIntAttribute("delay", &a.delay_buffer_depth);
|
el->QueryUnsignedAttribute("delay_ms", &a.delay_ms);
|
||||||
|
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -20,10 +20,10 @@
|
|||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
</dependency>
|
</dependency>
|
||||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
<security>
|
<security>
|
||||||
<requestedPrivileges>
|
<requestedPrivileges>
|
||||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
|
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
|
||||||
</requestedPrivileges>
|
</requestedPrivileges>
|
||||||
</security>
|
</security>
|
||||||
</trustInfo>
|
</trustInfo>
|
||||||
</assembly>
|
</assembly>
|
||||||
@@ -21,7 +21,7 @@ namespace cfg {
|
|||||||
if (SCREEN_RESIZE_CFG_PATH_OVERRIDE.has_value()) {
|
if (SCREEN_RESIZE_CFG_PATH_OVERRIDE.has_value()) {
|
||||||
this->config_path = SCREEN_RESIZE_CFG_PATH_OVERRIDE.value();
|
this->config_path = SCREEN_RESIZE_CFG_PATH_OVERRIDE.value();
|
||||||
if (fileutils::file_exists(this->config_path)) {
|
if (fileutils::file_exists(this->config_path)) {
|
||||||
log_info("ScreenResize", "loading config from: {}", this->config_path.string());
|
log_info("ScreenResize", "loading config from: {}", this->config_path);
|
||||||
file_exists = true;
|
file_exists = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Vendored
+1
@@ -20,6 +20,7 @@ Usage:
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|||||||
Vendored
+1
@@ -39,6 +39,7 @@
|
|||||||
#define ROBIN_HOOD_VERSION_PATCH 2 // for backwards-compatible bug fixes
|
#define ROBIN_HOOD_VERSION_PATCH 2 // for backwards-compatible bug fixes
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|||||||
@@ -38,11 +38,12 @@ std::vector<Analog> &games::bc::get_analogs() {
|
|||||||
if (analogs.empty()) {
|
if (analogs.empty()) {
|
||||||
analogs = GameAPI::Analogs::getAnalogs("Busou Shinki: Armored Princess Battle Conductor");
|
analogs = GameAPI::Analogs::getAnalogs("Busou Shinki: Armored Princess Battle Conductor");
|
||||||
|
|
||||||
GameAPI::Analogs::sortAnalogs(
|
using GameAPI::Analogs::AnalogType;
|
||||||
&analogs,
|
|
||||||
"Stick X",
|
GameAPI::Analogs::sortAnalogsWithType(&analogs, {
|
||||||
"Stick Y"
|
{ "Stick X", AnalogType::LinearCentered },
|
||||||
);
|
{ "Stick Y", AnalogType::LinearCentered }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return analogs;
|
return analogs;
|
||||||
|
|||||||
@@ -45,13 +45,14 @@ std::vector<Analog> &games::ccj::get_analogs() {
|
|||||||
if (analogs.empty()) {
|
if (analogs.empty()) {
|
||||||
analogs = GameAPI::Analogs::getAnalogs("Chase Chase Jokers");
|
analogs = GameAPI::Analogs::getAnalogs("Chase Chase Jokers");
|
||||||
|
|
||||||
GameAPI::Analogs::sortAnalogs(
|
using GameAPI::Analogs::AnalogType;
|
||||||
&analogs,
|
|
||||||
"Joystick X",
|
GameAPI::Analogs::sortAnalogsWithType(&analogs, {
|
||||||
"Joystick Y",
|
{ "Joystick X", AnalogType::LinearCentered },
|
||||||
"Trackball DX",
|
{ "Joystick Y", AnalogType::LinearCentered },
|
||||||
"Trackball DY"
|
{ "Trackball DX", AnalogType::LinearCentered },
|
||||||
);
|
{ "Trackball DY", AnalogType::LinearCentered }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return analogs;
|
return analogs;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
#include "games/io.h"
|
#include "games/io.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
|
|
||||||
@@ -112,7 +113,7 @@ namespace games::ccj {
|
|||||||
static std::chrono::time_point<std::chrono::steady_clock> lastModified = std::chrono::steady_clock::now();
|
static std::chrono::time_point<std::chrono::steady_clock> lastModified = std::chrono::steady_clock::now();
|
||||||
static std::chrono::milliseconds debounceDuration(100);
|
static std::chrono::milliseconds debounceDuration(100);
|
||||||
auto currentTime = std::chrono::steady_clock::now();
|
auto currentTime = std::chrono::steady_clock::now();
|
||||||
bool pressed = get_async_secondary_mouse() & 0x8000;
|
bool pressed = get_async_secondary_mouse();
|
||||||
bool focused = GetForegroundWindow() == hWnd;
|
bool focused = GetForegroundWindow() == hWnd;
|
||||||
|
|
||||||
if (focused && MOUSE_TRACKBALL_USE_TOGGLE && pressed && (currentTime - lastModified > debounceDuration)) {
|
if (focused && MOUSE_TRACKBALL_USE_TOGGLE && pressed && (currentTime - lastModified > debounceDuration)) {
|
||||||
@@ -243,6 +244,7 @@ namespace games::ccj {
|
|||||||
log_info("trackball", "thread start, use mouse: {}, toggle: {}", MOUSE_TRACKBALL, MOUSE_TRACKBALL_USE_TOGGLE);
|
log_info("trackball", "thread start, use mouse: {}, toggle: {}", MOUSE_TRACKBALL, MOUSE_TRACKBALL_USE_TOGGLE);
|
||||||
|
|
||||||
tbThread = new std::thread([&] {
|
tbThread = new std::thread([&] {
|
||||||
|
timeutils::PreciseSleepTimer timer;
|
||||||
while (tbThreadRunning) {
|
while (tbThreadRunning) {
|
||||||
if (hWnd && wndProc) {
|
if (hWnd && wndProc) {
|
||||||
wndProc(hWnd, WM_INPUT, RIM_INPUT, (LPARAM)fakeHandle);
|
wndProc(hWnd, WM_INPUT, RIM_INPUT, (LPARAM)fakeHandle);
|
||||||
@@ -251,7 +253,7 @@ namespace games::ccj {
|
|||||||
if (!tbThreadRunning)
|
if (!tbThreadRunning)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
std::this_thread::sleep_for(10ms);
|
timer.sleep(10);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "p3io/usbmem.h"
|
#include "p3io/usbmem.h"
|
||||||
|
|
||||||
#include "p4io/p4io.h"
|
#include "p4io/p4io.h"
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
using namespace acioemu;
|
using namespace acioemu;
|
||||||
|
|
||||||
@@ -53,9 +54,9 @@ namespace games::ddr {
|
|||||||
return SendMessage_real(hWnd, Msg, wParam, lParam);
|
return SendMessage_real(hWnd, Msg, wParam, lParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool contains_only_ascii(const std::string& str) {
|
bool contains_only_ascii(const std::wstring& str) {
|
||||||
for (auto c: str) {
|
for (auto c: str) {
|
||||||
if (static_cast<unsigned char>(c) > 127) {
|
if (c > 127) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,24 +81,24 @@ namespace games::ddr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fileutils::dir_exists(dir)) {
|
if (fileutils::dir_exists(dir)) {
|
||||||
log_info("ddr", "looking for codecs in this directory: {}", dir.string());
|
log_info("ddr", "looking for codecs in this directory: {}", dir);
|
||||||
} else {
|
} else {
|
||||||
log_info("ddr", "codecs directory not found: {}", dir.string());
|
log_info("ddr", "codecs directory not found: {}", dir);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &file : std::filesystem::directory_iterator(dir)) {
|
for (const auto &file : std::filesystem::directory_iterator(dir)) {
|
||||||
const auto &filename = file.path().filename();
|
const auto &filename = file.path().filename();
|
||||||
const auto extension = strtolower(filename.extension().string());
|
const auto extension = filename.extension().wstring();
|
||||||
|
|
||||||
if (extension != ".dll") {
|
if (wcsicmp(extension.c_str(), L".dll") != 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_info("ddr", "found DLL: {}, size: {} bytes", filename.string(), file.file_size());
|
log_info("ddr", "found DLL: {}, size: {} bytes", filename, file.file_size());
|
||||||
if (filename == "k-clvsd.dll" || filename.string().find("xactengine") == 0) {
|
if (filename == "k-clvsd.dll" || filename.wstring().find(L"xactengine") == 0) {
|
||||||
const std::wstring wcmd = L"regsvr32.exe /s \"" + file.path().wstring() + L"\"";
|
const std::wstring wcmd = L"regsvr32.exe /s \"" + file.path().wstring() + L"\"";
|
||||||
const std::string cmd = "regsvr32.exe /s \"" + file.path().string() + "\"";
|
const std::string cmd = fmt::format("regsvr32.exe /s \"{}\"", file.path());
|
||||||
|
|
||||||
int result = 0;
|
int result = 0;
|
||||||
std::thread t([wcmd, &result]() {
|
std::thread t([wcmd, &result]() {
|
||||||
@@ -111,12 +112,12 @@ namespace games::ddr {
|
|||||||
log_warning("ddr", "`{}` failed, returned {}", cmd, result);
|
log_warning("ddr", "`{}` failed, returned {}", cmd, result);
|
||||||
deferredlogs::defer_error_messages({
|
deferredlogs::defer_error_messages({
|
||||||
"DDR codec registration failure",
|
"DDR codec registration failure",
|
||||||
fmt::format(" {} failed to register with error {}", filename.string(), result)});
|
fmt::format(" {} failed to register with error {}", filename, result)});
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::once_flag printed;
|
static std::once_flag printed;
|
||||||
std::call_once(printed, [file]() {
|
std::call_once(printed, [file]() {
|
||||||
if (!contains_only_ascii(file.path().string())) {
|
if (!contains_only_ascii(file.path().wstring())) {
|
||||||
log_warning(
|
log_warning(
|
||||||
"ddr",
|
"ddr",
|
||||||
"BAD PATH ERROR\n\n"
|
"BAD PATH ERROR\n\n"
|
||||||
@@ -290,4 +291,50 @@ namespace games::ddr {
|
|||||||
// dispose device hook
|
// dispose device hook
|
||||||
devicehook_dispose();
|
devicehook_dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void get_analog_xy_axis(Analog &analog, bool &less, bool &more) {
|
||||||
|
if (!analog.isSet()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto value = GameAPI::Analogs::getState(RI_MGR, analog);
|
||||||
|
|
||||||
|
// stepmania linux source:
|
||||||
|
// https://github.com/stepmania/stepmania/blob/d55acb1ba26f1c5b5e3048d6d6c0bd116625216f/src/arch/InputHandler/InputHandler_Linux_Event.cpp#L410
|
||||||
|
|
||||||
|
// for example, value cap with range [0, 255]:
|
||||||
|
// 127 = 0.498, neutral
|
||||||
|
// 128 = 0.502, both
|
||||||
|
// apparently some adapters report values like above; this obviously makes a lot of
|
||||||
|
// assumptions about bit width of the HID value cap and how the adapter reports
|
||||||
|
// neutral/both values, but this is good enough for parity with stepmania and its many forks
|
||||||
|
if (0.5001f < value && value < 0.75f) {
|
||||||
|
less |= true;
|
||||||
|
more |= true;
|
||||||
|
|
||||||
|
} else if (value <= 0.25f) {
|
||||||
|
less |= true;
|
||||||
|
} else if (value >= 0.75f) {
|
||||||
|
more |= true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_analog_x_axis(int player, bool &left, bool &right) {
|
||||||
|
if (player != 1 && player != 2) {
|
||||||
|
log_fatal("ddr", "invalid player number: {}, expected 1 or 2", player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &analog = get_analogs().at(player == 1 ? Analogs::P1_LEFT_RIGHT : Analogs::P2_LEFT_RIGHT);
|
||||||
|
get_analog_xy_axis(analog, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_analog_y_axis(int player, bool &up, bool &down) {
|
||||||
|
if (player != 1 && player != 2) {
|
||||||
|
log_fatal("ddr", "invalid player number: {}, expected 1 or 2", player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &analog = get_analogs().at(player == 1 ? Analogs::P1_UP_DOWN : Analogs::P2_UP_DOWN);
|
||||||
|
get_analog_xy_axis(analog, up, down);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,4 +23,7 @@ namespace games::ddr {
|
|||||||
private:
|
private:
|
||||||
void register_codecs();
|
void register_codecs();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void get_analog_x_axis(int player, bool &left, bool &right);
|
||||||
|
void get_analog_y_axis(int player, bool &up, bool &down);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,50 @@ std::vector<Button> &games::ddr::get_buttons() {
|
|||||||
return analogs;
|
return analogs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string games::ddr::get_buttons_help() {
|
||||||
|
// keep to max 100 characters wide
|
||||||
|
return
|
||||||
|
"For DDR pad arrows, double check that simultaneous Left+Right and Up+Down can be detected.\n"
|
||||||
|
"You should boot the game, enter test menu, and use Foot Panel Check.\n\n"
|
||||||
|
"When mapping arrows, try the following in order:\n\n"
|
||||||
|
" 1. If your controller uses face buttons (A/B/X/Y), bind them here.\n"
|
||||||
|
" 2. If your controller supports XInput, use that.\n"
|
||||||
|
" 3. If your controller uses analog axis for arrows, try Analogs tab.\n"
|
||||||
|
" 4. Otherwise, you will need to use remapping software or get a better adapter."
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Analog> &games::ddr::get_analogs() {
|
||||||
|
static std::vector<Analog> analogs;
|
||||||
|
|
||||||
|
if (analogs.empty()) {
|
||||||
|
analogs = GameAPI::Analogs::getAnalogs("Dance Dance Revolution");
|
||||||
|
|
||||||
|
using namespace GameAPI::Analogs;
|
||||||
|
|
||||||
|
GameAPI::Analogs::sortAnalogsWithType(&analogs, {
|
||||||
|
{ "P1 Left-Right (Axis Fix)", AnalogType::LinearCentered },
|
||||||
|
{ "P1 Up-Down (Axis Fix)", AnalogType::LinearCentered },
|
||||||
|
{ "P2 Left-Right (Axis Fix)", AnalogType::LinearCentered },
|
||||||
|
{ "P2 Up-Down (Axis Fix)", AnalogType::LinearCentered }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return analogs;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string games::ddr::get_analogs_help() {
|
||||||
|
// keep to max 100 characters wide
|
||||||
|
return
|
||||||
|
"Only use this if your DDR pad outputs analog axis for arrows.\n\n"
|
||||||
|
"If the pad uses face buttons (A/B/X/Y), use Buttons tab instead.\n\n"
|
||||||
|
"Spice will treat values <=25% as Left, >=75% as Right, ~=50% as neutral,\n"
|
||||||
|
"and value between 50% and 75% as both arrows.\n\n"
|
||||||
|
"This is the classic Stepmania \"Axis Fix\" which may or may not work with\n"
|
||||||
|
"your dance pad or your adapter."
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Light> &games::ddr::get_lights() {
|
std::vector<Light> &games::ddr::get_lights() {
|
||||||
static std::vector<Light> lights;
|
static std::vector<Light> lights;
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,16 @@ namespace games::ddr {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// all analogs in correct order
|
||||||
|
namespace Analogs {
|
||||||
|
enum {
|
||||||
|
P1_LEFT_RIGHT,
|
||||||
|
P1_UP_DOWN,
|
||||||
|
P2_LEFT_RIGHT,
|
||||||
|
P2_UP_DOWN
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// all lights in correct order
|
// all lights in correct order
|
||||||
namespace Lights {
|
namespace Lights {
|
||||||
enum {
|
enum {
|
||||||
@@ -177,5 +187,8 @@ namespace games::ddr {
|
|||||||
|
|
||||||
// getters
|
// getters
|
||||||
std::vector<Button> &get_buttons();
|
std::vector<Button> &get_buttons();
|
||||||
|
std::string get_buttons_help();
|
||||||
|
std::string get_analogs_help();
|
||||||
|
std::vector<Analog> &get_analogs();
|
||||||
std::vector<Light> &get_lights();
|
std::vector<Light> &get_lights();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "cfg/api.h"
|
#include "cfg/api.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
#include "../ddr.h"
|
#include "../ddr.h"
|
||||||
@@ -459,7 +460,8 @@ int games::ddr::DDRP3IOHandle::device_io(
|
|||||||
if (nOutBufferSize >= 4) {
|
if (nOutBufferSize >= 4) {
|
||||||
|
|
||||||
// cool down
|
// cool down
|
||||||
Sleep(1);
|
static thread_local timeutils::PreciseSleepTimer timer;
|
||||||
|
timer.sleep(1);
|
||||||
|
|
||||||
// get controls as single variable (4 bytes)
|
// get controls as single variable (4 bytes)
|
||||||
auto &controls = *(uint32_t*) lpOutBuffer;
|
auto &controls = *(uint32_t*) lpOutBuffer;
|
||||||
@@ -498,32 +500,80 @@ int games::ddr::DDRP3IOHandle::device_io(
|
|||||||
|
|
||||||
// shift table
|
// shift table
|
||||||
static size_t shift_table[] = {
|
static size_t shift_table[] = {
|
||||||
30, 28, 29, 8, 9, 10, 11, 12, 24, 25, 14, 15, 16, 17, 18, 19, 20, 26, 27, 22, 23
|
30, 28, 29, // service, test, coin
|
||||||
|
8, // p1 start
|
||||||
|
9, 10, 11, 12, // p1 panel
|
||||||
|
24, 25, 14, 15, // p1 menu
|
||||||
|
16, // p2 start
|
||||||
|
17, 18, 19, 20, // p2 panel
|
||||||
|
26, 27, 22, 23 // p2 menu
|
||||||
};
|
};
|
||||||
static size_t button_table[] = {
|
static size_t button_table[] = {
|
||||||
Buttons::SERVICE,
|
Buttons::SERVICE,
|
||||||
Buttons::TEST,
|
Buttons::TEST,
|
||||||
Buttons::COIN_MECH,
|
Buttons::COIN_MECH,
|
||||||
|
|
||||||
Buttons::P1_START,
|
Buttons::P1_START,
|
||||||
|
|
||||||
Buttons::P1_PANEL_UP,
|
Buttons::P1_PANEL_UP,
|
||||||
Buttons::P1_PANEL_DOWN,
|
Buttons::P1_PANEL_DOWN,
|
||||||
Buttons::P1_PANEL_LEFT,
|
Buttons::P1_PANEL_LEFT,
|
||||||
Buttons::P1_PANEL_RIGHT,
|
Buttons::P1_PANEL_RIGHT,
|
||||||
|
|
||||||
Buttons::P1_MENU_UP,
|
Buttons::P1_MENU_UP,
|
||||||
Buttons::P1_MENU_DOWN,
|
Buttons::P1_MENU_DOWN,
|
||||||
Buttons::P1_MENU_LEFT,
|
Buttons::P1_MENU_LEFT,
|
||||||
Buttons::P1_MENU_RIGHT,
|
Buttons::P1_MENU_RIGHT,
|
||||||
|
|
||||||
Buttons::P2_START,
|
Buttons::P2_START,
|
||||||
|
|
||||||
Buttons::P2_PANEL_UP,
|
Buttons::P2_PANEL_UP,
|
||||||
Buttons::P2_PANEL_DOWN,
|
Buttons::P2_PANEL_DOWN,
|
||||||
Buttons::P2_PANEL_LEFT,
|
Buttons::P2_PANEL_LEFT,
|
||||||
Buttons::P2_PANEL_RIGHT,
|
Buttons::P2_PANEL_RIGHT,
|
||||||
|
|
||||||
Buttons::P2_MENU_UP,
|
Buttons::P2_MENU_UP,
|
||||||
Buttons::P2_MENU_DOWN,
|
Buttons::P2_MENU_DOWN,
|
||||||
Buttons::P2_MENU_LEFT,
|
Buttons::P2_MENU_LEFT,
|
||||||
Buttons::P2_MENU_RIGHT,
|
Buttons::P2_MENU_RIGHT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// get analogs
|
||||||
|
struct {
|
||||||
|
bool up;
|
||||||
|
bool down;
|
||||||
|
bool left;
|
||||||
|
bool right;
|
||||||
|
} analog_values[2] = {};
|
||||||
|
games::ddr::get_analog_x_axis(1, analog_values[0].left, analog_values[0].right);
|
||||||
|
games::ddr::get_analog_y_axis(1, analog_values[0].up, analog_values[0].down);
|
||||||
|
games::ddr::get_analog_x_axis(2, analog_values[1].left, analog_values[1].right);
|
||||||
|
games::ddr::get_analog_y_axis(2, analog_values[1].up, analog_values[1].down);
|
||||||
|
if (analog_values[0].up) {
|
||||||
|
controls |= 1 << 9;
|
||||||
|
}
|
||||||
|
if (analog_values[0].down) {
|
||||||
|
controls |= 1 << 10;
|
||||||
|
}
|
||||||
|
if (analog_values[0].left) {
|
||||||
|
controls |= 1 << 11;
|
||||||
|
}
|
||||||
|
if (analog_values[0].right) {
|
||||||
|
controls |= 1 << 12;
|
||||||
|
}
|
||||||
|
if (analog_values[1].up) {
|
||||||
|
controls |= 1 << 17;
|
||||||
|
}
|
||||||
|
if (analog_values[1].down) {
|
||||||
|
controls |= 1 << 18;
|
||||||
|
}
|
||||||
|
if (analog_values[1].left) {
|
||||||
|
controls |= 1 << 19;
|
||||||
|
}
|
||||||
|
if (analog_values[1].right) {
|
||||||
|
controls |= 1 << 20;
|
||||||
|
}
|
||||||
|
|
||||||
// update states
|
// update states
|
||||||
auto &buttons = get_buttons();
|
auto &buttons = get_buttons();
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
#include "../io.h"
|
#include "../io.h"
|
||||||
@@ -255,7 +256,8 @@ namespace games::ddr {
|
|||||||
case P4IO_IOCTL_GET_INPUTS: {
|
case P4IO_IOCTL_GET_INPUTS: {
|
||||||
|
|
||||||
// Prevents this function from being called at its normal 2000-2500 kHz cadence and overloading the CPU, instead reduces it to 250 Hz
|
// Prevents this function from being called at its normal 2000-2500 kHz cadence and overloading the CPU, instead reduces it to 250 Hz
|
||||||
Sleep(4);
|
static thread_local timeutils::PreciseSleepTimer timer;
|
||||||
|
timer.sleep(4);
|
||||||
|
|
||||||
memset(lpOutBuffer, 0, 16);
|
memset(lpOutBuffer, 0, 16);
|
||||||
auto controls = (uint32_t*) lpOutBuffer;
|
auto controls = (uint32_t*) lpOutBuffer;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "games/game.h"
|
#include "games/game.h"
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "util/memutils.h"
|
#include "util/memutils.h"
|
||||||
#include "rgb_cam.h"
|
#include "rgb_cam.h"
|
||||||
|
|
||||||
@@ -221,10 +222,11 @@ namespace games::drs {
|
|||||||
void start_touch() {
|
void start_touch() {
|
||||||
std::thread t([] {
|
std::thread t([] {
|
||||||
log_info("drs", "starting touch input thread");
|
log_info("drs", "starting touch input thread");
|
||||||
|
timeutils::PreciseSleepTimer timer;
|
||||||
|
|
||||||
// main loop
|
// main loop
|
||||||
while (TRUE) {
|
while (TRUE) {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
timer.sleep(1);
|
||||||
|
|
||||||
TOUCH_EVENTS.clear();
|
TOUCH_EVENTS.clear();
|
||||||
touch_get_events(TOUCH_EVENTS);
|
touch_get_events(TOUCH_EVENTS);
|
||||||
|
|||||||
@@ -35,13 +35,14 @@ std::vector<Analog> &games::ftt::get_analogs() {
|
|||||||
if (analogs.empty()) {
|
if (analogs.empty()) {
|
||||||
analogs = GameAPI::Analogs::getAnalogs("FutureTomTom");
|
analogs = GameAPI::Analogs::getAnalogs("FutureTomTom");
|
||||||
|
|
||||||
GameAPI::Analogs::sortAnalogs(
|
using GameAPI::Analogs::AnalogType;
|
||||||
&analogs,
|
|
||||||
"Pad 1",
|
GameAPI::Analogs::sortAnalogsWithType(&analogs, {
|
||||||
"Pad 2",
|
{ "Pad 1", AnalogType::LinearPositive },
|
||||||
"Pad 3",
|
{ "Pad 2", AnalogType::LinearPositive },
|
||||||
"Pad 4"
|
{ "Pad 3", AnalogType::LinearPositive },
|
||||||
);
|
{ "Pad 4", AnalogType::LinearPositive }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return analogs;
|
return analogs;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -8,11 +8,14 @@
|
|||||||
#include "misc/wintouchemu.h"
|
#include "misc/wintouchemu.h"
|
||||||
#include "overlay/overlay.h"
|
#include "overlay/overlay.h"
|
||||||
#include "util/cpuutils.h"
|
#include "util/cpuutils.h"
|
||||||
|
#include "util/deferlog.h"
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.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/utils.h"
|
||||||
#include "hooks/setupapihook.h"
|
#include "hooks/setupapihook.h"
|
||||||
|
|
||||||
namespace games::gitadora {
|
namespace games::gitadora {
|
||||||
@@ -20,7 +23,6 @@ namespace games::gitadora {
|
|||||||
// settings
|
// settings
|
||||||
bool TWOCHANNEL = false;
|
bool TWOCHANNEL = false;
|
||||||
std::optional<unsigned int> CAB_TYPE = std::nullopt;
|
std::optional<unsigned int> CAB_TYPE = std::nullopt;
|
||||||
bool ARENA_SINGLE_WINDOW = false;
|
|
||||||
bool P1_LEFTY = false;
|
bool P1_LEFTY = false;
|
||||||
bool P2_LEFTY = false;
|
bool P2_LEFTY = false;
|
||||||
std::optional<std::string> SUBSCREEN_OVERLAY_SIZE;
|
std::optional<std::string> SUBSCREEN_OVERLAY_SIZE;
|
||||||
@@ -223,8 +225,7 @@ namespace games::gitadora {
|
|||||||
|
|
||||||
// arena model launches a tiny window yet backbuffer at 4k, resulting in unusable overlay
|
// arena model launches a tiny window yet backbuffer at 4k, resulting in unusable overlay
|
||||||
// force scaling to make things usable
|
// force scaling to make things usable
|
||||||
if (!overlay::UI_SCALE_PERCENT.has_value() && is_arena_model() &&
|
if (!overlay::UI_SCALE_PERCENT.has_value() && is_arena_model() && !cfg::CONFIGURATOR_STANDALONE) {
|
||||||
GRAPHICS_WINDOWED && !cfg::CONFIGURATOR_STANDALONE) {
|
|
||||||
overlay::UI_SCALE_PERCENT = 250;
|
overlay::UI_SCALE_PERCENT = 250;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,9 +238,292 @@ namespace games::gitadora {
|
|||||||
} else {
|
} else {
|
||||||
log_info("gitadora", "guitar pick SOCD algorithm: legacy");
|
log_info("gitadora", "guitar pick SOCD algorithm: legacy");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SPICE64 && !SPICE_XP
|
||||||
|
|
||||||
|
if (is_arena_model() && !GRAPHICS_WINDOWED && !GRAPHICS_FORCE_SINGLE_ADAPTER) {
|
||||||
|
const auto &monitors = sysutils::enumerate_monitors();
|
||||||
|
const size_t active_count = monitors.size();
|
||||||
|
log_info("gitadora", "arena model: detected {} active monitor(s)", active_count);
|
||||||
|
if (active_count < 4) {
|
||||||
|
log_info("gitadora", "arena model: enable single monitor mode due to insufficient monitors");
|
||||||
|
GRAPHICS_FORCE_SINGLE_ADAPTER = true;
|
||||||
|
GRAPHICS_PREVENT_SECONDARY_WINDOW = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SPICE64 && !SPICE_XP
|
||||||
|
|
||||||
|
static decltype(GetDisplayConfigBufferSizes) *GetDisplayConfigBufferSizes_orig = nullptr;
|
||||||
|
static decltype(QueryDisplayConfig) *QueryDisplayConfig_orig = nullptr;
|
||||||
|
static decltype(DisplayConfigGetDeviceInfo) *DisplayConfigGetDeviceInfo_orig = nullptr;
|
||||||
|
|
||||||
|
// cached primary real monitor: its path + source/target mode entries.
|
||||||
|
// modeInfoIdx values on the path are renumbered to 0 / 1 so the cache is self-contained.
|
||||||
|
static DISPLAYCONFIG_PATH_INFO real_primary_path = {};
|
||||||
|
static DISPLAYCONFIG_MODE_INFO real_primary_modes[2] = {}; // [0]=source, [1]=target
|
||||||
|
|
||||||
|
// fake monitors appended after the real ones. the game classifies monitors
|
||||||
|
// by outputTechnology + connectorInstance:
|
||||||
|
// HDMI -> main 4k monitor (real primary)
|
||||||
|
// DP connInstance 0 -> left
|
||||||
|
// DP connInstance 1 -> right
|
||||||
|
// DP connInstance 2 -> small (sub/touch)
|
||||||
|
// ids are negated on the fake monitor headers so they can be distinguished
|
||||||
|
// from real ones in DisplayConfigGetDeviceInfo.
|
||||||
|
struct FakeMonitor {
|
||||||
|
LONG id;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int offset_x;
|
||||||
|
int offset_y;
|
||||||
|
UINT32 connector_instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ORDERING MATTERS: the d3d9 wrapper (FAKE_SUBSCREEN_ADAPTER) hands out
|
||||||
|
// adapters as "\\.\DISPLAY_SPICE_FAKE_{N}" for N=1,2,3. The game maps each
|
||||||
|
// adapter to a swap chain role via its DisplayConfig connector instance,
|
||||||
|
// so the entries here must be listed in the same order the wrapper enumerates
|
||||||
|
// them: id=1 -> adapter 1 -> left, id=2 -> adapter 2 -> right, id=3 -> adapter 3 -> small.
|
||||||
|
static constexpr FakeMonitor FAKE_MONITORS[] = {
|
||||||
|
{ 1, 1080, 1920, -100000, -100000, 0 }, // left (DP connector instance 0)
|
||||||
|
{ 2, 1080, 1920, -200000, -200000, 1 }, // right (DP connector instance 1)
|
||||||
|
{ 3, 800, 1280, -300000, -300000, 2 }, // small (DP connector instance 2, touch)
|
||||||
|
};
|
||||||
|
static constexpr UINT32 FAKE_MONITOR_COUNT = static_cast<UINT32>(std::size(FAKE_MONITORS));
|
||||||
|
|
||||||
|
// call QueryDisplayConfig once, keep only the primary monitor's path and its
|
||||||
|
// two referenced modes (source + target). modeInfoIdx values are rewritten to
|
||||||
|
// 0 and 1 so the cache is self-consistent.
|
||||||
|
static void cache_primary_monitor_info() {
|
||||||
|
UINT32 path_count = 0;
|
||||||
|
UINT32 mode_count = 0;
|
||||||
|
if (GetDisplayConfigBufferSizes_orig(
|
||||||
|
QDC_ONLY_ACTIVE_PATHS, &path_count, &mode_count) != ERROR_SUCCESS) {
|
||||||
|
log_fatal("gitadora", "cache_primary_monitor_info: GetDisplayConfigBufferSizes failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<DISPLAYCONFIG_PATH_INFO> all_paths(path_count);
|
||||||
|
std::vector<DISPLAYCONFIG_MODE_INFO> all_modes(mode_count);
|
||||||
|
if (QueryDisplayConfig_orig(
|
||||||
|
QDC_ONLY_ACTIVE_PATHS,
|
||||||
|
&path_count, all_paths.data(),
|
||||||
|
&mode_count, all_modes.data(),
|
||||||
|
nullptr) != ERROR_SUCCESS) {
|
||||||
|
log_fatal("gitadora", "cache_primary_monitor_info: QueryDisplayConfig failed");
|
||||||
|
}
|
||||||
|
all_paths.resize(path_count);
|
||||||
|
all_modes.resize(mode_count);
|
||||||
|
|
||||||
|
// pick the primary monitor: source mode at (0, 0)
|
||||||
|
auto primary = std::find_if(all_paths.begin(), all_paths.end(),
|
||||||
|
[&](const auto &p) {
|
||||||
|
const auto idx = p.sourceInfo.modeInfoIdx;
|
||||||
|
return idx < all_modes.size() &&
|
||||||
|
all_modes[idx].infoType == DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE &&
|
||||||
|
all_modes[idx].sourceMode.position.x == 0 &&
|
||||||
|
all_modes[idx].sourceMode.position.y == 0;
|
||||||
|
});
|
||||||
|
if (primary == all_paths.end()) {
|
||||||
|
log_fatal("gitadora", "cache_primary_monitor_info: no primary monitor found");
|
||||||
|
}
|
||||||
|
|
||||||
|
real_primary_modes[0] = all_modes[primary->sourceInfo.modeInfoIdx];
|
||||||
|
real_primary_modes[1] = all_modes[primary->targetInfo.modeInfoIdx];
|
||||||
|
real_primary_path = *primary;
|
||||||
|
real_primary_path.sourceInfo.modeInfoIdx = 0;
|
||||||
|
real_primary_path.targetInfo.modeInfoIdx = 1;
|
||||||
|
|
||||||
|
log_info("gitadora", "cache_primary_monitor_info: cached primary monitor");
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
LONG
|
||||||
|
WINAPI
|
||||||
|
GetDisplayConfigBufferSizes_hook(
|
||||||
|
UINT32 Flags,
|
||||||
|
UINT32 *pNumPathArrayElements,
|
||||||
|
UINT32 *pNumModeInfoArrayElements)
|
||||||
|
{
|
||||||
|
// populate cached primary real monitor on the first call
|
||||||
|
static std::once_flag populate_once;
|
||||||
|
std::call_once(populate_once, cache_primary_monitor_info);
|
||||||
|
|
||||||
|
// always report exactly 1 real + N fake monitors
|
||||||
|
*pNumPathArrayElements = 1 + FAKE_MONITOR_COUNT;
|
||||||
|
*pNumModeInfoArrayElements = 2 + FAKE_MONITOR_COUNT * 2;
|
||||||
|
|
||||||
|
log_info(
|
||||||
|
"gitadora",
|
||||||
|
"GetDisplayConfigBufferSizes: 1 real path + {} fake monitor(s)",
|
||||||
|
FAKE_MONITOR_COUNT);
|
||||||
|
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write fake monitor i into the caller's path/mode arrays. layout (single-monitor
|
||||||
|
// assumption): index 0 in both arrays holds the cached primary real monitor, so
|
||||||
|
// fake i occupies path slot (1 + i) and mode slots (2 + i*2) / (2 + i*2 + 1).
|
||||||
|
static void insert_fake_monitor(
|
||||||
|
DISPLAYCONFIG_PATH_INFO *paths,
|
||||||
|
DISPLAYCONFIG_MODE_INFO *modes,
|
||||||
|
UINT32 i)
|
||||||
|
{
|
||||||
|
const FakeMonitor &m = FAKE_MONITORS[i];
|
||||||
|
const UINT32 src_idx = 2 + i * 2;
|
||||||
|
const UINT32 tgt_idx = src_idx + 1;
|
||||||
|
const LUID adapter_id { .LowPart = static_cast<DWORD>(-m.id), .HighPart = -m.id };
|
||||||
|
const UINT32 uid = static_cast<UINT32>(-m.id);
|
||||||
|
|
||||||
|
paths[1 + i] = {
|
||||||
|
.sourceInfo = {
|
||||||
|
.adapterId = adapter_id,
|
||||||
|
.id = uid,
|
||||||
|
.modeInfoIdx = src_idx,
|
||||||
|
.statusFlags = DISPLAYCONFIG_SOURCE_IN_USE,
|
||||||
|
},
|
||||||
|
.targetInfo = {
|
||||||
|
.adapterId = adapter_id,
|
||||||
|
.id = uid,
|
||||||
|
.modeInfoIdx = tgt_idx,
|
||||||
|
.outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL,
|
||||||
|
.rotation = DISPLAYCONFIG_ROTATION_IDENTITY,
|
||||||
|
.scaling = DISPLAYCONFIG_SCALING_IDENTITY,
|
||||||
|
.refreshRate = { .Numerator = 60000, .Denominator = 1000 },
|
||||||
|
.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE,
|
||||||
|
.targetAvailable = TRUE,
|
||||||
|
.statusFlags = DISPLAYCONFIG_TARGET_IN_USE,
|
||||||
|
},
|
||||||
|
.flags = DISPLAYCONFIG_PATH_ACTIVE,
|
||||||
|
};
|
||||||
|
|
||||||
|
modes[src_idx] = {
|
||||||
|
.infoType = DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE,
|
||||||
|
.id = uid,
|
||||||
|
.adapterId = adapter_id,
|
||||||
|
.sourceMode = {
|
||||||
|
.width = static_cast<UINT32>(m.width),
|
||||||
|
.height = static_cast<UINT32>(m.height),
|
||||||
|
.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP,
|
||||||
|
.position = { .x = m.offset_x, .y = m.offset_y },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
modes[tgt_idx] = {
|
||||||
|
.infoType = DISPLAYCONFIG_MODE_INFO_TYPE_TARGET,
|
||||||
|
.id = uid,
|
||||||
|
.adapterId = adapter_id,
|
||||||
|
.targetMode = {},
|
||||||
|
};
|
||||||
|
|
||||||
|
log_misc(
|
||||||
|
"gitadora",
|
||||||
|
"inserted fake monitor: id={}, width={}, height={}, offset_x={}, offset_y={}",
|
||||||
|
m.id, m.width, m.height, m.offset_x, m.offset_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
LONG
|
||||||
|
WINAPI
|
||||||
|
QueryDisplayConfig_hook(
|
||||||
|
UINT32 flags,
|
||||||
|
UINT32* numPathArrayElements,
|
||||||
|
DISPLAYCONFIG_PATH_INFO* pathArray,
|
||||||
|
UINT32* numModeInfoArrayElements,
|
||||||
|
DISPLAYCONFIG_MODE_INFO* modeInfoArray,
|
||||||
|
DISPLAYCONFIG_TOPOLOGY_ID* currentTopologyId)
|
||||||
|
{
|
||||||
|
// copy cached primary real monitor into caller buffers at index 0
|
||||||
|
pathArray[0] = real_primary_path;
|
||||||
|
modeInfoArray[0] = real_primary_modes[0];
|
||||||
|
modeInfoArray[1] = real_primary_modes[1];
|
||||||
|
*numPathArrayElements = 1 + FAKE_MONITOR_COUNT;
|
||||||
|
*numModeInfoArrayElements = 2 + FAKE_MONITOR_COUNT * 2;
|
||||||
|
if (currentTopologyId != nullptr) {
|
||||||
|
*currentTopologyId = DISPLAYCONFIG_TOPOLOGY_EXTEND;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_misc("gitadora", "QueryDisplayConfig returning fake monitor paths and modes");
|
||||||
|
|
||||||
|
// append fake monitors after the real one
|
||||||
|
for (UINT32 i = 0; i < FAKE_MONITOR_COUNT; i++) {
|
||||||
|
insert_fake_monitor(pathArray, modeInfoArray, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
LONG
|
||||||
|
WINAPI
|
||||||
|
DisplayConfigGetDeviceInfo_hook(DISPLAYCONFIG_DEVICE_INFO_HEADER* requestPacket)
|
||||||
|
{
|
||||||
|
if (requestPacket == nullptr) {
|
||||||
|
return DisplayConfigGetDeviceInfo_orig(requestPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle fake monitors (negative id) directly without calling orig
|
||||||
|
const auto id = static_cast<int>(requestPacket->id);
|
||||||
|
if (id < 0) {
|
||||||
|
if (requestPacket->type == DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME) {
|
||||||
|
const auto sourceName = reinterpret_cast<DISPLAYCONFIG_SOURCE_DEVICE_NAME*>(requestPacket);
|
||||||
|
// name must match WrappedIDirect3D9::GetAdapterIdentifier
|
||||||
|
const std::string adapter_name = fmt::format("\\\\.\\DISPLAY_SPICE_FAKE_{}", -id);
|
||||||
|
wcscpy(sourceName->viewGdiDeviceName, s2ws(adapter_name).c_str());
|
||||||
|
log_misc("gitadora",
|
||||||
|
"DisplayConfigGetDeviceInfo: fake source id={} name={}", id, adapter_name);
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
if (requestPacket->type == DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME) {
|
||||||
|
const auto targetName = reinterpret_cast<DISPLAYCONFIG_TARGET_DEVICE_NAME*>(requestPacket);
|
||||||
|
const LONG fake_id = -id;
|
||||||
|
UINT32 conn_inst = 0xff;
|
||||||
|
for (const auto& f : FAKE_MONITORS) {
|
||||||
|
if (f.id == fake_id) {
|
||||||
|
conn_inst = f.connector_instance;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetName->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL;
|
||||||
|
targetName->connectorInstance = conn_inst;
|
||||||
|
wcscpy(targetName->monitorFriendlyDeviceName, L"Spice Fake Monitor");
|
||||||
|
wcscpy(targetName->monitorDevicePath, L"\\\\?\\SpiceFakeMonitor");
|
||||||
|
log_misc("gitadora",
|
||||||
|
"DisplayConfigGetDeviceInfo: fake target id={} -> DP connInst {}",
|
||||||
|
id, targetName->connectorInstance);
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto ret = DisplayConfigGetDeviceInfo_orig(requestPacket);
|
||||||
|
if (ret != ERROR_SUCCESS ||
|
||||||
|
requestPacket->type != DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// override the cached primary real monitor target info to look like HDMI/0
|
||||||
|
const auto targetName = reinterpret_cast<DISPLAYCONFIG_TARGET_DEVICE_NAME*>(requestPacket);
|
||||||
|
const auto& target = real_primary_path.targetInfo;
|
||||||
|
if (target.id == targetName->header.id &&
|
||||||
|
target.adapterId.HighPart == targetName->header.adapterId.HighPart &&
|
||||||
|
target.adapterId.LowPart == targetName->header.adapterId.LowPart)
|
||||||
|
{
|
||||||
|
targetName->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI;
|
||||||
|
targetName->connectorInstance = 0;
|
||||||
|
log_info("gitadora",
|
||||||
|
"overriding primary monitor (id={}) to pretend to be HDMI",
|
||||||
|
targetName->header.id);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
void GitaDoraGame::attach() {
|
void GitaDoraGame::attach() {
|
||||||
Game::attach();
|
Game::attach();
|
||||||
|
|
||||||
@@ -297,37 +581,61 @@ namespace games::gitadora {
|
|||||||
// volume change prevention
|
// volume change prevention
|
||||||
hooks::audio::mme::init(avs::game::DLL_INSTANCE);
|
hooks::audio::mme::init(avs::game::DLL_INSTANCE);
|
||||||
|
|
||||||
// touch hook
|
// monitor/touch hooks (windowed or full screen)
|
||||||
if (ARENA_SINGLE_WINDOW) {
|
if (GRAPHICS_FORCE_SINGLE_ADAPTER || GRAPHICS_PREVENT_SECONDARY_WINDOW) {
|
||||||
|
// enable touch hook for subscreen overlay
|
||||||
wintouchemu::FORCE = true;
|
wintouchemu::FORCE = true;
|
||||||
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
|
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
|
||||||
wintouchemu::hook("GITADORA", avs::game::DLL_INSTANCE);
|
wintouchemu::hook("GITADORA", avs::game::DLL_INSTANCE);
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
HMODULE gdme_module = libutils::try_module("libgdme.dll");
|
#if !SPICE_XP
|
||||||
|
|
||||||
|
if (!GRAPHICS_WINDOWED) {
|
||||||
|
// monitor hook: always pretend to have 1 primary real monitor + 3 fake monitors
|
||||||
|
// (LEFT / RIGHT / SMALL) so the game accepts the arena cab topology
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// window patch
|
// window patch
|
||||||
if (GRAPHICS_WINDOWED && !replace_pattern(
|
if (!is_arena_model()) {
|
||||||
gdme_module,
|
HMODULE gdme_module = libutils::try_module("libgdme.dll");
|
||||||
"754185ED753D8B4118BF0000CB02",
|
if (GRAPHICS_WINDOWED && !replace_pattern(
|
||||||
"9090????9090??????????????12", 0, 0))
|
gdme_module,
|
||||||
{
|
"754185ED753D8B4118BF0000CB02",
|
||||||
log_warning("gitadora", "windowed mode failed");
|
"9090????9090??????????????12", 0, 0)) {
|
||||||
|
log_warning("gitadora", "windowed mode failed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HMODULE bmsd_engine_module = libutils::try_module("libbmsd-engine.dll");
|
|
||||||
HMODULE bmsd_module = libutils::try_module("libbmsd.dll");
|
|
||||||
|
|
||||||
// two channel mod
|
// two channel mod
|
||||||
if (TWOCHANNEL) {
|
if (TWOCHANNEL) {
|
||||||
bmsd2_boot_orig = detour::iat_try("bmsd2_boot", bmsd2_boot_hook, bmsd_module);
|
if (is_arena_model()) {
|
||||||
|
log_warning("gitadora", "two channel audio (-2ch) is not supported on Arena Model - use a patch instead");
|
||||||
|
deferredlogs::defer_error_messages({
|
||||||
|
"two channel audio (-2ch) is not supported on Arena Model - use a patch instead",
|
||||||
|
});
|
||||||
|
|
||||||
if (!(replace_pattern(bmsd_engine_module, "33000000488D", "03??????????", 0, 0) ||
|
} else {
|
||||||
replace_pattern(bmsd_engine_module, "330000000F10", "03??????????", 0, 0)))
|
HMODULE bmsd_engine_module = libutils::try_module("libbmsd-engine.dll");
|
||||||
{
|
HMODULE bmsd_module = libutils::try_module("libbmsd.dll");
|
||||||
log_warning("gitadora", "two channel mode failed");
|
|
||||||
|
bmsd2_boot_orig = detour::iat_try("bmsd2_boot", bmsd2_boot_hook, bmsd_module);
|
||||||
|
if (!(replace_pattern(bmsd_engine_module, "33000000488D", "03??????????", 0, 0) ||
|
||||||
|
replace_pattern(bmsd_engine_module, "330000000F10", "03??????????", 0, 0))) {
|
||||||
|
log_warning("gitadora", "two channel mode failed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ namespace games::gitadora {
|
|||||||
// settings
|
// settings
|
||||||
extern bool TWOCHANNEL;
|
extern bool TWOCHANNEL;
|
||||||
extern std::optional<unsigned int> CAB_TYPE;
|
extern std::optional<unsigned int> CAB_TYPE;
|
||||||
extern bool ARENA_SINGLE_WINDOW;
|
|
||||||
extern bool P1_LEFTY;
|
extern bool P1_LEFTY;
|
||||||
extern bool P2_LEFTY;
|
extern bool P2_LEFTY;
|
||||||
extern std::optional<std::string> SUBSCREEN_OVERLAY_SIZE;
|
extern std::optional<std::string> SUBSCREEN_OVERLAY_SIZE;
|
||||||
|
|||||||
@@ -86,19 +86,21 @@ std::vector<Button> &games::gitadora::get_buttons() {
|
|||||||
std::vector<Analog> &games::gitadora::get_analogs() {
|
std::vector<Analog> &games::gitadora::get_analogs() {
|
||||||
static std::vector<Analog> analogs;
|
static std::vector<Analog> analogs;
|
||||||
|
|
||||||
|
using namespace GameAPI::Analogs;
|
||||||
|
|
||||||
if (analogs.empty()) {
|
if (analogs.empty()) {
|
||||||
analogs = GameAPI::Analogs::getAnalogs("GitaDora");
|
analogs = GameAPI::Analogs::getAnalogs("GitaDora");
|
||||||
|
|
||||||
GameAPI::Analogs::sortAnalogs(&analogs,
|
GameAPI::Analogs::sortAnalogsWithType(&analogs, {
|
||||||
"Guitar P1 Wail X",
|
{"Guitar P1 Wail X", AnalogType::LinearCentered},
|
||||||
"Guitar P1 Wail Y",
|
{"Guitar P1 Wail Y", AnalogType::LinearCentered},
|
||||||
"Guitar P1 Wail Z",
|
{"Guitar P1 Wail Z", AnalogType::LinearCentered},
|
||||||
"Guitar P1 Knob",
|
{"Guitar P1 Knob", AnalogType::Circular},
|
||||||
"Guitar P2 Wail X",
|
{"Guitar P2 Wail X", AnalogType::LinearCentered},
|
||||||
"Guitar P2 Wail Y",
|
{"Guitar P2 Wail Y", AnalogType::LinearCentered},
|
||||||
"Guitar P2 Wail Z",
|
{"Guitar P2 Wail Z", AnalogType::LinearCentered},
|
||||||
"Guitar P2 Knob"
|
{"Guitar P2 Knob", AnalogType::Circular}
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return analogs;
|
return analogs;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
#include "iidx.h"
|
#include "iidx.h"
|
||||||
@@ -82,7 +83,8 @@ bool games::iidx::IIDXFMSerialHandle::FMSerialDevice::parse_msg(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sleep - otherwise the IO thread will go too hard on the CPU
|
// sleep - otherwise the IO thread will go too hard on the CPU
|
||||||
Sleep(1);
|
static thread_local timeutils::PreciseSleepTimer timer;
|
||||||
|
timer.sleep(1);
|
||||||
|
|
||||||
// generate message
|
// generate message
|
||||||
auto msg = this->create_msg(msg_in, 0x2E);
|
auto msg = this->create_msg(msg_in, 0x2E);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
|
||||||
#include "iidx.h"
|
#include "iidx.h"
|
||||||
@@ -82,7 +83,8 @@ bool games::iidx::BI2XSerialHandle::BI2XDevice::parse_msg(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sleep - otherwise the IO thread will go too hard on the CPU
|
// sleep - otherwise the IO thread will go too hard on the CPU
|
||||||
Sleep(1);
|
static thread_local timeutils::PreciseSleepTimer timer;
|
||||||
|
timer.sleep(1);
|
||||||
|
|
||||||
// generate message
|
// generate message
|
||||||
auto msg = this->create_msg(msg_in, 0x2E);
|
auto msg = this->create_msg(msg_in, 0x2E);
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -66,6 +67,8 @@ namespace games::iidx {
|
|||||||
std::optional<std::string> SOUND_OUTPUT_DEVICE = std::nullopt;
|
std::optional<std::string> SOUND_OUTPUT_DEVICE = std::nullopt;
|
||||||
std::optional<std::string> SOUND_OUTPUT_DEVICE_IN_EFFECT = std::nullopt;
|
std::optional<std::string> SOUND_OUTPUT_DEVICE_IN_EFFECT = std::nullopt;
|
||||||
std::optional<std::string> ASIO_DRIVER = std::nullopt;
|
std::optional<std::string> ASIO_DRIVER = std::nullopt;
|
||||||
|
uint32_t TT_DELAY_P1 = 0;
|
||||||
|
uint32_t TT_DELAY_P2 = 0;
|
||||||
uint8_t DIGITAL_TT_SENS = 4;
|
uint8_t DIGITAL_TT_SENS = 4;
|
||||||
std::optional<std::string> SUBSCREEN_OVERLAY_SIZE = std::nullopt;
|
std::optional<std::string> SUBSCREEN_OVERLAY_SIZE = std::nullopt;
|
||||||
std::optional<std::string> SCREEN_MODE = std::nullopt;
|
std::optional<std::string> SCREEN_MODE = std::nullopt;
|
||||||
@@ -423,6 +426,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);
|
||||||
}
|
}
|
||||||
@@ -772,7 +783,7 @@ namespace games::iidx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// return higher 8 bit
|
// return higher 8 bit
|
||||||
return (uint8_t) (ret_value >> 2);
|
return (uint8_t)(ret_value >> 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char get_slider(uint8_t slider) {
|
unsigned char get_slider(uint8_t slider) {
|
||||||
@@ -906,8 +917,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",
|
||||||
|
|||||||
@@ -63,16 +63,17 @@ std::vector<Analog> &games::iidx::get_analogs() {
|
|||||||
if (analogs.empty()) {
|
if (analogs.empty()) {
|
||||||
analogs = GameAPI::Analogs::getAnalogs("Beatmania IIDX");
|
analogs = GameAPI::Analogs::getAnalogs("Beatmania IIDX");
|
||||||
|
|
||||||
GameAPI::Analogs::sortAnalogs(
|
using namespace GameAPI::Analogs;
|
||||||
&analogs,
|
|
||||||
"Turntable P1",
|
GameAPI::Analogs::sortAnalogsWithType(&analogs, {
|
||||||
"Turntable P2",
|
{ "Turntable P1", AnalogType::Circular },
|
||||||
"VEFX",
|
{ "Turntable P2", AnalogType::Circular },
|
||||||
"Low-EQ",
|
{ "VEFX", AnalogType::LinearPositive },
|
||||||
"Hi-EQ",
|
{ "Low-EQ", AnalogType::LinearPositive },
|
||||||
"Filter",
|
{ "Hi-EQ", AnalogType::LinearPositive },
|
||||||
"Play Volume"
|
{ "Filter", AnalogType::LinearPositive },
|
||||||
);
|
{ "Play Volume", AnalogType::LinearPositive }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return analogs;
|
return analogs;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
#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/precise_timer.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include "mf_wrappers.h"
|
#include "mf_wrappers.h"
|
||||||
|
|
||||||
@@ -399,6 +400,7 @@ namespace games::iidx {
|
|||||||
void IIDXLocalCamera::CreateThread() {
|
void IIDXLocalCamera::CreateThread() {
|
||||||
// Create thread
|
// Create thread
|
||||||
m_drawThread = new std::thread([this]() {
|
m_drawThread = new std::thread([this]() {
|
||||||
|
timeutils::PreciseSleepTimer timer;
|
||||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
|
||||||
|
|
||||||
double accumulator = 0.0;
|
double accumulator = 0.0;
|
||||||
@@ -412,7 +414,7 @@ namespace games::iidx {
|
|||||||
accumulator -= 1.0;
|
accumulator -= 1.0;
|
||||||
floorFrameTimeMicroSec += 1;
|
floorFrameTimeMicroSec += 1;
|
||||||
}
|
}
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds(floorFrameTimeMicroSec));
|
timer.sleep(std::chrono::microseconds(floorFrameTimeMicroSec));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include "touch/touch.h"
|
#include "touch/touch.h"
|
||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
|
|
||||||
#define POKE_NATIVE_TOUCH 0
|
#define POKE_NATIVE_TOUCH 0
|
||||||
|
|
||||||
@@ -200,6 +201,7 @@ namespace games::iidx::poke {
|
|||||||
// create new thread
|
// create new thread
|
||||||
THREAD_RUNNING = true;
|
THREAD_RUNNING = true;
|
||||||
THREAD = new std::thread([] {
|
THREAD = new std::thread([] {
|
||||||
|
timeutils::PreciseSleepTimer timer;
|
||||||
|
|
||||||
// log
|
// log
|
||||||
log_info("poke", "enabled");
|
log_info("poke", "enabled");
|
||||||
@@ -319,7 +321,7 @@ namespace games::iidx::poke {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// slow down
|
// slow down
|
||||||
Sleep(50);
|
timer.sleep(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|||||||
+50
-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,35 @@ 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");
|
buttons_help.insert({ ddr, ddr::get_buttons_help() });
|
||||||
file_hints[ddr].emplace_back("mdxja_945.dll");
|
analogs_help.insert({ ddr, ddr::get_analogs_help() });
|
||||||
file_hints[ddr].emplace_back("arkmdxp3.dll");
|
analogs.insert({ ddr, ddr::get_analogs() });
|
||||||
|
file_hints[ddr].push_back({"ddr.dll"});
|
||||||
|
file_hints[ddr].push_back({"mdxja_945.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 +114,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 +123,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 +131,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 +139,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 +148,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 +157,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 +165,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 +208,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 +217,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 +232,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 +254,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 +269,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 +297,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 +306,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 +329,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 +563,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);
|
||||||
}
|
}
|
||||||
|
|||||||
+86
-13
@@ -19,7 +19,7 @@
|
|||||||
namespace games::jb {
|
namespace games::jb {
|
||||||
|
|
||||||
// touch stuff
|
// touch stuff
|
||||||
bool TOUCH_LEGACY_BOX = false;
|
JubeatTouchAlgorithm TOUCH_ALGORITHM = Improved;
|
||||||
static bool TOUCH_ENABLE = false;
|
static bool TOUCH_ENABLE = false;
|
||||||
static bool TOUCH_ATTACHED = false;
|
static bool TOUCH_ATTACHED = false;
|
||||||
static bool IS_PORTRAIT = true;
|
static bool IS_PORTRAIT = true;
|
||||||
@@ -78,7 +78,7 @@ namespace games::jb {
|
|||||||
// note that the IO code in device.cpp will correctly compensate for orientation, depending on the model.
|
// note that the IO code in device.cpp will correctly compensate for orientation, depending on the model.
|
||||||
TOUCH_POINTS.clear();
|
TOUCH_POINTS.clear();
|
||||||
touch_get_points(TOUCH_POINTS);
|
touch_get_points(TOUCH_POINTS);
|
||||||
if (TOUCH_LEGACY_BOX) {
|
if (TOUCH_ALGORITHM == Legacy) {
|
||||||
auto offset = IS_PORTRAIT ? 580 : 0;
|
auto offset = IS_PORTRAIT ? 580 : 0;
|
||||||
for (auto &tp : TOUCH_POINTS) {
|
for (auto &tp : TOUCH_POINTS) {
|
||||||
|
|
||||||
@@ -94,6 +94,17 @@ namespace games::jb {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (auto &tp : TOUCH_POINTS) {
|
for (auto &tp : TOUCH_POINTS) {
|
||||||
|
// check window out of bounds
|
||||||
|
if (IS_PORTRAIT) {
|
||||||
|
if (tp.x > 768 || tp.y > 1360) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (tp.x > 1360 || tp.y > 768) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int x_relative = tp.x;
|
int x_relative = tp.x;
|
||||||
int y_relative = tp.y;
|
int y_relative = tp.y;
|
||||||
|
|
||||||
@@ -110,19 +121,66 @@ namespace games::jb {
|
|||||||
y_relative -= 8;
|
y_relative -= 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x_relative < 0 || y_relative < 0) {
|
int x_index = -1;
|
||||||
|
int x_hitbox = 0;
|
||||||
|
int y_index = -1;
|
||||||
|
int y_hitbox = 0;
|
||||||
|
|
||||||
|
// x_hitbox and y_hitbox is relative to top-left pixel of each button
|
||||||
|
if (x_relative >= 0) {
|
||||||
|
x_index = x_relative / JB_BUTTON_HITBOX;
|
||||||
|
x_hitbox = x_relative % JB_BUTTON_HITBOX;
|
||||||
|
}
|
||||||
|
if (y_relative >= 0) {
|
||||||
|
y_index = y_relative / JB_BUTTON_HITBOX;
|
||||||
|
y_hitbox = y_relative % JB_BUTTON_HITBOX;
|
||||||
|
}
|
||||||
|
|
||||||
|
// log_info("jb", "raw={}, idx={}, hitbox={}", tp.x, x_index, x_hitbox);
|
||||||
|
|
||||||
|
if (TOUCH_ALGORITHM == Improved) {
|
||||||
|
if (IS_PORTRAIT) {
|
||||||
|
// extend to left border
|
||||||
|
if (x_relative < 0) {
|
||||||
|
x_index = 0;
|
||||||
|
}
|
||||||
|
// right and bottom borders are covered by the hit box
|
||||||
|
} else {
|
||||||
|
// extend to top border
|
||||||
|
if (y_relative < 0) {
|
||||||
|
y_index = 0;
|
||||||
|
}
|
||||||
|
// extend to left border
|
||||||
|
if (x_relative < 0) {
|
||||||
|
x_index = 0;
|
||||||
|
}
|
||||||
|
// bottom border is covered by the hit box
|
||||||
|
// rightmost edge - ignore them entirely
|
||||||
|
if (x_index == 3 && JB_BUTTON_SIZE < x_hitbox) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x_index < 0 || y_index < 0 || x_index > 3 || y_index > 3) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// x_hitbox and y_hitbox is relative to top-left pixel of each button
|
|
||||||
int x_index = x_relative / JB_BUTTON_HITBOX;
|
|
||||||
int x_hitbox = x_relative % JB_BUTTON_HITBOX;
|
|
||||||
int y_index = y_relative / JB_BUTTON_HITBOX;
|
|
||||||
int y_hitbox = y_relative % JB_BUTTON_HITBOX;
|
|
||||||
|
|
||||||
// check if the gap was touched
|
// check if the gap was touched
|
||||||
if (x_hitbox > JB_BUTTON_SIZE || y_hitbox > JB_BUTTON_SIZE) {
|
if (TOUCH_ALGORITHM == AcAccurate) {
|
||||||
continue;
|
// in ac-accurate mode, touching the gap is ignored
|
||||||
|
if (x_hitbox > JB_BUTTON_SIZE || y_hitbox > JB_BUTTON_SIZE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (TOUCH_ALGORITHM == Improved) {
|
||||||
|
// in improved mode, touching the gap triggers the closest button
|
||||||
|
if (x_index <= 2 && (JB_BUTTON_SIZE + JB_BUTTON_GAP / 2) < x_hitbox) {
|
||||||
|
x_index++;
|
||||||
|
}
|
||||||
|
if (y_index <= 2 && (JB_BUTTON_SIZE + JB_BUTTON_GAP / 2) < y_hitbox) {
|
||||||
|
y_index++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the corresponding state
|
// set the corresponding state
|
||||||
@@ -167,7 +225,7 @@ namespace games::jb {
|
|||||||
void JBGame::pre_attach() {
|
void JBGame::pre_attach() {
|
||||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||||
const auto current_path = std::filesystem::current_path();
|
const auto current_path = std::filesystem::current_path();
|
||||||
log_misc("jubeat", "current working directory: {}", current_path.string());
|
log_misc("jubeat", "current working directory: {}", current_path);
|
||||||
if (current_path.parent_path() == current_path.root_path()) {
|
if (current_path.parent_path() == current_path.root_path()) {
|
||||||
log_warning(
|
log_warning(
|
||||||
"jubeat",
|
"jubeat",
|
||||||
@@ -178,7 +236,7 @@ namespace games::jb {
|
|||||||
" c:\\jubeat\\contents\\spice.exe <- OK\n\n"
|
" c:\\jubeat\\contents\\spice.exe <- OK\n\n"
|
||||||
"To fix this, create a new directory and move ALL game files there.\n\n"
|
"To fix this, create a new directory and move ALL game files there.\n\n"
|
||||||
"Your current working directory: {}\n",
|
"Your current working directory: {}\n",
|
||||||
current_path.string());
|
current_path);
|
||||||
|
|
||||||
log_fatal(
|
log_fatal(
|
||||||
"jubeat",
|
"jubeat",
|
||||||
@@ -193,6 +251,21 @@ namespace games::jb {
|
|||||||
// enable touch
|
// enable touch
|
||||||
TOUCH_ENABLE = true;
|
TOUCH_ENABLE = true;
|
||||||
|
|
||||||
|
switch (TOUCH_ALGORITHM) {
|
||||||
|
case Legacy:
|
||||||
|
log_info("jubeat", "using 'legacy' touch targets");
|
||||||
|
break;
|
||||||
|
case Improved:
|
||||||
|
log_info("jubeat", "using 'improved' touch targets");
|
||||||
|
break;
|
||||||
|
case AcAccurate:
|
||||||
|
log_info("jubeat", "using 'ac accurate' touch targets");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log_fatal("jubeat", "unknown touch algo detected in touch_update, this is a bug");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// enable debug logging of gftools
|
// enable debug logging of gftools
|
||||||
HMODULE gftools = libutils::try_module("gftools.dll");
|
HMODULE gftools = libutils::try_module("gftools.dll");
|
||||||
detour::inline_hook((void *) GFDbgSetReportFunc, libutils::try_proc(
|
detour::inline_hook((void *) GFDbgSetReportFunc, libutils::try_proc(
|
||||||
|
|||||||
@@ -4,8 +4,14 @@
|
|||||||
|
|
||||||
namespace games::jb {
|
namespace games::jb {
|
||||||
|
|
||||||
|
enum JubeatTouchAlgorithm {
|
||||||
|
Legacy,
|
||||||
|
Improved,
|
||||||
|
AcAccurate
|
||||||
|
};
|
||||||
|
|
||||||
// touch stuff
|
// touch stuff
|
||||||
extern bool TOUCH_LEGACY_BOX;
|
extern JubeatTouchAlgorithm TOUCH_ALGORITHM;
|
||||||
extern bool TOUCH_STATE[16];
|
extern bool TOUCH_STATE[16];
|
||||||
void touch_update();
|
void touch_update();
|
||||||
|
|
||||||
|
|||||||
@@ -36,11 +36,11 @@ std::vector<Analog> &games::mga::get_analogs() {
|
|||||||
if (analogs.empty()) {
|
if (analogs.empty()) {
|
||||||
analogs = GameAPI::Analogs::getAnalogs("Metal Gear");
|
analogs = GameAPI::Analogs::getAnalogs("Metal Gear");
|
||||||
|
|
||||||
GameAPI::Analogs::sortAnalogs(
|
using namespace GameAPI::Analogs;
|
||||||
&analogs,
|
GameAPI::Analogs::sortAnalogsWithType(&analogs, {
|
||||||
"Joy X",
|
{ "Joy X", AnalogType::LinearCentered },
|
||||||
"Joy Y"
|
{ "Joy Y", AnalogType::LinearCentered }
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return analogs;
|
return analogs;
|
||||||
|
|||||||
@@ -165,37 +165,38 @@ std::vector<Analog> &games::nost::get_analogs() {
|
|||||||
if (analogs.empty()) {
|
if (analogs.empty()) {
|
||||||
analogs = GameAPI::Analogs::getAnalogs("Nostalgia");
|
analogs = GameAPI::Analogs::getAnalogs("Nostalgia");
|
||||||
|
|
||||||
GameAPI::Analogs::sortAnalogs(
|
using namespace GameAPI::Analogs;
|
||||||
&analogs,
|
|
||||||
"Key 1",
|
GameAPI::Analogs::sortAnalogsWithType(&analogs, {
|
||||||
"Key 2",
|
{ "Key 1", AnalogType::LinearPositive },
|
||||||
"Key 3",
|
{ "Key 2", AnalogType::LinearPositive },
|
||||||
"Key 4",
|
{ "Key 3", AnalogType::LinearPositive },
|
||||||
"Key 5",
|
{ "Key 4", AnalogType::LinearPositive },
|
||||||
"Key 6",
|
{ "Key 5", AnalogType::LinearPositive },
|
||||||
"Key 7",
|
{ "Key 6", AnalogType::LinearPositive },
|
||||||
"Key 8",
|
{ "Key 7", AnalogType::LinearPositive },
|
||||||
"Key 9",
|
{ "Key 8", AnalogType::LinearPositive },
|
||||||
"Key 10",
|
{ "Key 9", AnalogType::LinearPositive },
|
||||||
"Key 11",
|
{ "Key 10", AnalogType::LinearPositive },
|
||||||
"Key 12",
|
{ "Key 11", AnalogType::LinearPositive },
|
||||||
"Key 13",
|
{ "Key 12", AnalogType::LinearPositive },
|
||||||
"Key 14",
|
{ "Key 13", AnalogType::LinearPositive },
|
||||||
"Key 15",
|
{ "Key 14", AnalogType::LinearPositive },
|
||||||
"Key 16",
|
{ "Key 15", AnalogType::LinearPositive },
|
||||||
"Key 17",
|
{ "Key 16", AnalogType::LinearPositive },
|
||||||
"Key 18",
|
{ "Key 17", AnalogType::LinearPositive },
|
||||||
"Key 19",
|
{ "Key 18", AnalogType::LinearPositive },
|
||||||
"Key 20",
|
{ "Key 19", AnalogType::LinearPositive },
|
||||||
"Key 21",
|
{ "Key 20", AnalogType::LinearPositive },
|
||||||
"Key 22",
|
{ "Key 21", AnalogType::LinearPositive },
|
||||||
"Key 23",
|
{ "Key 22", AnalogType::LinearPositive },
|
||||||
"Key 24",
|
{ "Key 23", AnalogType::LinearPositive },
|
||||||
"Key 25",
|
{ "Key 24", AnalogType::LinearPositive },
|
||||||
"Key 26",
|
{ "Key 25", AnalogType::LinearPositive },
|
||||||
"Key 27",
|
{ "Key 26", AnalogType::LinearPositive },
|
||||||
"Key 28"
|
{ "Key 27", AnalogType::LinearPositive },
|
||||||
);
|
{ "Key 28", AnalogType::LinearPositive }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return analogs;
|
return analogs;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "touch/touch.h"
|
#include "touch/touch.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
|
|
||||||
namespace games::nost::poke {
|
namespace games::nost::poke {
|
||||||
|
|
||||||
@@ -63,6 +64,7 @@ namespace games::nost::poke {
|
|||||||
// create new thread
|
// create new thread
|
||||||
THREAD_RUNNING = true;
|
THREAD_RUNNING = true;
|
||||||
THREAD = new std::thread([] {
|
THREAD = new std::thread([] {
|
||||||
|
timeutils::PreciseSleepTimer timer;
|
||||||
const DWORD touch_id = (DWORD)(0xFFFFFFFE);
|
const DWORD touch_id = (DWORD)(0xFFFFFFFE);
|
||||||
|
|
||||||
const int swipe_anim_total_frames = 6;
|
const int swipe_anim_total_frames = 6;
|
||||||
@@ -178,7 +180,7 @@ namespace games::nost::poke {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// slow down
|
// slow down
|
||||||
Sleep(30);
|
timer.sleep(30);
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "touchpanel.h"
|
#include "touchpanel.h"
|
||||||
|
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "touch/touch.h"
|
#include "touch/touch.h"
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
@@ -44,6 +45,9 @@ bool games::onpara::TouchPanelHandle::open(LPCWSTR lpFileName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int games::onpara::TouchPanelHandle::read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) {
|
int games::onpara::TouchPanelHandle::read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) {
|
||||||
|
|
||||||
|
static thread_local timeutils::PreciseSleepTimer timer;
|
||||||
|
|
||||||
DWORD i;
|
DWORD i;
|
||||||
auto buffer = reinterpret_cast<uint8_t *>(lpBuffer);
|
auto buffer = reinterpret_cast<uint8_t *>(lpBuffer);
|
||||||
|
|
||||||
@@ -67,7 +71,7 @@ int games::onpara::TouchPanelHandle::read(LPVOID lpBuffer, DWORD nNumberOfBytesT
|
|||||||
enqueue_packet(report);
|
enqueue_packet(report);
|
||||||
|
|
||||||
// prevent cpu bullying
|
// prevent cpu bullying
|
||||||
std::this_thread::sleep_for(1ms);
|
timer.sleep(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy from output queue
|
// copy from output queue
|
||||||
|
|||||||
@@ -51,11 +51,12 @@ std::vector<Analog> &games::pc::get_analogs() {
|
|||||||
if (analogs.empty()) {
|
if (analogs.empty()) {
|
||||||
analogs = GameAPI::Analogs::getAnalogs("Polaris Chord");
|
analogs = GameAPI::Analogs::getAnalogs("Polaris Chord");
|
||||||
|
|
||||||
GameAPI::Analogs::sortAnalogs(
|
using GameAPI::Analogs::AnalogType;
|
||||||
&analogs,
|
|
||||||
"Fader-L",
|
GameAPI::Analogs::sortAnalogsWithType(&analogs, {
|
||||||
"Fader-R"
|
{ "Fader-L", AnalogType::LinearCentered },
|
||||||
);
|
{ "Fader-R", AnalogType::LinearCentered }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return analogs;
|
return analogs;
|
||||||
|
|||||||
@@ -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_1");
|
||||||
|
} 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();
|
||||||
|
|||||||
+27
-13
@@ -4,29 +4,34 @@
|
|||||||
#include "hooks/devicehook.h"
|
#include "hooks/devicehook.h"
|
||||||
#include "hooks/audio/backends/dsound/dsound_backend.h"
|
#include "hooks/audio/backends/dsound/dsound_backend.h"
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
#include "cfg/configurator.h"
|
||||||
|
|
||||||
#include "touch.h"
|
#include "touch.h"
|
||||||
|
|
||||||
|
namespace games::rb {
|
||||||
|
uint16_t TOUCH_POLL_RATE = 0;
|
||||||
|
}
|
||||||
|
|
||||||
static decltype(SleepEx) *SleepEx_orig;
|
static decltype(SleepEx) *SleepEx_orig;
|
||||||
|
|
||||||
static DWORD WINAPI SleepEx_hook(DWORD dwMilliseconds, BOOL bAltertable) {
|
static DWORD WINAPI SleepEx_hook(DWORD dwMilliseconds, BOOL bAlertable) {
|
||||||
|
|
||||||
/*
|
// increase touch poll rate
|
||||||
* Increase touch poll from ~110 FPS to ~500 FPS
|
|
||||||
*/
|
|
||||||
if (dwMilliseconds == 8) {
|
if (dwMilliseconds == 8) {
|
||||||
static bool initialized = false;
|
dwMilliseconds = 1000 / games::rb::TOUCH_POLL_RATE;
|
||||||
if (!initialized) {
|
}
|
||||||
initialized = true;
|
|
||||||
|
|
||||||
// if we only sleep for 1ms we also don't need the high priority RB sets
|
// most calls from rb are actually non-alertable
|
||||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
|
if (!bAlertable) {
|
||||||
}
|
static thread_local timeutils::PreciseSleepTimer timer;
|
||||||
dwMilliseconds = 1;
|
timer.sleep(dwMilliseconds);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// call original
|
// call original
|
||||||
return SleepEx_orig(dwMilliseconds, bAltertable);
|
return SleepEx_orig(dwMilliseconds, bAlertable);
|
||||||
}
|
}
|
||||||
|
|
||||||
games::rb::RBGame::RBGame() : Game("Reflec Beat") {
|
games::rb::RBGame::RBGame() : Game("Reflec Beat") {
|
||||||
@@ -35,6 +40,10 @@ games::rb::RBGame::RBGame() : Game("Reflec Beat") {
|
|||||||
void games::rb::RBGame::attach() {
|
void games::rb::RBGame::attach() {
|
||||||
Game::attach();
|
Game::attach();
|
||||||
|
|
||||||
|
if (1000 < games::rb::TOUCH_POLL_RATE) {
|
||||||
|
log_fatal("rb", "-rbtouchsize is set too high; cannot exceed 1000");
|
||||||
|
}
|
||||||
|
|
||||||
// init stuff
|
// init stuff
|
||||||
devicehook_init();
|
devicehook_init();
|
||||||
|
|
||||||
@@ -47,7 +56,12 @@ void games::rb::RBGame::attach() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
SleepEx_orig = detour::iat_try("SleepEx", SleepEx_hook, avs::game::DLL_INSTANCE);
|
if (0 < TOUCH_POLL_RATE) {
|
||||||
|
log_info("rb", "force increasing touch poll rate by hooking SleepEx ({}Hz)", TOUCH_POLL_RATE);
|
||||||
|
SleepEx_orig = detour::iat_try("SleepEx", SleepEx_hook, avs::game::DLL_INSTANCE);
|
||||||
|
} else {
|
||||||
|
log_info("rb", "not changing touch poll rate (120Hz by default)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void games::rb::RBGame::detach() {
|
void games::rb::RBGame::detach() {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
namespace games::rb {
|
namespace games::rb {
|
||||||
|
|
||||||
extern uint16_t TOUCH_SCALING;
|
extern uint16_t TOUCH_SCALING;
|
||||||
|
extern uint8_t TOUCH_SIZE;
|
||||||
|
extern uint16_t TOUCH_POLL_RATE;
|
||||||
|
|
||||||
class RBGame : public games::Game {
|
class RBGame : public games::Game {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ static std::string WINDOW_TITLE = "REFLEC BEAT";
|
|||||||
|
|
||||||
namespace games::rb {
|
namespace games::rb {
|
||||||
uint16_t TOUCH_SCALING = 1000;
|
uint16_t TOUCH_SCALING = 1000;
|
||||||
|
uint8_t TOUCH_SIZE = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
games::rb::ReflecBeatTouchDeviceHandle::ReflecBeatTouchDeviceHandle(bool log_fps) {
|
games::rb::ReflecBeatTouchDeviceHandle::ReflecBeatTouchDeviceHandle(bool log_fps) {
|
||||||
@@ -160,16 +161,20 @@ int games::rb::ReflecBeatTouchDeviceHandle::read(LPVOID lpBuffer, DWORD nNumberO
|
|||||||
const auto point_x = (int) ((x - window_width / 2.0) * 48.0 / 54.0 + window_width / 2.0);
|
const auto point_x = (int) ((x - window_width / 2.0) * 48.0 / 54.0 + window_width / 2.0);
|
||||||
const auto point_y = (int) (y - window_height / 76);
|
const auto point_y = (int) (y - window_height / 76);
|
||||||
|
|
||||||
// insert 9 times with offset to double the precision
|
// center
|
||||||
grid_insert(data, point_x, point_y);
|
grid_insert(data, point_x, point_y);
|
||||||
grid_insert(data, point_x - offset_x, point_y);
|
|
||||||
grid_insert(data, point_x - offset_x, point_y - offset_y);
|
// surrounding points
|
||||||
grid_insert(data, point_x - offset_x, point_y + offset_y);
|
if (games::rb::TOUCH_SIZE == 3) {
|
||||||
grid_insert(data, point_x + offset_x, point_y);
|
grid_insert(data, point_x - offset_x, point_y); // west
|
||||||
grid_insert(data, point_x + offset_x, point_y + offset_y);
|
grid_insert(data, point_x - offset_x, point_y - offset_y); // northwest
|
||||||
grid_insert(data, point_x + offset_x, point_y - offset_y);
|
grid_insert(data, point_x - offset_x, point_y + offset_y); // southwest
|
||||||
grid_insert(data, point_x, point_y - offset_y);
|
grid_insert(data, point_x + offset_x, point_y); // east
|
||||||
grid_insert(data, point_x, point_y + offset_y);
|
grid_insert(data, point_x + offset_x, point_y + offset_y); // southeast
|
||||||
|
grid_insert(data, point_x + offset_x, point_y - offset_y); // northeast
|
||||||
|
grid_insert(data, point_x, point_y - offset_y); // north
|
||||||
|
grid_insert(data, point_x, point_y + offset_y); // south
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy data to buffer
|
// copy data to buffer
|
||||||
|
|||||||
@@ -35,12 +35,13 @@ std::vector<Analog> &games::rf3d::get_analogs() {
|
|||||||
if (analogs.empty()) {
|
if (analogs.empty()) {
|
||||||
analogs = GameAPI::Analogs::getAnalogs("Road Fighters 3D");
|
analogs = GameAPI::Analogs::getAnalogs("Road Fighters 3D");
|
||||||
|
|
||||||
GameAPI::Analogs::sortAnalogs(
|
using GameAPI::Analogs::AnalogType;
|
||||||
&analogs,
|
|
||||||
"Wheel",
|
GameAPI::Analogs::sortAnalogsWithType(&analogs, {
|
||||||
"Accelerate",
|
{ "Wheel", AnalogType::LinearCentered },
|
||||||
"Brake"
|
{ "Accelerate", AnalogType::LinearPositive },
|
||||||
);
|
{ "Brake", AnalogType::LinearPositive }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return analogs;
|
return analogs;
|
||||||
|
|||||||
@@ -31,13 +31,14 @@ std::vector<Analog> &games::sc::get_analogs() {
|
|||||||
if (analogs.empty()) {
|
if (analogs.empty()) {
|
||||||
analogs = GameAPI::Analogs::getAnalogs("Steel Chronicle");
|
analogs = GameAPI::Analogs::getAnalogs("Steel Chronicle");
|
||||||
|
|
||||||
GameAPI::Analogs::sortAnalogs(
|
using GameAPI::Analogs::AnalogType;
|
||||||
&analogs,
|
|
||||||
"Left Stick X",
|
GameAPI::Analogs::sortAnalogsWithType(&analogs, {
|
||||||
"Left Stick Y",
|
{ "Left Stick X", AnalogType::LinearCentered },
|
||||||
"Right Stick X",
|
{ "Left Stick Y", AnalogType::LinearCentered },
|
||||||
"Right Stick Y"
|
{ "Right Stick X", AnalogType::LinearCentered },
|
||||||
);
|
{ "Right Stick Y", AnalogType::LinearCentered }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return analogs;
|
return analogs;
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,11 +31,12 @@ std::vector<Analog> &games::silentscope::get_analogs() {
|
|||||||
if (analogs.empty()) {
|
if (analogs.empty()) {
|
||||||
analogs = GameAPI::Analogs::getAnalogs("Silent Scope: Bone Eater");
|
analogs = GameAPI::Analogs::getAnalogs("Silent Scope: Bone Eater");
|
||||||
|
|
||||||
GameAPI::Analogs::sortAnalogs(
|
using GameAPI::Analogs::AnalogType;
|
||||||
&analogs,
|
|
||||||
"Gun X",
|
GameAPI::Analogs::sortAnalogsWithType(&analogs, {
|
||||||
"Gun Y"
|
{ "Gun X", AnalogType::LinearCentered },
|
||||||
);
|
{ "Gun Y", AnalogType::LinearCentered }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return analogs;
|
return analogs;
|
||||||
|
|||||||
@@ -50,13 +50,14 @@ std::vector<Analog> &games::we::get_analogs() {
|
|||||||
if (analogs.empty()) {
|
if (analogs.empty()) {
|
||||||
analogs = GameAPI::Analogs::getAnalogs("Winning Eleven");
|
analogs = GameAPI::Analogs::getAnalogs("Winning Eleven");
|
||||||
|
|
||||||
GameAPI::Analogs::sortAnalogs(
|
using GameAPI::Analogs::AnalogType;
|
||||||
&analogs,
|
|
||||||
"Pad Stick Left X",
|
GameAPI::Analogs::sortAnalogsWithType(&analogs, {
|
||||||
"Pad Stick Left Y",
|
{ "Pad Stick Left X", AnalogType::LinearCentered },
|
||||||
"Pad Stick Right X",
|
{ "Pad Stick Left Y", AnalogType::LinearCentered },
|
||||||
"Pad Stick Right Y"
|
{ "Pad Stick Right X", AnalogType::LinearCentered },
|
||||||
);
|
{ "Pad Stick Right Y", AnalogType::LinearCentered }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return analogs;
|
return analogs;
|
||||||
|
|||||||
@@ -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;
|
||||||
log_warning(
|
std::call_once(printed, [ret]() {
|
||||||
"audio",
|
log_warning(
|
||||||
"CoCreateInstance for CLSID_DirectSoundI3DL2ReverbDMO failed with 0x80040154, swap with CLSID_DirectSoundWavesReverbDMO "
|
"audio",
|
||||||
"(workaround for Windows 11 KB5052093 issue); REVERB EX replaced with REVERB");
|
"CoCreateInstance for CLSID_DirectSoundI3DL2ReverbDMO failed with {}, swap with CLSID_DirectSoundWavesReverbDMO...",
|
||||||
|
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));
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "hooks/audio/audio.h"
|
#include "hooks/audio/audio.h"
|
||||||
#include "hooks/audio/backends/wasapi/audio_client.h"
|
#include "hooks/audio/backends/wasapi/audio_client.h"
|
||||||
#include "hooks/audio/backends/wasapi/defs.h"
|
#include "hooks/audio/backends/wasapi/defs.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
|
|
||||||
static REFERENCE_TIME WASAPI_TARGET_REFTIME = TARGET_REFTIME;
|
static REFERENCE_TIME WASAPI_TARGET_REFTIME = TARGET_REFTIME;
|
||||||
|
|
||||||
@@ -185,6 +186,7 @@ HRESULT WaveOutBackend::on_get_buffer(uint32_t num_frames_requested, BYTE **ppDa
|
|||||||
}
|
}
|
||||||
HRESULT WaveOutBackend::on_release_buffer(uint32_t num_frames_written, DWORD dwFlags) {
|
HRESULT WaveOutBackend::on_release_buffer(uint32_t num_frames_written, DWORD dwFlags) {
|
||||||
bool written = false;
|
bool written = false;
|
||||||
|
timeutils::PreciseSleepTimer timer;
|
||||||
|
|
||||||
// reset the dispatcher event
|
// reset the dispatcher event
|
||||||
ResetEvent(this->dispatcher_event);
|
ResetEvent(this->dispatcher_event);
|
||||||
@@ -209,7 +211,7 @@ HRESULT WaveOutBackend::on_release_buffer(uint32_t num_frames_written, DWORD dwF
|
|||||||
|
|
||||||
// avoid pegging the CPU
|
// avoid pegging the CPU
|
||||||
if (!written) {
|
if (!written) {
|
||||||
Sleep(1);
|
timer.sleep(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ static int avs_fs_mount(const char *mountpoint, const char *fsroot, const char *
|
|||||||
std::filesystem::create_directories(mapped_path, err);
|
std::filesystem::create_directories(mapped_path, err);
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
log_warning("hooks::avs", "failed to create '{}': {}", mapped_path.string(), err.message());
|
log_warning("hooks::avs", "failed to create '{}': {}", mapped_path, err.message());
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// if this is the `e:\`, then create the special directories
|
// if this is the `e:\`, then create the special directories
|
||||||
@@ -305,7 +305,7 @@ static int avs_fs_mount(const char *mountpoint, const char *fsroot, const char *
|
|||||||
|
|
||||||
log_misc("hooks::avs", "source directory '{}' remapped to '{}'",
|
log_misc("hooks::avs", "source directory '{}' remapped to '{}'",
|
||||||
fsroot,
|
fsroot,
|
||||||
mapped_path.string());
|
mapped_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_fs_root = mapped_path.string();
|
new_fs_root = mapped_path.string();
|
||||||
|
|||||||
@@ -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,26 @@ 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) {
|
||||||
|
if (games::popn::is_pikapika_model() && result == 1) {
|
||||||
|
FAKE_SUBSCREEN_ADAPTER = true;
|
||||||
|
log_info(
|
||||||
|
"graphics::d3d9",
|
||||||
|
"GetAdapterCount returned {}, popn needs 2 adapters - enabling fake submonitor mode", result);
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
} else if (games::gitadora::is_arena_model() && !GRAPHICS_WINDOWED && result < 4) {
|
||||||
|
FAKE_SUBSCREEN_ADAPTER = true;
|
||||||
|
log_info(
|
||||||
|
"graphics::d3d9",
|
||||||
|
"GetAdapterCount returned {}, gfdm needs 4 adapters - enabling fake submonitor mode", result);
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::GetAdapterIdentifier(
|
HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::GetAdapterIdentifier(
|
||||||
@@ -470,10 +490,28 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::GetAdapterIdentifier(
|
|||||||
DWORD Flags,
|
DWORD Flags,
|
||||||
D3DADAPTER_IDENTIFIER9 *pIdentifier)
|
D3DADAPTER_IDENTIFIER9 *pIdentifier)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (FAKE_SUBSCREEN_ADAPTER && Adapter > 0 && pIdentifier) {
|
||||||
|
*pIdentifier = {};
|
||||||
|
const std::string adapter_name = fmt::format("\\\\.\\DISPLAY_SPICE_FAKE_{}", Adapter);
|
||||||
|
strcpy(pIdentifier->DeviceName, adapter_name.c_str());
|
||||||
|
log_misc(
|
||||||
|
"graphics::d3d9",
|
||||||
|
"GetAdapterIdentifier called for fake subscreen adapter {}: {}",
|
||||||
|
Adapter,
|
||||||
|
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 > 0) {
|
||||||
|
log_misc("graphics::d3d9", "GetAdapterModeCount called for fake subscreen adapter {}", Adapter);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
return pReal->GetAdapterModeCount(Adapter, Format);
|
return pReal->GetAdapterModeCount(Adapter, Format);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -483,6 +521,19 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::EnumAdapterModes(
|
|||||||
UINT Mode,
|
UINT Mode,
|
||||||
D3DDISPLAYMODE *pMode)
|
D3DDISPLAYMODE *pMode)
|
||||||
{
|
{
|
||||||
|
if (FAKE_SUBSCREEN_ADAPTER && Adapter > 0) {
|
||||||
|
log_misc("graphics::d3d9", "EnumAdapterModes called for fake subscreen adapter {}", 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 +630,11 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CheckDeviceFormat(
|
|||||||
D3DRESOURCETYPE RType,
|
D3DRESOURCETYPE RType,
|
||||||
D3DFORMAT CheckFormat)
|
D3DFORMAT CheckFormat)
|
||||||
{
|
{
|
||||||
|
if (FAKE_SUBSCREEN_ADAPTER && Adapter > 0) {
|
||||||
|
log_misc("graphics::d3d9", "CheckDeviceFormat called for fake subscreen adapter {}", 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 +716,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 +731,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 +1079,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;
|
||||||
@@ -1092,9 +1151,15 @@ 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 the game 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", "M32"}) &&
|
||||||
graphics_d3d9_ldj_init_sub_screen(*ppReturnedDeviceInterface, &pPresentationParameters[1]);
|
(orig_behavior_flags & D3DCREATE_ADAPTERGROUP_DEVICE)) {
|
||||||
|
|
||||||
|
UINT i = 1;
|
||||||
|
if (games::gitadora::is_arena_model()) {
|
||||||
|
i = 2;
|
||||||
|
}
|
||||||
|
graphics_d3d9_ldj_init_sub_screen(*ppReturnedDeviceInterface, &pPresentationParameters[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1132,8 +1197,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 +1205,29 @@ 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);
|
|
||||||
|
int swapchain = 1;
|
||||||
|
if (games::gitadora::is_arena_model()) {
|
||||||
|
swapchain = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = device->GetSwapChain(swapchain, &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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1178,7 +1252,7 @@ static void graphics_d3d9_ldj_on_present(IDirect3DDevice9 *wrapped_device) {
|
|||||||
// iidx/sdvx
|
// iidx/sdvx
|
||||||
int swapchain = 1;
|
int swapchain = 1;
|
||||||
if (games::gitadora::is_arena_model()) {
|
if (games::gitadora::is_arena_model()) {
|
||||||
swapchain = 3;
|
swapchain = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ATTEMPTED_SUB_SWAP_CHAIN_ACQUIRE && SUB_SWAP_CHAIN == nullptr) {
|
if (!ATTEMPTED_SUB_SWAP_CHAIN_ACQUIRE && SUB_SWAP_CHAIN == nullptr) {
|
||||||
@@ -1196,7 +1270,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1394,9 +1471,11 @@ void graphics_d3d9_on_present(
|
|||||||
// for IIDX TDJ / SDVX UFC, handle subscreen
|
// for IIDX TDJ / SDVX UFC, handle subscreen
|
||||||
const bool is_vm = games::sdvx::is_valkyrie_model();
|
const bool is_vm = games::sdvx::is_valkyrie_model();
|
||||||
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::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW;
|
(GRAPHICS_FORCE_SINGLE_ADAPTER || GRAPHICS_PREVENT_SECONDARY_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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -129,13 +129,17 @@ ULONG STDMETHODCALLTYPE WrappedIDirect3DDevice9::Release() {
|
|||||||
this->main_swapchain->Release();
|
this->main_swapchain->Release();
|
||||||
this->main_swapchain = nullptr;
|
this->main_swapchain = nullptr;
|
||||||
}
|
}
|
||||||
if (this->sub_swapchain) {
|
for (auto &sc : this->sub_swapchain) {
|
||||||
this->sub_swapchain->Release();
|
if (sc) {
|
||||||
this->sub_swapchain = nullptr;
|
sc->Release();
|
||||||
|
sc = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (this->fake_sub_swapchain) {
|
for (auto &sc : this->fake_sub_swapchain) {
|
||||||
this->fake_sub_swapchain->Release();
|
if (sc) {
|
||||||
this->fake_sub_swapchain = nullptr;
|
sc->Release();
|
||||||
|
sc = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overlay::ENABLED) {
|
if (overlay::ENABLED) {
|
||||||
@@ -250,27 +254,47 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateAdditionalSwapChain(
|
|||||||
WRAP_VERBOSE;
|
WRAP_VERBOSE;
|
||||||
|
|
||||||
HRESULT hr = pReal->CreateAdditionalSwapChain(pPresentationParameters, ppSwapChain);
|
HRESULT hr = pReal->CreateAdditionalSwapChain(pPresentationParameters, ppSwapChain);
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
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 &&
|
(GRAPHICS_FORCE_SINGLE_ADAPTER || GRAPHICS_PREVENT_SECONDARY_WINDOW)) {
|
||||||
pPresentationParameters->BackBufferWidth == 800) {
|
if (pPresentationParameters->BackBufferWidth == 800) {
|
||||||
create_swap_chain = true;
|
// SMALL (subscreen)
|
||||||
|
create_swap_chain = true;
|
||||||
|
index = 0;
|
||||||
|
} else if (pPresentationParameters->BackBufferWidth == 1080) {
|
||||||
|
// LEFT/RIGHT
|
||||||
|
create_swap_chain = true;
|
||||||
|
index = 1;
|
||||||
|
if (sub_swapchain[index] || fake_sub_swapchain[index]) {
|
||||||
|
index = 2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log_warning("graphics::d3d9", "unknown swap chain detected in CreateAdditionalSwapChain");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (create_swap_chain) {
|
if (create_swap_chain) {
|
||||||
if (SUCCEEDED(hr) && !sub_swapchain) {
|
log_misc(
|
||||||
sub_swapchain = new WrappedIDirect3DSwapChain9(this, *ppSwapChain);
|
"graphics::d3d9",
|
||||||
sub_swapchain->should_run_hooks = false;
|
"CreateAdditionalSwapChain called for swap chain {}, creating swap chain",
|
||||||
} else if (FAILED(hr) && !fake_sub_swapchain) {
|
index);
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr) && !sub_swapchain[index]) {
|
||||||
|
sub_swapchain[index] = new WrappedIDirect3DSwapChain9(this, *ppSwapChain);
|
||||||
|
sub_swapchain[index]->should_run_hooks = false;
|
||||||
|
} else if (FAILED(hr) && !fake_sub_swapchain[index]) {
|
||||||
log_warning("graphics::d3d9",
|
log_warning("graphics::d3d9",
|
||||||
"failed to create sub swap chain, hr={}, using fake swap chain",
|
"failed to create sub swap chain, hr={}, using fake swap chain",
|
||||||
FMT_HRESULT(hr));
|
FMT_HRESULT(hr));
|
||||||
|
|
||||||
fake_sub_swapchain = new FakeIDirect3DSwapChain9(this, pPresentationParameters, false);
|
fake_sub_swapchain[index] = new FakeIDirect3DSwapChain9(this, pPresentationParameters, false);
|
||||||
fake_sub_swapchain->AddRef();
|
fake_sub_swapchain[index]->AddRef();
|
||||||
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(fake_sub_swapchain);
|
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(fake_sub_swapchain[index]);
|
||||||
|
|
||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
}
|
}
|
||||||
@@ -305,25 +329,23 @@ 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() && iSwapChain == 2) {
|
||||||
games::gitadora::ARENA_SINGLE_WINDOW &&
|
|
||||||
iSwapChain == 3) {
|
|
||||||
swap = true;
|
swap = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (swap) {
|
if (swap) {
|
||||||
if (sub_swapchain) {
|
if (sub_swapchain[0]) {
|
||||||
sub_swapchain->AddRef();
|
sub_swapchain[0]->AddRef();
|
||||||
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(sub_swapchain);
|
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(sub_swapchain[0]);
|
||||||
|
|
||||||
graphics_screens_register(iSwapChain);
|
graphics_screens_register(iSwapChain);
|
||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
} else if (fake_sub_swapchain) {
|
} else if (fake_sub_swapchain[0]) {
|
||||||
fake_sub_swapchain->AddRef();
|
fake_sub_swapchain[0]->AddRef();
|
||||||
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(fake_sub_swapchain);
|
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(fake_sub_swapchain[0]);
|
||||||
|
|
||||||
graphics_screens_register(iSwapChain);
|
graphics_screens_register(iSwapChain);
|
||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
@@ -344,7 +366,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[0] && avs::game::is_model({"LDJ", "KFC", "M39"})) {
|
||||||
n += 1;
|
n += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -204,7 +204,8 @@ struct WrappedIDirect3DDevice9 : IDirect3DDevice9Ex {
|
|||||||
std::atomic_ulong refs = 1;
|
std::atomic_ulong refs = 1;
|
||||||
|
|
||||||
WrappedIDirect3DSwapChain9 *main_swapchain = nullptr;
|
WrappedIDirect3DSwapChain9 *main_swapchain = nullptr;
|
||||||
WrappedIDirect3DSwapChain9 *sub_swapchain = nullptr;
|
WrappedIDirect3DSwapChain9 *sub_swapchain[3] = { nullptr, nullptr, nullptr };
|
||||||
FakeIDirect3DSwapChain9 *fake_sub_swapchain = nullptr;
|
FakeIDirect3DSwapChain9 *fake_sub_swapchain[3] = { nullptr, nullptr, nullptr };
|
||||||
|
|
||||||
IDirect3DVertexShader9 *vertex_shader = nullptr;
|
IDirect3DVertexShader9 *vertex_shader = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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));
|
||||||
@@ -53,6 +59,12 @@ static CaptureData GRAPHICS_CAPTURE_BUFFER[GRAPHICS_CAPTURE_SCREEN_NO] {};
|
|||||||
static std::mutex GRAPHICS_CAPTURE_BUFFER_M[GRAPHICS_CAPTURE_SCREEN_NO] {};
|
static std::mutex GRAPHICS_CAPTURE_BUFFER_M[GRAPHICS_CAPTURE_SCREEN_NO] {};
|
||||||
static std::condition_variable GRAPHICS_CAPTURE_CV[GRAPHICS_CAPTURE_SCREEN_NO] {};
|
static std::condition_variable GRAPHICS_CAPTURE_CV[GRAPHICS_CAPTURE_SCREEN_NO] {};
|
||||||
|
|
||||||
|
static std::optional<graphics_orientation> target_orientation_on_boot;
|
||||||
|
static UINT target_refresh_rate_on_boot = 0;
|
||||||
|
static std::optional<std::pair<uint32_t, uint32_t>> target_resolution_on_boot;
|
||||||
|
static bool monitor_settings_changed = false;
|
||||||
|
static bool monitor_layout_needs_reset = false;
|
||||||
|
|
||||||
// flag settings
|
// flag settings
|
||||||
bool GRAPHICS_CAPTURE_CURSOR = false;
|
bool GRAPHICS_CAPTURE_CURSOR = false;
|
||||||
bool GRAPHICS_LOG_HRESULT = false;
|
bool GRAPHICS_LOG_HRESULT = false;
|
||||||
@@ -98,6 +110,7 @@ static decltype(SetWindowLongA) *SetWindowLongA_orig = nullptr;
|
|||||||
static decltype(SetWindowLongW) *SetWindowLongW_orig = nullptr;
|
static decltype(SetWindowLongW) *SetWindowLongW_orig = nullptr;
|
||||||
static decltype(SetWindowPos) *SetWindowPos_orig = nullptr;
|
static decltype(SetWindowPos) *SetWindowPos_orig = nullptr;
|
||||||
static decltype(ShowWindow) *ShowWindow_orig = nullptr;
|
static decltype(ShowWindow) *ShowWindow_orig = nullptr;
|
||||||
|
static decltype(SetDisplayConfig) *SetDisplayConfig_addr = nullptr;
|
||||||
|
|
||||||
static void reset_window_hook(HWND hWnd) {
|
static void reset_window_hook(HWND hWnd) {
|
||||||
overlay::destroy(hWnd);
|
overlay::destroy(hWnd);
|
||||||
@@ -151,6 +164,7 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
|
|||||||
if (wintouchemu::INJECT_MOUSE_AS_WM_TOUCH) {
|
if (wintouchemu::INJECT_MOUSE_AS_WM_TOUCH) {
|
||||||
// drop mouse inputs since only wintouches should be used
|
// drop mouse inputs since only wintouches should be used
|
||||||
switch (uMsg) {
|
switch (uMsg) {
|
||||||
|
case WM_MOUSEMOVE:
|
||||||
case WM_LBUTTONDOWN:
|
case WM_LBUTTONDOWN:
|
||||||
case WM_LBUTTONUP:
|
case WM_LBUTTONUP:
|
||||||
case WM_MBUTTONDOWN:
|
case WM_MBUTTONDOWN:
|
||||||
@@ -226,7 +240,7 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
|
|||||||
|
|
||||||
switch (uMsg) {
|
switch (uMsg) {
|
||||||
case WM_MOVE:
|
case WM_MOVE:
|
||||||
case WM_SIZE: {
|
case WM_SIZE:
|
||||||
// Update SPICETOUCH space when the main window changes size or moves.
|
// Update SPICETOUCH space when the main window changes size or moves.
|
||||||
// The update happens regardless of whether the "fake" spicetouch window is present or not.
|
// The update happens regardless of whether the "fake" spicetouch window is present or not.
|
||||||
// This allows touches received on subscreen window to be translated correctly.
|
// This allows touches received on subscreen window to be translated correctly.
|
||||||
@@ -243,7 +257,16 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
|
|||||||
SPICETOUCH_TOUCH_WIDTH, SPICETOUCH_TOUCH_HEIGHT,
|
SPICETOUCH_TOUCH_WIDTH, SPICETOUCH_TOUCH_HEIGHT,
|
||||||
SWP_NOZORDER | SWP_NOREDRAW | SWP_NOREPOSITION | SWP_NOACTIVATE);
|
SWP_NOZORDER | SWP_NOREDRAW | SWP_NOREPOSITION | SWP_NOACTIVATE);
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
|
case WM_ACTIVATEAPP:
|
||||||
|
if (wParam) {
|
||||||
|
// regained focus
|
||||||
|
// this *can* get called twice in a row when restoring, but update_monitor_at_runtime
|
||||||
|
// is idempotent (checks current display settings to see if changes are needed),
|
||||||
|
// so it shouldn't cause any issues
|
||||||
|
update_monitor_at_runtime();
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -331,18 +354,27 @@ 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)
|
// update style / ex-style
|
||||||
if ((is_tdj_sub_window && GRAPHICS_IIDX_WSUB) || is_sdvx_sub_window || is_gfdm_sub_window) {
|
if (is_tdj_sub_window || is_sdvx_sub_window || is_gfdm_sub_window || is_popn_sub_window) {
|
||||||
|
// hide maximize button (prevent misaligned touches)
|
||||||
dwStyle &= ~(WS_MAXIMIZEBOX);
|
dwStyle &= ~(WS_MAXIMIZEBOX);
|
||||||
}
|
|
||||||
// mouse clicks become misaligned when resized
|
// mouse clicks become misaligned when resized
|
||||||
if (is_gfdm_sub_window) {
|
|
||||||
dwStyle &= ~(WS_SIZEBOX);
|
dwStyle &= ~(WS_SIZEBOX);
|
||||||
}
|
|
||||||
if ((is_tdj_sub_window || is_sdvx_sub_window) && GRAPHICS_WSUB_BORDERLESS) {
|
// borderless
|
||||||
dwStyle &= ~(WS_OVERLAPPEDWINDOW);
|
if (GRAPHICS_WINDOWED && GRAPHICS_WSUB_BORDERLESS) {
|
||||||
|
dwStyle &= ~(WS_OVERLAPPEDWINDOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't show the sub window on task bar / alt-tab targets
|
||||||
|
if (!GRAPHICS_WINDOWED) {
|
||||||
|
dwExStyle &= ~(WS_EX_APPWINDOW);
|
||||||
|
dwExStyle |= WS_EX_TOOLWINDOW;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_sdvx_sub_window) {
|
if (is_sdvx_sub_window) {
|
||||||
@@ -363,6 +395,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);
|
||||||
@@ -385,11 +421,18 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
|||||||
}
|
}
|
||||||
|
|
||||||
// only hook touch window if multiple windows are allowed
|
// only hook touch window if multiple windows are allowed
|
||||||
if (is_gfdm_sub_window && !games::gitadora::ARENA_SINGLE_WINDOW) {
|
if (is_gfdm_sub_window && GRAPHICS_WINDOWED && !GRAPHICS_PREVENT_SECONDARY_WINDOW) {
|
||||||
GFDM_SUBSCREEN_WINDOW = result;
|
GFDM_SUBSCREEN_WINDOW = result;
|
||||||
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 +501,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,
|
||||||
@@ -647,12 +702,19 @@ static BOOL WINAPI SetWindowPos_hook(HWND hWnd, HWND hWndInsertAfter,
|
|||||||
|
|
||||||
static BOOL WINAPI ShowWindow_hook(HWND hWnd, int nCmdShow) {
|
static BOOL WINAPI ShowWindow_hook(HWND hWnd, int nCmdShow) {
|
||||||
if (games::gitadora::is_arena_model() &&
|
if (games::gitadora::is_arena_model() &&
|
||||||
games::gitadora::ARENA_SINGLE_WINDOW &&
|
GRAPHICS_PREVENT_SECONDARY_WINDOW &&
|
||||||
hWnd != GRAPHICS_HOOKED_WINDOW) {
|
hWnd != GRAPHICS_HOOKED_WINDOW) {
|
||||||
log_info("graphics", "ShowWindow_hook - hiding sub window {}", fmt::ptr(hWnd));
|
log_info("graphics", "ShowWindow_hook - hiding sub window {}", fmt::ptr(hWnd));
|
||||||
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 +957,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) {
|
||||||
@@ -1102,7 +1167,7 @@ void change_primary_monitor(const std::string &monitor_name) {
|
|||||||
const auto DisplayConfigGetDeviceInfo_addr =
|
const auto DisplayConfigGetDeviceInfo_addr =
|
||||||
reinterpret_cast<decltype(DisplayConfigGetDeviceInfo) *>(
|
reinterpret_cast<decltype(DisplayConfigGetDeviceInfo) *>(
|
||||||
GetProcAddress(user32, "DisplayConfigGetDeviceInfo"));
|
GetProcAddress(user32, "DisplayConfigGetDeviceInfo"));
|
||||||
const auto SetDisplayConfig_addr =
|
SetDisplayConfig_addr =
|
||||||
reinterpret_cast<decltype(SetDisplayConfig) *>(
|
reinterpret_cast<decltype(SetDisplayConfig) *>(
|
||||||
GetProcAddress(user32, "SetDisplayConfig"));
|
GetProcAddress(user32, "SetDisplayConfig"));
|
||||||
if (GetDisplayConfigBufferSizes_addr == nullptr || QueryDisplayConfig_addr == nullptr ||
|
if (GetDisplayConfigBufferSizes_addr == nullptr || QueryDisplayConfig_addr == nullptr ||
|
||||||
@@ -1217,12 +1282,19 @@ void change_primary_monitor(const std::string &monitor_name) {
|
|||||||
log_fatal("graphics", "SetDisplayConfig failed, check -mainmonitor option: {}", status);
|
log_fatal("graphics", "SetDisplayConfig failed, check -mainmonitor option: {}", status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
monitor_layout_needs_reset = true;
|
||||||
|
|
||||||
// a little extra time for windows to settle and redraw things
|
// a little extra time for windows to settle and redraw things
|
||||||
Sleep(2000);
|
Sleep(2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_monitor_on_boot(std::optional<graphics_orientation> target_orientation, UINT target_refresh_rate) {
|
void update_monitor(
|
||||||
// note: all of this is only being done for the primary motnior
|
bool is_boot,
|
||||||
|
std::optional<graphics_orientation> target_orientation,
|
||||||
|
UINT target_refresh_rate,
|
||||||
|
std::optional<std::pair<uint32_t, uint32_t>> target_resolution) {
|
||||||
|
|
||||||
|
// note: all of this is only being done for the primary monitor
|
||||||
|
|
||||||
// get current settings
|
// get current settings
|
||||||
DEVMODEA dm = {};
|
DEVMODEA dm = {};
|
||||||
@@ -1290,7 +1362,7 @@ void update_monitor_on_boot(std::optional<graphics_orientation> target_orientati
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update refresh rate
|
// update refresh rate
|
||||||
if (target_refresh_rate > 0) {
|
if (target_refresh_rate > 0 && target_refresh_rate != dm.dmDisplayFrequency) {
|
||||||
log_misc("graphics",
|
log_misc("graphics",
|
||||||
"current refresh rate {} => desired refresh rate {}",
|
"current refresh rate {} => desired refresh rate {}",
|
||||||
dm.dmDisplayFrequency, target_refresh_rate);
|
dm.dmDisplayFrequency, target_refresh_rate);
|
||||||
@@ -1300,27 +1372,107 @@ void update_monitor_on_boot(std::optional<graphics_orientation> target_orientati
|
|||||||
needs_update = true;
|
needs_update = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target_resolution.has_value()) {
|
||||||
|
if (dm.dmPelsWidth != target_resolution.value().first ||
|
||||||
|
dm.dmPelsHeight != target_resolution.value().second) {
|
||||||
|
|
||||||
|
log_misc(
|
||||||
|
"graphics",
|
||||||
|
"current resolution {}, {} => desired resolution {}, {}",
|
||||||
|
dm.dmPelsWidth, dm.dmPelsHeight,
|
||||||
|
target_resolution.value().first, target_resolution.value().second);
|
||||||
|
|
||||||
|
dm.dmPelsWidth = target_resolution.value().first;
|
||||||
|
dm.dmPelsHeight = target_resolution.value().second;
|
||||||
|
needs_update = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!needs_update) {
|
if (!needs_update) {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
|
log_misc("graphics", "display settings are already up to date, no changes needed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto result = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
|
const auto result = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
|
||||||
if (result != DISP_CHANGE_SUCCESSFUL) {
|
if (result != DISP_CHANGE_SUCCESSFUL) {
|
||||||
log_fatal(
|
if (is_boot) {
|
||||||
"graphics",
|
log_fatal(
|
||||||
"failed to update display settings ({}px x {}px @ {}Hz): error {}, double check options",
|
"graphics",
|
||||||
dm.dmPelsWidth,
|
"failed to update display settings ({}px x {}px @ {}Hz): error {}, double check options",
|
||||||
dm.dmPelsHeight,
|
dm.dmPelsWidth,
|
||||||
dm.dmDisplayFrequency,
|
dm.dmPelsHeight,
|
||||||
result);
|
dm.dmDisplayFrequency,
|
||||||
|
result);
|
||||||
|
} else {
|
||||||
|
log_warning(
|
||||||
|
"graphics",
|
||||||
|
"failed to update display settings ({}px x {}px @ {}Hz): error {}, double check options",
|
||||||
|
dm.dmPelsWidth,
|
||||||
|
dm.dmPelsHeight,
|
||||||
|
dm.dmDisplayFrequency,
|
||||||
|
result);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
monitor_settings_changed = true;
|
||||||
|
// sleep for a little bit after changing monitor settings to delay game launch/resume
|
||||||
|
Sleep(1000);
|
||||||
log_info("graphics", "display settings updated successfully ({}px x {}px @ {}Hz)",
|
log_info("graphics", "display settings updated successfully ({}px x {}px @ {}Hz)",
|
||||||
dm.dmPelsWidth,
|
dm.dmPelsWidth,
|
||||||
dm.dmPelsHeight,
|
dm.dmPelsHeight,
|
||||||
dm.dmDisplayFrequency);
|
dm.dmDisplayFrequency);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// sleep for a little bit after changing monitor settings to delay game launch
|
|
||||||
Sleep(1000);
|
void update_monitor_on_boot(
|
||||||
|
std::optional<graphics_orientation> target_orientation,
|
||||||
|
UINT target_refresh_rate,
|
||||||
|
std::optional<std::pair<uint32_t, uint32_t>> target_resolution) {
|
||||||
|
|
||||||
|
target_orientation_on_boot = target_orientation;
|
||||||
|
target_refresh_rate_on_boot = target_refresh_rate;
|
||||||
|
target_resolution_on_boot = target_resolution;
|
||||||
|
log_misc("graphics", "applying monitor updates at boot...");
|
||||||
|
update_monitor(
|
||||||
|
true,
|
||||||
|
target_orientation,
|
||||||
|
target_refresh_rate,
|
||||||
|
target_resolution);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_monitor_at_runtime() {
|
||||||
|
if (!GRAPHICS_WINDOWED && monitor_settings_changed) {
|
||||||
|
log_misc("graphics", "applying monitor updates at runtime as window regained focus...");
|
||||||
|
update_monitor(
|
||||||
|
false,
|
||||||
|
target_orientation_on_boot,
|
||||||
|
target_refresh_rate_on_boot,
|
||||||
|
target_resolution_on_boot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_monitor_on_exit() {
|
||||||
|
|
||||||
|
// while CDS_FULLSCREEN is *supposed* to be temporary & the OS attempts to
|
||||||
|
// restore the original settings on exit, it can sometimes fail to do that;
|
||||||
|
// therefore, we try our best to clean things up on the way out
|
||||||
|
if (monitor_settings_changed) {
|
||||||
|
monitor_settings_changed = false;
|
||||||
|
log_misc("graphics", "resetting monitor settings on exit...");
|
||||||
|
ChangeDisplaySettingsW(nullptr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// same for this one.
|
||||||
|
if (monitor_layout_needs_reset) {
|
||||||
|
monitor_layout_needs_reset = false;
|
||||||
|
log_misc("graphics", "restoring primary monitor on exit...");
|
||||||
|
if (SetDisplayConfig_addr != nullptr) {
|
||||||
|
SetDisplayConfig_addr(
|
||||||
|
0,
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
nullptr,
|
||||||
|
SDC_APPLY | SDC_USE_DATABASE_CURRENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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,22 @@ 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,
|
||||||
|
std::optional<std::pair<uint32_t, uint32_t>> target_resolution);
|
||||||
|
|
||||||
|
void update_monitor_at_runtime();
|
||||||
|
void reset_monitor_on_exit();
|
||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
#include <winsock2.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <iptypes.h>
|
||||||
|
#include <iphlpapi.h>
|
||||||
|
#include <icmpapi.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "util/detour.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct IcmpHdr {
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t code;
|
||||||
|
uint16_t checksum_be;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
decltype(IcmpSendEcho) *IcmpSendEcho_orig = nullptr;
|
||||||
|
|
||||||
|
DWORD WINAPI IcmpSendEcho_hook(
|
||||||
|
HANDLE IcmpHandle,
|
||||||
|
IPAddr DestinationAddress,
|
||||||
|
LPVOID RequestData,
|
||||||
|
WORD RequestSize,
|
||||||
|
PIP_OPTION_INFORMATION RequestOptions,
|
||||||
|
LPVOID ReplyBuffer,
|
||||||
|
DWORD ReplySize,
|
||||||
|
DWORD Timeout) {
|
||||||
|
if (!IcmpSendEcho_orig) {
|
||||||
|
SetLastError(ERROR_INVALID_FUNCTION);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
DWORD n = IcmpSendEcho_orig(
|
||||||
|
IcmpHandle,
|
||||||
|
DestinationAddress,
|
||||||
|
RequestData,
|
||||||
|
RequestSize,
|
||||||
|
RequestOptions,
|
||||||
|
ReplyBuffer,
|
||||||
|
ReplySize,
|
||||||
|
Timeout);
|
||||||
|
if (n != 0) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
if (ReplyBuffer == nullptr || ReplySize < sizeof(ICMP_ECHO_REPLY) || RequestData == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (RequestSize < sizeof(IcmpHdr)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const auto *req = reinterpret_cast<const uint8_t *>(RequestData);
|
||||||
|
const auto *ih = reinterpret_cast<const IcmpHdr *>(req);
|
||||||
|
if (ih->type != 8 || ih->code != 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(ReplyBuffer, 0, ReplySize);
|
||||||
|
auto *reply = reinterpret_cast<ICMP_ECHO_REPLY *>(ReplyBuffer);
|
||||||
|
const size_t data_off = offsetof(ICMP_ECHO_REPLY, Data);
|
||||||
|
const size_t avail = ReplySize > data_off ? ReplySize - data_off : 0;
|
||||||
|
const size_t copy_len = (std::min)(static_cast<size_t>(RequestSize), avail);
|
||||||
|
reply->Address = DestinationAddress;
|
||||||
|
reply->Status = IP_SUCCESS;
|
||||||
|
reply->RoundTripTime = 1;
|
||||||
|
reply->DataSize = static_cast<USHORT>(copy_len);
|
||||||
|
if (copy_len > 0) {
|
||||||
|
memcpy(reinterpret_cast<uint8_t *>(ReplyBuffer) + data_off, RequestData, copy_len);
|
||||||
|
}
|
||||||
|
SetLastError(ERROR_SUCCESS);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void icmphook_iphlpapi_install() {
|
||||||
|
static bool done = false;
|
||||||
|
if (done) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
done = true;
|
||||||
|
if (!detour::trampoline_try(
|
||||||
|
"iphlpapi.dll",
|
||||||
|
"IcmpSendEcho",
|
||||||
|
(void *) IcmpSendEcho_hook,
|
||||||
|
(void **) &IcmpSendEcho_orig)) {
|
||||||
|
log_warning("network", "ICMP emulation: IcmpSendEcho hook was not installed");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,692 @@
|
|||||||
|
#include "icmphook_net.h"
|
||||||
|
|
||||||
|
#include "util/detour.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
|
||||||
|
#include <iphlpapi.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <deque>
|
||||||
|
#include <vector>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstring>
|
||||||
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
void icmphook_iphlpapi_install();
|
||||||
|
|
||||||
|
#ifndef SPICE64
|
||||||
|
using socket_uint = unsigned int;
|
||||||
|
#else
|
||||||
|
using socket_uint = unsigned long long;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace icmphook_internal {
|
||||||
|
|
||||||
|
constexpr uint32_t k_loopback_host = 0x7f000001u;
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct IcmpHeaderView {
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t code;
|
||||||
|
uint16_t checksum_be;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
bool icmp_packet_valid(const uint8_t *icmp, size_t len) {
|
||||||
|
if (len < sizeof(IcmpHeaderView)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
unsigned sum = 0;
|
||||||
|
for (size_t i = 0; i + 1 < len; i += 2) {
|
||||||
|
sum += (static_cast<unsigned>(icmp[i]) << 8) | icmp[i + 1];
|
||||||
|
sum = (sum >> 16) + (sum & 0xFFFF);
|
||||||
|
}
|
||||||
|
if (len & 1) {
|
||||||
|
sum += static_cast<unsigned>(icmp[len - 1]) << 8;
|
||||||
|
sum = (sum >> 16) + (sum & 0xFFFF);
|
||||||
|
}
|
||||||
|
sum = (sum >> 16) + (sum & 0xFFFF);
|
||||||
|
return sum == 0xFFFFu || sum == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool icmp_build_echo_reply(std::vector<uint8_t> &out, const uint8_t *req, size_t req_len) {
|
||||||
|
if (req_len < sizeof(IcmpHeaderView)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out.resize(req_len);
|
||||||
|
memcpy(out.data(), req, req_len);
|
||||||
|
auto *hdr = reinterpret_cast<IcmpHeaderView *>(out.data());
|
||||||
|
hdr->type = 0;
|
||||||
|
hdr->code = 0;
|
||||||
|
hdr->checksum_be = 0;
|
||||||
|
unsigned sum = 0;
|
||||||
|
for (size_t i = 0; i + 1 < out.size(); i += 2) {
|
||||||
|
sum += (static_cast<unsigned>(out[i]) << 8) | out[i + 1];
|
||||||
|
sum = (sum >> 16) + (sum & 0xFFFF);
|
||||||
|
}
|
||||||
|
if (out.size() & 1) {
|
||||||
|
sum += static_cast<unsigned>(out[out.size() - 1]) << 8;
|
||||||
|
sum = (sum >> 16) + (sum & 0xFFFF);
|
||||||
|
}
|
||||||
|
sum = (sum >> 16) + (sum & 0xFFFF);
|
||||||
|
uint16_t csum = static_cast<uint16_t>(~sum);
|
||||||
|
hdr->checksum_be = htons(csum);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool icmp_fix_pointer(
|
||||||
|
const uint8_t *base,
|
||||||
|
size_t total_len,
|
||||||
|
const uint8_t **icmp_start,
|
||||||
|
size_t *icmp_len) {
|
||||||
|
if (total_len >= 20 && (base[0] >> 4) == 4) {
|
||||||
|
size_t ihl = (base[0] & 0x0Fu) * 4;
|
||||||
|
if (ihl < 20 || ihl > total_len || base[9] != IPPROTO_ICMP) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*icmp_start = base + ihl;
|
||||||
|
*icmp_len = total_len - ihl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
*icmp_start = base;
|
||||||
|
*icmp_len = total_len;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ipv4_encode_datagram(
|
||||||
|
std::vector<uint8_t> &out,
|
||||||
|
uint32_t src_host_order,
|
||||||
|
uint32_t dst_host_order,
|
||||||
|
const uint8_t *payload,
|
||||||
|
size_t payload_len) {
|
||||||
|
const size_t hdr_len = 20;
|
||||||
|
if (payload_len > 0xFFFFu - hdr_len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto total_len = static_cast<uint16_t>(hdr_len + payload_len);
|
||||||
|
out.resize(hdr_len + payload_len);
|
||||||
|
uint8_t *p = out.data();
|
||||||
|
memset(p, 0, hdr_len);
|
||||||
|
p[0] = 0x45;
|
||||||
|
p[1] = 0;
|
||||||
|
p[2] = static_cast<uint8_t>(total_len >> 8);
|
||||||
|
p[3] = static_cast<uint8_t>(total_len & 0xFF);
|
||||||
|
p[6] = 0x40;
|
||||||
|
p[8] = 1;
|
||||||
|
p[9] = IPPROTO_ICMP;
|
||||||
|
uint32_t src_wire = htonl(src_host_order);
|
||||||
|
uint32_t dst_wire = htonl(dst_host_order);
|
||||||
|
memcpy(p + 12, &src_wire, 4);
|
||||||
|
memcpy(p + 16, &dst_wire, 4);
|
||||||
|
unsigned sum = 0;
|
||||||
|
for (size_t i = 0; i < hdr_len; i += 2) {
|
||||||
|
sum += (static_cast<unsigned>(p[i]) << 8) | p[i + 1];
|
||||||
|
while (sum >> 16) {
|
||||||
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint16_t hdr_csum = static_cast<uint16_t>(~sum);
|
||||||
|
p[10] = static_cast<uint8_t>(hdr_csum >> 8);
|
||||||
|
p[11] = static_cast<uint8_t>(hdr_csum & 0xFF);
|
||||||
|
memcpy(p + hdr_len, payload, payload_len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cached adapter list. Populated once per process: adapter changes after the
|
||||||
|
// first ICMP send are intentionally not picked up, in exchange for avoiding
|
||||||
|
// repeated IP Helper calls on every emulated send.
|
||||||
|
std::once_flag g_adapters_cache_init;
|
||||||
|
std::vector<uint8_t> g_adapters_cache;
|
||||||
|
|
||||||
|
void fill_adapters_cache() {
|
||||||
|
std::call_once(g_adapters_cache_init, []() {
|
||||||
|
ULONG buf_len = 0;
|
||||||
|
DWORD r = GetAdaptersInfo(nullptr, &buf_len);
|
||||||
|
if (r != ERROR_BUFFER_OVERFLOW && r != ERROR_SUCCESS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (buf_len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_adapters_cache.resize(buf_len);
|
||||||
|
auto *info = reinterpret_cast<PIP_ADAPTER_INFO>(g_adapters_cache.data());
|
||||||
|
if (GetAdaptersInfo(info, &buf_len) != ERROR_SUCCESS) {
|
||||||
|
g_adapters_cache.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t local_ipv4_for_peer(
|
||||||
|
uint32_t peer_host_order,
|
||||||
|
uint32_t bind_pref_host_order,
|
||||||
|
bool has_bind) {
|
||||||
|
if (peer_host_order == k_loopback_host) {
|
||||||
|
return k_loopback_host;
|
||||||
|
}
|
||||||
|
if (has_bind && bind_pref_host_order != 0) {
|
||||||
|
return bind_pref_host_order;
|
||||||
|
}
|
||||||
|
fill_adapters_cache();
|
||||||
|
if (g_adapters_cache.empty()) {
|
||||||
|
return peer_host_order;
|
||||||
|
}
|
||||||
|
auto *info = reinterpret_cast<PIP_ADAPTER_INFO>(g_adapters_cache.data());
|
||||||
|
for (PIP_ADAPTER_INFO a = info; a != nullptr; a = a->Next) {
|
||||||
|
uint32_t ip = ntohl(inet_addr(a->IpAddressList.IpAddress.String));
|
||||||
|
uint32_t mask = ntohl(inet_addr(a->IpAddressList.IpMask.String));
|
||||||
|
if (mask == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((peer_host_order & mask) == (ip & mask)) {
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return peer_host_order;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EmuSock {
|
||||||
|
std::deque<std::vector<uint8_t>> queue;
|
||||||
|
std::condition_variable_any cv;
|
||||||
|
DWORD rx_timeout_ms = INFINITE;
|
||||||
|
bool nonblocking = false;
|
||||||
|
bool has_bind = false;
|
||||||
|
uint32_t bind_ip_host = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::recursive_mutex g_mu;
|
||||||
|
std::unordered_map<SOCKET, std::unique_ptr<EmuSock>> g_socks;
|
||||||
|
std::atomic<uint32_t> g_seq{1};
|
||||||
|
|
||||||
|
// Fast-path flag so the bind() hook in networkhook.cpp can short-circuit
|
||||||
|
// without taking g_mu when the ICMP feature was never enabled.
|
||||||
|
std::atomic<bool> g_installed{false};
|
||||||
|
|
||||||
|
decltype(socket) *socket_orig = nullptr;
|
||||||
|
decltype(WSASocketW) *WSASocketW_orig = nullptr;
|
||||||
|
decltype(WSASocketA) *WSASocketA_orig = nullptr;
|
||||||
|
decltype(closesocket) *closesocket_orig = nullptr;
|
||||||
|
decltype(bind) *bind_trampoline_orig = nullptr;
|
||||||
|
decltype(sendto) *sendto_orig = nullptr;
|
||||||
|
decltype(recvfrom) *recvfrom_orig = nullptr;
|
||||||
|
decltype(WSASendTo) *WSASendTo_orig = nullptr;
|
||||||
|
decltype(WSARecvFrom) *WSARecvFrom_orig = nullptr;
|
||||||
|
decltype(ioctlsocket) *ioctlsocket_orig = nullptr;
|
||||||
|
decltype(setsockopt) *setsockopt_orig = nullptr;
|
||||||
|
|
||||||
|
EmuSock *lookup(SOCKET s) {
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(g_mu);
|
||||||
|
auto it = g_socks.find(s);
|
||||||
|
if (it == g_socks.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return it->second.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
SOCKET alloc_icmp_socket() {
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(g_mu);
|
||||||
|
for (;;) {
|
||||||
|
uint32_t x = g_seq.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
SOCKET s = (SOCKET)(socket_uint)(0xE0000000u | (x & 0x0FFFFFFFu));
|
||||||
|
if (s == INVALID_SOCKET) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (g_socks.find(s) == g_socks.end()) {
|
||||||
|
g_socks[s] = std::make_unique<EmuSock>();
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void release_icmp_socket(SOCKET s) {
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(g_mu);
|
||||||
|
auto it = g_socks.find(s);
|
||||||
|
if (it != g_socks.end()) {
|
||||||
|
it->second->cv.notify_all();
|
||||||
|
g_socks.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool process_icmp_send(SOCKET s, const uint8_t *buf, int len, const sockaddr *to, int tolen) {
|
||||||
|
if (len <= 0 || tolen < (int) sizeof(sockaddr_in) || !buf || !to) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auto *sin = reinterpret_cast<const sockaddr_in *>(to);
|
||||||
|
if (sin->sin_family != AF_INET) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
uint32_t peer_host = ntohl(sin->sin_addr.s_addr);
|
||||||
|
|
||||||
|
const uint8_t *icmp_ptr = nullptr;
|
||||||
|
size_t icmp_len = 0;
|
||||||
|
const auto *raw = reinterpret_cast<const uint8_t *>(buf);
|
||||||
|
if (!icmp_fix_pointer(raw, static_cast<size_t>(len), &icmp_ptr, &icmp_len)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!icmp_packet_valid(icmp_ptr, icmp_len)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (icmp_len < sizeof(IcmpHeaderView)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const auto *ih = reinterpret_cast<const IcmpHeaderView *>(icmp_ptr);
|
||||||
|
if (ih->type != 8 || ih->code != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> icmp_reply;
|
||||||
|
if (!icmp_build_echo_reply(icmp_reply, icmp_ptr, icmp_len)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> packet;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(g_mu);
|
||||||
|
auto it = g_socks.find(s);
|
||||||
|
if (it == g_socks.end()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
EmuSock *es = it->second.get();
|
||||||
|
uint32_t local_host = local_ipv4_for_peer(peer_host, es->bind_ip_host, es->has_bind);
|
||||||
|
if (!ipv4_encode_datagram(
|
||||||
|
packet, peer_host, local_host, icmp_reply.data(), icmp_reply.size())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
es->queue.push_back(std::move(packet));
|
||||||
|
es->cv.notify_all();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SOCKET WINAPI socket_hook(int af, int type, int protocol) {
|
||||||
|
if (af == AF_INET && type == SOCK_RAW && protocol == IPPROTO_ICMP) {
|
||||||
|
SOCKET s = alloc_icmp_socket();
|
||||||
|
log_info("network", "ICMP emulation: allocated raw socket {}", (socket_uint) s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return socket_orig(af, type, protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
SOCKET WINAPI WSASocketW_hook(
|
||||||
|
int af,
|
||||||
|
int type,
|
||||||
|
int protocol,
|
||||||
|
LPWSAPROTOCOL_INFOW lpProtocolInfo,
|
||||||
|
GROUP g,
|
||||||
|
DWORD dwFlags) {
|
||||||
|
const bool is_raw_icmp =
|
||||||
|
af == AF_INET && type == SOCK_RAW && protocol == IPPROTO_ICMP;
|
||||||
|
if (lpProtocolInfo == nullptr && is_raw_icmp) {
|
||||||
|
SOCKET s = alloc_icmp_socket();
|
||||||
|
log_info("network", "ICMP emulation: allocated raw WSASocketW {}", (socket_uint) s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return WSASocketW_orig(af, type, protocol, lpProtocolInfo, g, dwFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
SOCKET WINAPI WSASocketA_hook(
|
||||||
|
int af,
|
||||||
|
int type,
|
||||||
|
int protocol,
|
||||||
|
LPWSAPROTOCOL_INFOA lpProtocolInfo,
|
||||||
|
GROUP g,
|
||||||
|
DWORD dwFlags) {
|
||||||
|
const bool is_raw_icmp =
|
||||||
|
af == AF_INET && type == SOCK_RAW && protocol == IPPROTO_ICMP;
|
||||||
|
if (lpProtocolInfo == nullptr && is_raw_icmp) {
|
||||||
|
SOCKET s = alloc_icmp_socket();
|
||||||
|
log_info("network", "ICMP emulation: allocated raw WSASocketA {}", (socket_uint) s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return WSASocketA_orig(af, type, protocol, lpProtocolInfo, g, dwFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
int WINAPI closesocket_hook(SOCKET s) {
|
||||||
|
if (lookup(s)) {
|
||||||
|
release_icmp_socket(s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return closesocket_orig(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
int WINAPI bind_hook_ws2(SOCKET s, const sockaddr *name, int namelen) {
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(g_mu);
|
||||||
|
auto it = g_socks.find(s);
|
||||||
|
if (it != g_socks.end() && name && namelen >= (int) sizeof(sockaddr_in)) {
|
||||||
|
auto *in = reinterpret_cast<const sockaddr_in *>(name);
|
||||||
|
if (in->sin_family != AF_INET) {
|
||||||
|
WSASetLastError(WSAEAFNOSUPPORT);
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
it->second->has_bind = true;
|
||||||
|
it->second->bind_ip_host = ntohl(in->sin_addr.s_addr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bind_trampoline_orig(s, name, namelen);
|
||||||
|
}
|
||||||
|
|
||||||
|
int WINAPI sendto_hook(
|
||||||
|
SOCKET s, const char *buf, int len, int flags, const sockaddr *to, int tolen) {
|
||||||
|
EmuSock *es = lookup(s);
|
||||||
|
if (!es) {
|
||||||
|
return sendto_orig(s, buf, len, flags, to, tolen);
|
||||||
|
}
|
||||||
|
if (!buf || !to) {
|
||||||
|
WSASetLastError(WSAEFAULT);
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
process_icmp_send(s, reinterpret_cast<const uint8_t *>(buf), len, to, tolen);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WINAPI WSASendTo_hook(
|
||||||
|
SOCKET s,
|
||||||
|
LPWSABUF lpBuffers,
|
||||||
|
DWORD dwBufferCount,
|
||||||
|
LPDWORD lpNumberOfBytesSent,
|
||||||
|
DWORD dwFlags,
|
||||||
|
const sockaddr *lpTo,
|
||||||
|
int iTolen,
|
||||||
|
LPWSAOVERLAPPED lpOverlapped,
|
||||||
|
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) {
|
||||||
|
EmuSock *es = lookup(s);
|
||||||
|
if (!es) {
|
||||||
|
return WSASendTo_orig(
|
||||||
|
s,
|
||||||
|
lpBuffers,
|
||||||
|
dwBufferCount,
|
||||||
|
lpNumberOfBytesSent,
|
||||||
|
dwFlags,
|
||||||
|
lpTo,
|
||||||
|
iTolen,
|
||||||
|
lpOverlapped,
|
||||||
|
lpCompletionRoutine);
|
||||||
|
}
|
||||||
|
if (lpOverlapped != nullptr) {
|
||||||
|
WSASetLastError(WSAEOPNOTSUPP);
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
if (dwBufferCount != 1 || !lpBuffers || !lpNumberOfBytesSent) {
|
||||||
|
WSASetLastError(WSAEINVAL);
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
int len = (int) lpBuffers[0].len;
|
||||||
|
process_icmp_send(
|
||||||
|
s,
|
||||||
|
reinterpret_cast<const uint8_t *>(lpBuffers[0].buf),
|
||||||
|
len,
|
||||||
|
lpTo,
|
||||||
|
iTolen);
|
||||||
|
*lpNumberOfBytesSent = static_cast<DWORD>(len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dequeue one datagram for an emulated ICMP socket into the caller-provided
|
||||||
|
// buffer. If out_full_dgram_bytes is non-null, it receives the full packet
|
||||||
|
// size (before truncation to len) so callers can signal WSAEMSGSIZE.
|
||||||
|
int emu_recv_dequeue(
|
||||||
|
SOCKET s, char *buf, int len, sockaddr *from, int *fromlen,
|
||||||
|
size_t *out_full_dgram_bytes) {
|
||||||
|
if (!buf || len <= 0) {
|
||||||
|
WSASetLastError(WSAEINVAL);
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> pkt;
|
||||||
|
{
|
||||||
|
std::unique_lock<std::recursive_mutex> lock(g_mu);
|
||||||
|
auto it = g_socks.find(s);
|
||||||
|
if (it == g_socks.end()) {
|
||||||
|
WSASetLastError(WSAENOTSOCK);
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto wait_pred = [&] {
|
||||||
|
auto j = g_socks.find(s);
|
||||||
|
return j == g_socks.end() || !j->second->queue.empty();
|
||||||
|
};
|
||||||
|
|
||||||
|
EmuSock *es = it->second.get();
|
||||||
|
if (es->nonblocking) {
|
||||||
|
if (es->queue.empty()) {
|
||||||
|
WSASetLastError(WSAEWOULDBLOCK);
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
} else if (es->rx_timeout_ms == INFINITE || es->rx_timeout_ms == 0) {
|
||||||
|
it->second->cv.wait(lock, wait_pred);
|
||||||
|
} else {
|
||||||
|
const auto timeout = std::chrono::milliseconds(es->rx_timeout_ms);
|
||||||
|
if (!it->second->cv.wait_for(lock, timeout, wait_pred)) {
|
||||||
|
WSASetLastError(WSAETIMEDOUT);
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it = g_socks.find(s);
|
||||||
|
if (it == g_socks.end()) {
|
||||||
|
WSASetLastError(WSAENOTSOCK);
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
es = it->second.get();
|
||||||
|
if (es->queue.empty()) {
|
||||||
|
WSASetLastError(WSAENOTSOCK);
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
pkt = std::move(es->queue.front());
|
||||||
|
es->queue.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_full_dgram_bytes) {
|
||||||
|
*out_full_dgram_bytes = pkt.size();
|
||||||
|
}
|
||||||
|
const int take = (len < (int) pkt.size()) ? len : (int) pkt.size();
|
||||||
|
memcpy(buf, pkt.data(), static_cast<size_t>(take));
|
||||||
|
if (from && fromlen && *fromlen >= (int) sizeof(sockaddr_in)) {
|
||||||
|
auto *out = reinterpret_cast<sockaddr_in *>(from);
|
||||||
|
memset(out, 0, sizeof(*out));
|
||||||
|
out->sin_family = AF_INET;
|
||||||
|
if (pkt.size() >= 20) {
|
||||||
|
memcpy(&out->sin_addr, pkt.data() + 12, 4);
|
||||||
|
} else {
|
||||||
|
out->sin_addr.s_addr = htonl(k_loopback_host);
|
||||||
|
}
|
||||||
|
*fromlen = sizeof(sockaddr_in);
|
||||||
|
}
|
||||||
|
return take;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WINAPI recvfrom_hook(
|
||||||
|
SOCKET s, char *buf, int len, int flags, sockaddr *from, int *fromlen) {
|
||||||
|
if (!lookup(s)) {
|
||||||
|
return recvfrom_orig(s, buf, len, flags, from, fromlen);
|
||||||
|
}
|
||||||
|
return emu_recv_dequeue(s, buf, len, from, fromlen, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int WINAPI WSARecvFrom_hook(
|
||||||
|
SOCKET s,
|
||||||
|
LPWSABUF lpBuffers,
|
||||||
|
DWORD dwBufferCount,
|
||||||
|
LPDWORD lpNumberOfBytesRecvd,
|
||||||
|
LPDWORD lpFlags,
|
||||||
|
sockaddr *lpFrom,
|
||||||
|
LPINT lpFromlen,
|
||||||
|
LPWSAOVERLAPPED lpOverlapped,
|
||||||
|
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) {
|
||||||
|
if (!lookup(s)) {
|
||||||
|
return WSARecvFrom_orig(
|
||||||
|
s,
|
||||||
|
lpBuffers,
|
||||||
|
dwBufferCount,
|
||||||
|
lpNumberOfBytesRecvd,
|
||||||
|
lpFlags,
|
||||||
|
lpFrom,
|
||||||
|
lpFromlen,
|
||||||
|
lpOverlapped,
|
||||||
|
lpCompletionRoutine);
|
||||||
|
}
|
||||||
|
if (lpOverlapped != nullptr) {
|
||||||
|
WSASetLastError(WSAEOPNOTSUPP);
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
if (dwBufferCount != 1 || !lpBuffers || !lpNumberOfBytesRecvd) {
|
||||||
|
WSASetLastError(WSAEINVAL);
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
sockaddr_in from_tmp{};
|
||||||
|
int fromlen_tmp = sizeof(from_tmp);
|
||||||
|
size_t full_bytes = 0;
|
||||||
|
int r = emu_recv_dequeue(
|
||||||
|
s,
|
||||||
|
lpBuffers[0].buf,
|
||||||
|
(int) lpBuffers[0].len,
|
||||||
|
reinterpret_cast<sockaddr *>(&from_tmp),
|
||||||
|
&fromlen_tmp,
|
||||||
|
&full_bytes);
|
||||||
|
if (r == SOCKET_ERROR) {
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
if (full_bytes > lpBuffers[0].len) {
|
||||||
|
WSASetLastError(WSAEMSGSIZE);
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
*lpNumberOfBytesRecvd = static_cast<DWORD>(r);
|
||||||
|
if (lpFlags) {
|
||||||
|
*lpFlags = 0;
|
||||||
|
}
|
||||||
|
if (lpFrom && lpFromlen && *lpFromlen >= (int) sizeof(sockaddr_in)) {
|
||||||
|
*reinterpret_cast<sockaddr_in *>(lpFrom) = from_tmp;
|
||||||
|
*lpFromlen = sizeof(sockaddr_in);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WINAPI ioctlsocket_hook(SOCKET s, long cmd, u_long *argp) {
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(g_mu);
|
||||||
|
auto it = g_socks.find(s);
|
||||||
|
if (it != g_socks.end()) {
|
||||||
|
EmuSock *es = it->second.get();
|
||||||
|
if (argp && static_cast<u_long>(cmd) == FIONBIO) {
|
||||||
|
es->nonblocking = (*argp != 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
WSASetLastError(WSAEOPNOTSUPP);
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ioctlsocket_orig(s, cmd, argp);
|
||||||
|
}
|
||||||
|
|
||||||
|
int WINAPI setsockopt_hook(
|
||||||
|
SOCKET s, int level, int optname, const char *optval, int optlen) {
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(g_mu);
|
||||||
|
auto it = g_socks.find(s);
|
||||||
|
if (it != g_socks.end()) {
|
||||||
|
const bool deref_optval = level == SOL_SOCKET && optlen >= (int) sizeof(DWORD);
|
||||||
|
if (deref_optval && optval) {
|
||||||
|
EmuSock *es = it->second.get();
|
||||||
|
auto v = reinterpret_cast<const DWORD *>(optval);
|
||||||
|
if (optname == SO_RCVTIMEO) {
|
||||||
|
DWORD ms = *v;
|
||||||
|
es->rx_timeout_ms = (ms == 0) ? INFINITE : ms;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (optname == SO_SNDTIMEO) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
WSASetLastError(WSAEOPNOTSUPP);
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
if (!deref_optval) {
|
||||||
|
WSASetLastError(WSAEOPNOTSUPP);
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return setsockopt_orig(s, level, optname, optval, optlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void install_icmphook_hooks() {
|
||||||
|
static bool done = false;
|
||||||
|
if (done) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
done = true;
|
||||||
|
|
||||||
|
bool ok = true;
|
||||||
|
ok &= detour::trampoline_try(
|
||||||
|
"ws2_32.dll", "socket",
|
||||||
|
(void *) socket_hook, (void **) &socket_orig);
|
||||||
|
ok &= detour::trampoline_try(
|
||||||
|
"ws2_32.dll", "WSASocketW",
|
||||||
|
(void *) WSASocketW_hook, (void **) &WSASocketW_orig);
|
||||||
|
ok &= detour::trampoline_try(
|
||||||
|
"ws2_32.dll", "WSASocketA",
|
||||||
|
(void *) WSASocketA_hook, (void **) &WSASocketA_orig);
|
||||||
|
ok &= detour::trampoline_try(
|
||||||
|
"ws2_32.dll", "closesocket",
|
||||||
|
(void *) closesocket_hook, (void **) &closesocket_orig);
|
||||||
|
ok &= detour::trampoline_try(
|
||||||
|
"ws2_32.dll", "bind",
|
||||||
|
(void *) bind_hook_ws2, (void **) &bind_trampoline_orig);
|
||||||
|
ok &= detour::trampoline_try(
|
||||||
|
"ws2_32.dll", "sendto",
|
||||||
|
(void *) sendto_hook, (void **) &sendto_orig);
|
||||||
|
ok &= detour::trampoline_try(
|
||||||
|
"ws2_32.dll", "recvfrom",
|
||||||
|
(void *) recvfrom_hook, (void **) &recvfrom_orig);
|
||||||
|
ok &= detour::trampoline_try(
|
||||||
|
"ws2_32.dll", "WSASendTo",
|
||||||
|
(void *) WSASendTo_hook, (void **) &WSASendTo_orig);
|
||||||
|
ok &= detour::trampoline_try(
|
||||||
|
"ws2_32.dll", "WSARecvFrom",
|
||||||
|
(void *) WSARecvFrom_hook, (void **) &WSARecvFrom_orig);
|
||||||
|
ok &= detour::trampoline_try(
|
||||||
|
"ws2_32.dll", "ioctlsocket",
|
||||||
|
(void *) ioctlsocket_hook, (void **) &ioctlsocket_orig);
|
||||||
|
ok &= detour::trampoline_try(
|
||||||
|
"ws2_32.dll", "setsockopt",
|
||||||
|
(void *) setsockopt_hook, (void **) &setsockopt_orig);
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
log_warning(
|
||||||
|
"network",
|
||||||
|
"ICMP emulation: one or more ws2_32 hooks failed to install");
|
||||||
|
} else {
|
||||||
|
log_info("network", "ICMP emulation hooks installed (raw ICMP sockets)");
|
||||||
|
}
|
||||||
|
|
||||||
|
g_installed.store(true, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace icmphook_internal
|
||||||
|
|
||||||
|
bool icmphook_is_emulated_socket(SOCKET s) {
|
||||||
|
if (!icmphook_internal::g_installed.load(std::memory_order_acquire)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(icmphook_internal::g_mu);
|
||||||
|
return icmphook_internal::g_socks.find(s) != icmphook_internal::g_socks.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool icmphook_try_bind(SOCKET s, const struct sockaddr *name, int namelen, int *out_result) {
|
||||||
|
if (!icmphook_internal::g_installed.load(std::memory_order_acquire)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(icmphook_internal::g_mu);
|
||||||
|
if (icmphook_internal::g_socks.find(s) == icmphook_internal::g_socks.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*out_result = icmphook_internal::bind_hook_ws2(s, name, namelen);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void icmphook_net_init() {
|
||||||
|
icmphook_internal::install_icmphook_hooks();
|
||||||
|
icmphook_iphlpapi_install();
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <winsock2.h>
|
||||||
|
|
||||||
|
bool icmphook_is_emulated_socket(SOCKET s);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Handle bind() for emulated ICMP sockets: records interface address and succeeds without kernel bind.
|
||||||
|
* Returns true if this socket was handled (caller should return 0).
|
||||||
|
*/
|
||||||
|
bool icmphook_try_bind(SOCKET s, const struct sockaddr *name, int namelen, int *out_result);
|
||||||
|
|
||||||
|
void icmphook_net_init();
|
||||||
@@ -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");
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "util/fileutils.h"
|
#include "util/fileutils.h"
|
||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
#include "util/deferlog.h"
|
#include "util/deferlog.h"
|
||||||
|
#include "hooks/icmphook_net.h"
|
||||||
|
|
||||||
// hooking related stuff
|
// hooking related stuff
|
||||||
static decltype(GetAdaptersInfo) *GetAdaptersInfo_orig = nullptr;
|
static decltype(GetAdaptersInfo) *GetAdaptersInfo_orig = nullptr;
|
||||||
@@ -189,6 +190,11 @@ static int WINAPI bind_hook(SOCKET s, const struct sockaddr *name, int namelen)
|
|||||||
#pragma ide diagnostic ignored "OCDFAInspection"
|
#pragma ide diagnostic ignored "OCDFAInspection"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
int icmp_bind_result = 0;
|
||||||
|
if (icmphook_try_bind(s, name, namelen, &icmp_bind_result)) {
|
||||||
|
return icmp_bind_result;
|
||||||
|
}
|
||||||
|
|
||||||
// cast to sockaddr_in
|
// cast to sockaddr_in
|
||||||
struct sockaddr_in *in_name = (struct sockaddr_in *) name;
|
struct sockaddr_in *in_name = (struct sockaddr_in *) name;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <shlwapi.h>
|
#include <shlwapi.h>
|
||||||
|
#include <windows.h>
|
||||||
#include <cfg/configurator.h>
|
#include <cfg/configurator.h>
|
||||||
|
|
||||||
#include "api/modules/capture.h"
|
#include "api/modules/capture.h"
|
||||||
@@ -76,6 +77,7 @@
|
|||||||
#include "hooks/graphics/graphics.h"
|
#include "hooks/graphics/graphics.h"
|
||||||
#include "hooks/lang.h"
|
#include "hooks/lang.h"
|
||||||
#include "hooks/networkhook.h"
|
#include "hooks/networkhook.h"
|
||||||
|
#include "hooks/icmphook_net.h"
|
||||||
#include "hooks/unisintrhook.h"
|
#include "hooks/unisintrhook.h"
|
||||||
#include "launcher/launcher.h"
|
#include "launcher/launcher.h"
|
||||||
#include "launcher/logger.h"
|
#include "launcher/logger.h"
|
||||||
@@ -98,6 +100,7 @@
|
|||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
#include "rawinput/touch.h"
|
#include "rawinput/touch.h"
|
||||||
#include "reader/reader.h"
|
#include "reader/reader.h"
|
||||||
|
#include "sdk/sdk.h"
|
||||||
#include "stubs/stubs.h"
|
#include "stubs/stubs.h"
|
||||||
#include "touch/touch.h"
|
#include "touch/touch.h"
|
||||||
#include "util/cpuutils.h"
|
#include "util/cpuutils.h"
|
||||||
@@ -107,6 +110,7 @@
|
|||||||
#include "util/libutils.h"
|
#include "util/libutils.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/peb.h"
|
#include "util/peb.h"
|
||||||
|
#include "util/precise_timer.h"
|
||||||
#include "util/socd_cleaner.h"
|
#include "util/socd_cleaner.h"
|
||||||
#include "util/sysutils.h"
|
#include "util/sysutils.h"
|
||||||
#include "util/tapeled.h"
|
#include "util/tapeled.h"
|
||||||
@@ -242,6 +246,7 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
bool easrv_smart = false;
|
bool easrv_smart = false;
|
||||||
bool load_stubs = false;
|
bool load_stubs = false;
|
||||||
bool netfix_disable = false;
|
bool netfix_disable = false;
|
||||||
|
bool icmphook_enable = false;
|
||||||
bool lang_disable = false;
|
bool lang_disable = false;
|
||||||
std::string process_priority_str = "high";
|
std::string process_priority_str = "high";
|
||||||
bool cardio_enabled = false;
|
bool cardio_enabled = false;
|
||||||
@@ -297,6 +302,26 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
auto &options = *options_ptr;
|
auto &options = *options_ptr;
|
||||||
|
|
||||||
|
#if !SPICE_XP
|
||||||
|
|
||||||
|
{
|
||||||
|
// skip elevation only if AutoElevate is explicitly set to "user"
|
||||||
|
const bool skip_elevation = options[launcher::Options::AutoElevate].is_active() &&
|
||||||
|
options[launcher::Options::AutoElevate].value_text() == "user";
|
||||||
|
if (!skip_elevation && !sysutils::is_running_as_admin()) {
|
||||||
|
log_info("launcher", "relaunching with administrator privileges");
|
||||||
|
if (sysutils::relaunch_as_admin()) {
|
||||||
|
exit(0);
|
||||||
|
} else {
|
||||||
|
// elevation failed or was denied by the user
|
||||||
|
log_fatal("launcher", "failed to launch with administrator privileges");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !SPICE_XP
|
||||||
|
|
||||||
// check options
|
// check options
|
||||||
// TODO: get rid of some booleans here and make use of the options directly
|
// TODO: get rid of some booleans here and make use of the options directly
|
||||||
if (options[launcher::Options::OpenConfigurator].value_bool()) {
|
if (options[launcher::Options::OpenConfigurator].value_bool()) {
|
||||||
@@ -383,6 +408,16 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
monitor_orientation = ORIENTATION_CW;
|
monitor_orientation = ORIENTATION_CW;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::pair<uint32_t, uint32_t>> monitor_resolution;
|
||||||
|
if (options[launcher::Options::ChangeResolution].is_active()) {
|
||||||
|
std::pair<uint32_t, uint32_t> result;
|
||||||
|
if (parse_width_height(options[launcher::Options::ChangeResolution].value_text(), result)) {
|
||||||
|
monitor_resolution = result;
|
||||||
|
} else if (!cfg_run && !cfg::CONFIGURATOR_STANDALONE) {
|
||||||
|
log_fatal("launcher", "failed to parse -changeres");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (options[launcher::Options::spice2x_NoD3D9DeviceHook].value_bool()) {
|
if (options[launcher::Options::spice2x_NoD3D9DeviceHook].value_bool()) {
|
||||||
D3D9_DEVICE_HOOK_DISABLE = true;
|
D3D9_DEVICE_HOOK_DISABLE = true;
|
||||||
// touch emulation gets disabled, might as well turn these on
|
// touch emulation gets disabled, might as well turn these on
|
||||||
@@ -473,6 +508,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 +625,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;
|
||||||
}
|
}
|
||||||
@@ -596,8 +644,11 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::GitaDoraCabinetType].is_active()) {
|
if (options[launcher::Options::GitaDoraCabinetType].is_active()) {
|
||||||
games::gitadora::CAB_TYPE = options[launcher::Options::GitaDoraCabinetType].value_uint32();
|
games::gitadora::CAB_TYPE = options[launcher::Options::GitaDoraCabinetType].value_uint32();
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::GitaDoraArenaSingleWindow].value_bool() && GRAPHICS_WINDOWED) {
|
if (options[launcher::Options::GitaDoraArenaSingleWindow].value_bool()) {
|
||||||
games::gitadora::ARENA_SINGLE_WINDOW = true;
|
// for full screen
|
||||||
|
GRAPHICS_FORCE_SINGLE_ADAPTER = true;
|
||||||
|
// for windowed
|
||||||
|
GRAPHICS_PREVENT_SECONDARY_WINDOW = true;
|
||||||
}
|
}
|
||||||
if (options[launcher::Options::GitaDoraWailHold].is_active()) {
|
if (options[launcher::Options::GitaDoraWailHold].is_active()) {
|
||||||
socd::TILT_HOLD_MS = options[launcher::Options::GitaDoraWailHold].value_uint32();
|
socd::TILT_HOLD_MS = options[launcher::Options::GitaDoraWailHold].value_uint32();
|
||||||
@@ -707,6 +758,9 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::DisableNetworkFixes].value_bool()) {
|
if (options[launcher::Options::DisableNetworkFixes].value_bool()) {
|
||||||
netfix_disable = true;
|
netfix_disable = true;
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::EnableICMPHook].value_bool()) {
|
||||||
|
icmphook_enable = true;
|
||||||
|
}
|
||||||
if (options[launcher::Options::DisableACPHook].value_bool()) {
|
if (options[launcher::Options::DisableACPHook].value_bool()) {
|
||||||
lang_disable = true;
|
lang_disable = true;
|
||||||
}
|
}
|
||||||
@@ -864,6 +918,12 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
PIN_MACRO_ENABLED = true;
|
PIN_MACRO_ENABLED = true;
|
||||||
PIN_MACRO_VALUES[1] = options[launcher::Options::Player2PinMacro].value_text();
|
PIN_MACRO_VALUES[1] = options[launcher::Options::Player2PinMacro].value_text();
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::AutoPinMacroTrigger0].is_active()) {
|
||||||
|
AUTO_PIN_MACRO_TRIGGER[0] = options[launcher::Options::AutoPinMacroTrigger0].value_text();
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::AutoPinMacroTrigger1].is_active()) {
|
||||||
|
AUTO_PIN_MACRO_TRIGGER[1] = options[launcher::Options::AutoPinMacroTrigger1].value_text();
|
||||||
|
}
|
||||||
|
|
||||||
for (auto &reader : options[launcher::Options::ICCAReaderPort].values_text()) {
|
for (auto &reader : options[launcher::Options::ICCAReaderPort].values_text()) {
|
||||||
static int reader_id = 0;
|
static int reader_id = 0;
|
||||||
@@ -1107,6 +1167,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,13 +1188,37 @@ 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_ALGORITHM = games::jb::JubeatTouchAlgorithm::Legacy;
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::JubeatTouchAlgo].is_active()) {
|
||||||
|
if (options[launcher::Options::JubeatTouchAlgo].value_text() == "accurate") {
|
||||||
|
games::jb::TOUCH_ALGORITHM = games::jb::JubeatTouchAlgorithm::AcAccurate;
|
||||||
|
} else if (options[launcher::Options::JubeatTouchAlgo].value_text() == "legacy") {
|
||||||
|
games::jb::TOUCH_ALGORITHM = games::jb::JubeatTouchAlgorithm::Legacy;
|
||||||
|
} else {
|
||||||
|
games::jb::TOUCH_ALGORITHM = games::jb::JubeatTouchAlgorithm::Improved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reflec beat touch emulation
|
||||||
if (options[launcher::Options::spice2x_RBTouchScale].is_active()) {
|
if (options[launcher::Options::spice2x_RBTouchScale].is_active()) {
|
||||||
games::rb::TOUCH_SCALING = options[launcher::Options::spice2x_RBTouchScale].value_uint32();
|
games::rb::TOUCH_SCALING = options[launcher::Options::spice2x_RBTouchScale].value_uint32();
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::RBTouchSize].is_active()) {
|
||||||
|
const auto text = options[launcher::Options::RBTouchSize].value_text();
|
||||||
|
if (text == "3") {
|
||||||
|
games::rb::TOUCH_SIZE = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (options[launcher::Options::RBTouchPollRate].is_active()) {
|
||||||
|
games::rb::TOUCH_POLL_RATE = options[launcher::Options::RBTouchPollRate].value_uint32();
|
||||||
|
}
|
||||||
|
|
||||||
if (options[launcher::Options::spice2x_AsioForceUnload].value_bool()) {
|
if (options[launcher::Options::spice2x_AsioForceUnload].value_bool()) {
|
||||||
hooks::audio::ASIO_FORCE_UNLOAD_ON_STOP = true;
|
hooks::audio::ASIO_FORCE_UNLOAD_ON_STOP = true;
|
||||||
}
|
}
|
||||||
@@ -1255,6 +1340,10 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
games::loveplus::CAMERA_ENABLE = true;
|
games::loveplus::CAMERA_ENABLE = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options[launcher::Options::DisableHighResTimer].value_bool()) {
|
||||||
|
timeutils::TIMER_HACKS_DISABLE = true;
|
||||||
|
}
|
||||||
|
|
||||||
// API debugging
|
// API debugging
|
||||||
if (api_debug && !cfg::CONFIGURATOR_STANDALONE) {
|
if (api_debug && !cfg::CONFIGURATOR_STANDALONE) {
|
||||||
API_CONTROLLER = std::make_unique<api::Controller>(api_port, api_pass, api_pretty);
|
API_CONTROLLER = std::make_unique<api::Controller>(api_port, api_pass, api_pretty);
|
||||||
@@ -1294,7 +1383,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 +1403,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);
|
||||||
@@ -1508,16 +1603,18 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// early hooks
|
// early hooks
|
||||||
for (auto &hook : early_hooks) {
|
if (!cfg_run) {
|
||||||
log_info("launcher", "loading early hook DLL {}", hook);
|
for (auto &hook : early_hooks) {
|
||||||
libutils::print_dll_info(hook);
|
log_info("launcher", "loading early hook DLL {}", hook);
|
||||||
HMODULE module;
|
libutils::print_dll_info(hook);
|
||||||
if (!(module = libutils::try_library(hook))) {
|
HMODULE module;
|
||||||
log_warning("launcher", "failed to load early hook {}: {}", hook, get_last_error_string());
|
if (!(module = libutils::try_library(hook))) {
|
||||||
deferredlogs::defer_error_messages({
|
log_warning("launcher", "failed to load early hook {}: {}", hook, get_last_error_string());
|
||||||
fmt::format("failed to load early hook {}:", hook),
|
deferredlogs::defer_error_messages({
|
||||||
fmt::format(" {}", get_last_error_string())
|
fmt::format("failed to load early hook {}:", hook),
|
||||||
});
|
fmt::format(" {}", get_last_error_string())
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1745,11 +1842,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;
|
||||||
attach_hpm = 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;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1802,9 +1909,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");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2140,7 +2245,7 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::PrimaryMonitor].is_active()) {
|
if (options[launcher::Options::PrimaryMonitor].is_active()) {
|
||||||
change_primary_monitor(options[launcher::Options::PrimaryMonitor].value_text());
|
change_primary_monitor(options[launcher::Options::PrimaryMonitor].value_text());
|
||||||
}
|
}
|
||||||
update_monitor_on_boot(monitor_orientation, GRAPHICS_FORCE_REFRESH);
|
update_monitor_on_boot(monitor_orientation, GRAPHICS_FORCE_REFRESH, monitor_resolution);
|
||||||
|
|
||||||
// initialize raw input
|
// initialize raw input
|
||||||
RI_MGR = std::make_unique<rawinput::RawInputManager>();
|
RI_MGR = std::make_unique<rawinput::RawInputManager>();
|
||||||
@@ -2193,10 +2298,18 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
hooks::lang::early_init();
|
hooks::lang::early_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// use high resolution timer for this process
|
||||||
|
timeutils::set_timer_resolution();
|
||||||
|
|
||||||
// load DLLs
|
// load DLLs
|
||||||
avs::core::load_dll();
|
avs::core::load_dll();
|
||||||
avs::ea3::load_dll();
|
avs::ea3::load_dll();
|
||||||
|
|
||||||
|
// ICMP emulation (opt-in; before games open raw ICMP sockets)
|
||||||
|
if (icmphook_enable) {
|
||||||
|
icmphook_net_init();
|
||||||
|
}
|
||||||
|
|
||||||
// net fix
|
// net fix
|
||||||
if (!netfix_disable) {
|
if (!netfix_disable) {
|
||||||
networkhook_init();
|
networkhook_init();
|
||||||
@@ -2226,7 +2339,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();
|
||||||
@@ -2327,15 +2440,16 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
bt5api_hook(module);
|
bt5api_hook(module);
|
||||||
|
sdk::register_sdk_hooks(hook, module);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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"
|
||||||
@@ -2361,6 +2475,9 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
bt5api_init();
|
bt5api_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// init SDK
|
||||||
|
sdk::init_sdk_modules();
|
||||||
|
|
||||||
// API
|
// API
|
||||||
if (api_enable || std::min(api_serial_port.size(), api_serial_baud.size()) > 0) {
|
if (api_enable || std::min(api_serial_port.size(), api_serial_baud.size()) > 0) {
|
||||||
API_CONTROLLER = std::make_unique<api::Controller>(api_port, api_pass, api_pretty);
|
API_CONTROLLER = std::make_unique<api::Controller>(api_port, api_pass, api_pretty);
|
||||||
@@ -2468,6 +2585,8 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
bt5api_dispose();
|
bt5api_dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sdk::fini_sdk_modules();
|
||||||
|
|
||||||
// stop raw input
|
// stop raw input
|
||||||
RI_MGR.reset();
|
RI_MGR.reset();
|
||||||
|
|
||||||
@@ -2507,7 +2626,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",
|
||||||
@@ -202,7 +203,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
{
|
{
|
||||||
.title = "Show Cursor & Touch Emulation Enable",
|
.title = "Show Cursor & Touch Emulation Enable",
|
||||||
.name = "s",
|
.name = "s",
|
||||||
.desc = "Shows the cursor in the game window; also turns on touch emulation. Do not use if you use a real touch screen.",
|
.desc = "Shows the cursor in the game window; also turns on touch emulation. Do not enable this if you have a real touch screen.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.category = "Common",
|
.category = "Common",
|
||||||
},
|
},
|
||||||
@@ -264,7 +265,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.title = "Full Screen Orientation Swap (EXPERIMENTAL)",
|
.title = "Full Screen Orientation Swap (EXPERIMENTAL)",
|
||||||
.name = "forceresswap",
|
.name = "forceresswap",
|
||||||
.desc =
|
.desc =
|
||||||
"Allows you to play portrait games in in landscape (and vice versa) by transposing resolution and applying image scaling.\n\n"
|
"Allows you to play portrait games in landscape (and vice versa) by transposing resolution and applying image scaling.\n\n"
|
||||||
"Works great for some games, but can COMPLETELY BREAK other games - YMMV!\n\n"
|
"Works great for some games, but can COMPLETELY BREAK other games - YMMV!\n\n"
|
||||||
"Strongly consider combining this with -forceres option to render at monitor native resolution\n\n"
|
"Strongly consider combining this with -forceres option to render at monitor native resolution\n\n"
|
||||||
"WARNING: for SDVX, this messes with camera angle for note lanes, causing it to be zoomed out, and causes performance issues "
|
"WARNING: for SDVX, this messes with camera angle for note lanes, causing it to be zoomed out, and causes performance issues "
|
||||||
@@ -311,7 +312,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.name = "sp2x-dx9on12",
|
.name = "sp2x-dx9on12",
|
||||||
.display_name = "dx9on12",
|
.display_name = "dx9on12",
|
||||||
.aliases= "dx9on12",
|
.aliases= "dx9on12",
|
||||||
.desc = "Use D3D9On12 wrapper library, requires Windows 10. Has no effect games on that don't use DX9. Can cause some games to crash.\n\n"
|
.desc = "Use D3D9On12 wrapper library, requires Windows 10. Has no effect on games that don't use DX9. Can cause some games to crash.\n\n"
|
||||||
"Default: auto (use DX9 for most games, but turn on 9on12 for games that require it on non-NVIDIA GPUs).",
|
"Default: auto (use DX9 for most games, but turn on 9on12 for games that require it on non-NVIDIA GPUs).",
|
||||||
.type = OptionType::Enum,
|
.type = OptionType::Enum,
|
||||||
.category = "Graphics (Common)",
|
.category = "Graphics (Common)",
|
||||||
@@ -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",
|
||||||
@@ -994,9 +1038,10 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Advanced)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "GitaDora Two Channel Audio",
|
.title = "GitaDora Two Channel Audio (DX/SD only)",
|
||||||
.name = "2ch",
|
.name = "2ch",
|
||||||
.desc = "Attempt to reduce audio channels down to just two channels.",
|
.desc = "Attempt to reduce audio channels down to just two channels. "
|
||||||
|
"This option does nothing for Arena Model (GW Delta and above); need a patch for that.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "GitaDora",
|
.game_name = "GitaDora",
|
||||||
.category = "Game Options",
|
.category = "Game Options",
|
||||||
@@ -1012,9 +1057,12 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// GitaDoraArenaSingleWindow
|
// GitaDoraArenaSingleWindow
|
||||||
.title = "GitaDora Arena Single Window (EXPERIMENTAL)",
|
.title = "GitaDora Arena Disable Subscreens (EXPERIMENTAL)",
|
||||||
.name = "gdaonewindow",
|
.name = "gdaonewindow",
|
||||||
.desc = "For Arena Model, when combined with windowed mode, only show the main game window; enables subscreen overlay.",
|
.desc = "For Arena Model:\n\n"
|
||||||
|
"Windowed mode: instead of 4 windows, create 1 window.\n\n"
|
||||||
|
"Fullscreen mode: instead of requiring 4 monitors, use only the primary monitor. WARNING: requires 4K monitor.\n\n"
|
||||||
|
"To access the subscreen, use the subscreen overlay.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "GitaDora",
|
.game_name = "GitaDora",
|
||||||
.category = "Game Options",
|
.category = "Game Options",
|
||||||
@@ -1025,8 +1073,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.name = "gdlefty",
|
.name = "gdlefty",
|
||||||
.desc = "Enables lefty mode, flipping motion sensor directions. Default: off.\n\n"
|
.desc = "Enables lefty mode, flipping motion sensor directions. Default: off.\n\n"
|
||||||
"Without this option, enabling LEFT in the game option will continuously trigger UP wail.\n\n"
|
"Without this option, enabling LEFT in the game option will continuously trigger UP wail.\n\n"
|
||||||
"Has no effect if you are using analog bindings for X/Y axis; expectation is that your controller "
|
"Has no effect if you are using analog bindings for X/Y axis; most controllers do not handle this correctly.\n\n"
|
||||||
"handles this correctly (most do not, however).\n\n"
|
|
||||||
"As always, remember to restart the game after changing options. If you need to change in the game, "
|
"As always, remember to restart the game after changing options. If you need to change in the game, "
|
||||||
"use the I/O panel overlay window.",
|
"use the I/O panel overlay window.",
|
||||||
.type = OptionType::Enum,
|
.type = OptionType::Enum,
|
||||||
@@ -1224,7 +1271,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
{
|
{
|
||||||
.title = "Force Load LovePlus Module",
|
.title = "Force Load LovePlus Module",
|
||||||
.name = "loveplus",
|
.name = "loveplus",
|
||||||
.desc = "manually enable LovePlus module.",
|
.desc = "Manually enable LovePlus module.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "LovePlus",
|
.game_name = "LovePlus",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Advanced)",
|
||||||
@@ -1232,7 +1279,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
{
|
{
|
||||||
.title = "Force Load Charge Machine Module",
|
.title = "Force Load Charge Machine Module",
|
||||||
.name = "pcm",
|
.name = "pcm",
|
||||||
.desc = "manually enable Charge Machine module.",
|
.desc = "Manually enable Charge Machine module.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "Charge Machine",
|
.game_name = "Charge Machine",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Advanced)",
|
||||||
@@ -1240,7 +1287,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
{
|
{
|
||||||
.title = "Force Load Ongaku Paradise Module",
|
.title = "Force Load Ongaku Paradise Module",
|
||||||
.name = "onpara",
|
.name = "onpara",
|
||||||
.desc = "manually enable Ongaku Paradise module.",
|
.desc = "Manually enable Ongaku Paradise module.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "Ongaku Paradise",
|
.game_name = "Ongaku Paradise",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Advanced)",
|
||||||
@@ -1248,7 +1295,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
{
|
{
|
||||||
.title = "Force Load Busou Shinki Module",
|
.title = "Force Load Busou Shinki Module",
|
||||||
.name = "busou",
|
.name = "busou",
|
||||||
.desc = "manually enable Busou Shinki module.",
|
.desc = "Manually enable Busou Shinki module.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "Busou Shinki: Armored Princess Battle Conductor",
|
.game_name = "Busou Shinki: Armored Princess Battle Conductor",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Advanced)",
|
||||||
@@ -1257,7 +1304,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
// LoadCCJModule
|
// LoadCCJModule
|
||||||
.title = "Force Load Chase Chase Jokers Module",
|
.title = "Force Load Chase Chase Jokers Module",
|
||||||
.name = "ccj",
|
.name = "ccj",
|
||||||
.desc = "manually enable Chase Chase Jokers module.",
|
.desc = "Manually enable Chase Chase Jokers module.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "Chase Chase Jokers",
|
.game_name = "Chase Chase Jokers",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Advanced)",
|
||||||
@@ -1266,7 +1313,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
// LoadQKSModule
|
// LoadQKSModule
|
||||||
.title = "Force Load QuizKnock STADIUM Module",
|
.title = "Force Load QuizKnock STADIUM Module",
|
||||||
.name = "qks",
|
.name = "qks",
|
||||||
.desc = "manually enable QuizKnock STADIUM module",
|
.desc = "Manually enable QuizKnock STADIUM module.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "QuizKnock STADIUM",
|
.game_name = "QuizKnock STADIUM",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Advanced)",
|
||||||
@@ -1275,7 +1322,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
// LoadMFGModule
|
// LoadMFGModule
|
||||||
.title = "Force Load Mahjong Fight Girl Module",
|
.title = "Force Load Mahjong Fight Girl Module",
|
||||||
.name = "mfg",
|
.name = "mfg",
|
||||||
.desc = "manually enable Mahjong Fight Girl module.",
|
.desc = "Manually enable Mahjong Fight Girl module.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "Mahjong Fight Girl",
|
.game_name = "Mahjong Fight Girl",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Advanced)",
|
||||||
@@ -1284,7 +1331,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
// LoadPCModule
|
// LoadPCModule
|
||||||
.title = "Force Load Polaris Chord Module",
|
.title = "Force Load Polaris Chord Module",
|
||||||
.name = "pc",
|
.name = "pc",
|
||||||
.desc = "manually enable Polaris Chord module.",
|
.desc = "Manually enable Polaris Chord module.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "Polaris Chord",
|
.game_name = "Polaris Chord",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Advanced)",
|
||||||
@@ -1293,7 +1340,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
// LoadMusecaModule
|
// LoadMusecaModule
|
||||||
.title = "Force Load Museca Module",
|
.title = "Force Load Museca Module",
|
||||||
.name = "museca",
|
.name = "museca",
|
||||||
.desc = "manually enable Museca module.",
|
.desc = "Manually enable Museca module.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "Museca",
|
.game_name = "Museca",
|
||||||
.category = "Game Options (Advanced)",
|
.category = "Game Options (Advanced)",
|
||||||
@@ -1417,7 +1464,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
{
|
{
|
||||||
.title = "API Verbose Logging",
|
.title = "API Verbose Logging",
|
||||||
.name = "apilogging",
|
.name = "apilogging",
|
||||||
.desc = "verbose logging of API activity.",
|
.desc = "Verbose logging of API activity.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.category = "API (Dev)",
|
.category = "API (Dev)",
|
||||||
},
|
},
|
||||||
@@ -1540,8 +1587,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.title = "Force Touch Emulation",
|
.title = "Force Touch Emulation",
|
||||||
.name = "touchemuforce",
|
.name = "touchemuforce",
|
||||||
.desc = "Force enable hook for GetTouchInputInfo API and inject WM_TOUCH events. "
|
.desc = "Force enable hook for GetTouchInputInfo API and inject WM_TOUCH events. "
|
||||||
"This is automatically turned on when needed, so it is not typically required to "
|
"This is automatically enabled when needed and is typically not required. Do not enable this unless you have trouble.",
|
||||||
"turn it on manually. Do not enable this unless you have trouble.",
|
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.category = "Touch Parameters",
|
.category = "Touch Parameters",
|
||||||
},
|
},
|
||||||
@@ -1644,7 +1690,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
"legacy: Preserve buggy old behavior (Default)\n\n"
|
"legacy: Preserve buggy old behavior (Default)\n\n"
|
||||||
"fix: Add E00401 to non-FeliCa cards, scan FeliCa cards as-is (Recommended for most users)\n\n"
|
"fix: Add E00401 to non-FeliCa cards, scan FeliCa cards as-is (Recommended for most users)\n\n"
|
||||||
"all: Add E00401 to all cards, including FeliCa cards (For really old games only)\n\n"
|
"all: Add E00401 to all cards, including FeliCa cards (For really old games only)\n\n"
|
||||||
"The algorithm is simple; the prefix is applied, the scanned card number is appended upto 8 bytes, and remaining digits are discarded. "
|
"The algorithm is simple; the prefix is applied, the scanned card number is appended up to 8 bytes, and remaining digits are discarded. "
|
||||||
"For example, abcd56780123 is converted to e00401abcd567801 on card in. This results in different card numbers so "
|
"For example, abcd56780123 is converted to e00401abcd567801 on card in. This results in different card numbers so "
|
||||||
"be careful when connecting to servers!",
|
"be careful when connecting to servers!",
|
||||||
.type = OptionType::Enum,
|
.type = OptionType::Enum,
|
||||||
@@ -1675,7 +1721,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
{
|
{
|
||||||
.title = "Realtime Process Priority (DEPRECATED - use -processpriority instead)",
|
.title = "Realtime Process Priority (DEPRECATED - use -processpriority instead)",
|
||||||
.name = "realtime",
|
.name = "realtime",
|
||||||
.desc = "Sets the process priority to realtime, can help with odd lag spikes.",
|
.desc = "Sets the process priority to realtime; can help with odd lag spikes.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.hidden = true,
|
.hidden = true,
|
||||||
.category = "Performance",
|
.category = "Performance",
|
||||||
@@ -1686,7 +1732,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.name = "sp2x-processpriority",
|
.name = "sp2x-processpriority",
|
||||||
.display_name = "processpriority",
|
.display_name = "processpriority",
|
||||||
.aliases= "processpriority",
|
.aliases= "processpriority",
|
||||||
.desc = "Sets the process priority, can help with odd lag spikes. Default: high.",
|
.desc = "Sets the process priority, which can help with odd lag spikes. Default: high.",
|
||||||
.type = OptionType::Enum,
|
.type = OptionType::Enum,
|
||||||
.category = "Performance",
|
.category = "Performance",
|
||||||
.elements = {
|
.elements = {
|
||||||
@@ -1763,7 +1809,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.desc = "Disables all audio hooks, including device initialization and volume hooks.\n\n"
|
.desc = "Disables all audio hooks, including device initialization and volume hooks.\n\n"
|
||||||
"Default: off (allow Spice to hook IMMDeviceEnumerator for and optionally let "
|
"Default: off (allow Spice to hook IMMDeviceEnumerator for and optionally let "
|
||||||
"you use a different backend instead of exclusive WASAPI).\n\n"
|
"you use a different backend instead of exclusive WASAPI).\n\n"
|
||||||
"Check this if you want games to natively access your audio device.",
|
"Check this to allow games to natively access your audio device.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.category = "Audio (Hacks)",
|
.category = "Audio (Hacks)",
|
||||||
},
|
},
|
||||||
@@ -1775,7 +1821,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.aliases= "volumehookdisable",
|
.aliases= "volumehookdisable",
|
||||||
.desc = "Disables audio volume hook.\n\n"
|
.desc = "Disables audio volume hook.\n\n"
|
||||||
"Default: off (prevent games from changing audio volume by hooking IAudioEndpointVolume).\n\n"
|
"Default: off (prevent games from changing audio volume by hooking IAudioEndpointVolume).\n\n"
|
||||||
"Check this if you want games to freely change your volume.",
|
"Check this to allow games to freely change your volume.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.category = "Audio (Hacks)",
|
.category = "Audio (Hacks)",
|
||||||
},
|
},
|
||||||
@@ -1876,6 +1922,15 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
{"3", "Landscape, Flipped"}
|
{"3", "Landscape, Flipped"}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// ChangeResolution
|
||||||
|
.title = "Change Monitor Resolution",
|
||||||
|
.name = "changeres",
|
||||||
|
.desc = "Changes monitor resolution before booting the game.",
|
||||||
|
.type = OptionType::Text,
|
||||||
|
.setting_name = "1280,720",
|
||||||
|
.category = "Monitor"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.title = "AVS Log Level",
|
.title = "AVS Log Level",
|
||||||
.name = "loglevel",
|
.name = "loglevel",
|
||||||
@@ -1908,7 +1963,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
{
|
{
|
||||||
.title = "Blocking Logger",
|
.title = "Blocking Logger",
|
||||||
.name = "logblock",
|
.name = "logblock",
|
||||||
.desc = "Slower but safer logging used for debugging.",
|
.desc = "Slower but safer logging for debugging.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.category = "Debug Log",
|
.category = "Debug Log",
|
||||||
},
|
},
|
||||||
@@ -2187,6 +2242,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",
|
||||||
@@ -2227,19 +2290,35 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// spice2x_JubeatLegacyTouch
|
// spice2x_JubeatLegacyTouch
|
||||||
.title = "JB Legacy Touch Targets",
|
.title = "JB Legacy Touch Targets (Deprecated - use -jubeattouchalgo instead)",
|
||||||
.name = "sp2x-jubeatlegacytouch",
|
.name = "sp2x-jubeatlegacytouch",
|
||||||
.display_name = "jubeatlegacytouch",
|
.display_name = "jubeatlegacytouch",
|
||||||
.aliases= "jubeatlegacytouch",
|
.aliases= "jubeatlegacytouch",
|
||||||
.desc = "For touch screen players - use the legacy & less accurate grid-based layout for touch recognition, "
|
.desc = "Deprecated - use -jubeattouchalgo instead.",
|
||||||
"instead of the new & more accurate touch targets. Default: off.",
|
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
|
.hidden = true,
|
||||||
.game_name = "Jubeat",
|
.game_name = "Jubeat",
|
||||||
.category = "Game Options",
|
.category = "Game Options",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.title = "JB Touch Algorithm",
|
||||||
|
.name = "jubeattouchalgo",
|
||||||
|
.desc = "For touch screen players: choose the touch algorithm to use.\n\n"
|
||||||
|
"legacy - evenly divide the grid into 16 squares; old spicetools behavior, slightly inaccurate in gaps\n\n"
|
||||||
|
"improved (default) - squares register as-is, gaps will trigger the closest square\n\n"
|
||||||
|
"accurate - only touches within squares will trigger; gaps do nothing (for AC size touch screens)",
|
||||||
|
.type = OptionType::Enum,
|
||||||
|
.game_name = "Jubeat",
|
||||||
|
.category = "Game Options",
|
||||||
|
.elements = {
|
||||||
|
{"legacy", ""},
|
||||||
|
{"improved", ""},
|
||||||
|
{"accurate", ""},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// spice2x_RBTouchScale
|
// spice2x_RBTouchScale
|
||||||
.title = "RB Scale Touch Input",
|
.title = "RB Touch Emulation Scale",
|
||||||
.name = "sp2x-rbscaletouch",
|
.name = "sp2x-rbscaletouch",
|
||||||
.display_name = "rbscaletouch",
|
.display_name = "rbscaletouch",
|
||||||
.aliases= "rbscaletouch",
|
.aliases= "rbscaletouch",
|
||||||
@@ -2249,6 +2328,31 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.game_name = "Reflec Beat",
|
.game_name = "Reflec Beat",
|
||||||
.category = "Game Options",
|
.category = "Game Options",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// RBTouchSize
|
||||||
|
.title = "RB Touch Emulation Size",
|
||||||
|
.name = "rbtouchsize",
|
||||||
|
.desc = "Size of the touch area; how many IR sensors a single finger activates. Default: 1 (1x1).",
|
||||||
|
.type = OptionType::Enum,
|
||||||
|
.game_name = "Reflec Beat",
|
||||||
|
.category = "Game Options",
|
||||||
|
.elements = {
|
||||||
|
{"1", "1x1"},
|
||||||
|
{"3", "3x3"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// RBTouchPollRate
|
||||||
|
.title = "RB Touch Emulation Poll Hz",
|
||||||
|
.name = "rbtouchhz",
|
||||||
|
.desc = "By default, the game polls for touch at 120Hz. "
|
||||||
|
"This option overrides that rate; enter a number betwen 1 and 1000.\n\n"
|
||||||
|
"It should be noted that higher poll does not necessarily improve accuracy or performance.",
|
||||||
|
.type = OptionType::Integer,
|
||||||
|
.setting_name = "250",
|
||||||
|
.game_name = "Reflec Beat",
|
||||||
|
.category = "Game Options",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// spice2x_AsioForceUnload
|
// spice2x_AsioForceUnload
|
||||||
.title = "ASIO Force Unload On Stop",
|
.title = "ASIO Force Unload On Stop",
|
||||||
@@ -2316,7 +2420,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 +2447,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",
|
||||||
@@ -2375,6 +2490,28 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
{"both", ""},
|
{"both", ""},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// AutoPinMacroTrigger0
|
||||||
|
.title = "Auto PIN Macro Trigger Log String (P1)",
|
||||||
|
.name = "autopinmacrotrigger0",
|
||||||
|
.desc =
|
||||||
|
"Substring matched against game log output to detect when Player 1's PIN entry "
|
||||||
|
"screen is ready. When the substring appears in any log line, the PIN macro is "
|
||||||
|
"typed for Player 1 only. Leave blank to disable auto-trigger for P1.",
|
||||||
|
.type = OptionType::Text,
|
||||||
|
.category = "Network (Advanced)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// AutoPinMacroTrigger1
|
||||||
|
.title = "Auto PIN Macro Trigger Log String (P2)",
|
||||||
|
.name = "autopinmacrotrigger1",
|
||||||
|
.desc =
|
||||||
|
"Substring matched against game log output to detect when Player 2's PIN entry "
|
||||||
|
"screen is ready. When the substring appears in any log line, the PIN macro is "
|
||||||
|
"typed for Player 2 only. Leave blank to disable auto-trigger for P2.",
|
||||||
|
.type = OptionType::Text,
|
||||||
|
.category = "Network (Advanced)",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// spice2x_LowLatencySharedAudio
|
// spice2x_LowLatencySharedAudio
|
||||||
.title = "Low Latency Shared Audio",
|
.title = "Low Latency Shared Audio",
|
||||||
@@ -2414,7 +2551,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.aliases= "nonvapi",
|
.aliases= "nonvapi",
|
||||||
.desc = "Completely block the game from accessing NVAPI. Can be used if NVAPI is returning undesirable "
|
.desc = "Completely block the game from accessing NVAPI. Can be used if NVAPI is returning undesirable "
|
||||||
"results to the game (e.g., wrong values from NvDisplayConfig for SDVX). You may need to apply additional "
|
"results to the game (e.g., wrong values from NvDisplayConfig for SDVX). You may need to apply additional "
|
||||||
"hex edits in order to boot the game correctly.",
|
"hex edits to boot the game correctly.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.category = "Graphics (Common)",
|
.category = "Graphics (Common)",
|
||||||
},
|
},
|
||||||
@@ -2462,7 +2599,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.name = "sp2x-smx-stage",
|
.name = "sp2x-smx-stage",
|
||||||
.display_name = "smxstage",
|
.display_name = "smxstage",
|
||||||
.aliases= "smxstage",
|
.aliases= "smxstage",
|
||||||
.desc = "StepmaniaX stage will show up as a device that can recieve lighting output. "
|
.desc = "StepmaniaX stage will show up as a device that can receive lighting output. "
|
||||||
"For configurator, restart spicecfg.exe after enabling this to have the device show up for binding.",
|
"For configurator, restart spicecfg.exe after enabling this to have the device show up for binding.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.category = "I/O Options",
|
.category = "I/O Options",
|
||||||
@@ -2473,7 +2610,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.name = "sp2x-smx-dedicab",
|
.name = "sp2x-smx-dedicab",
|
||||||
.display_name = "smxdedicab",
|
.display_name = "smxdedicab",
|
||||||
.aliases = "smxdedicab",
|
.aliases = "smxdedicab",
|
||||||
.desc = "StepManiaX Dedicated Cabinet will show up as a device that can recieve lighting output. "
|
.desc = "StepManiaX Dedicated Cabinet will show up as a device that can receive lighting output. "
|
||||||
"For configurator, restart spicecfg.exe after enabling this to have the device show up for binding.",
|
"For configurator, restart spicecfg.exe after enabling this to have the device show up for binding.",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.category = "I/O Options"
|
.category = "I/O Options"
|
||||||
@@ -2577,7 +2714,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
"Enables emulation of touch gestures to make it easier to navigate the game without a touchscreen.\n\n"
|
"Enables emulation of touch gestures to make it easier to navigate the game without a touchscreen.\n\n"
|
||||||
"This enables two things:\n"
|
"This enables two things:\n"
|
||||||
" 1. Swipe/Touch bindings in Buttons tab (Swipe Next Page, etc)\n"
|
" 1. Swipe/Touch bindings in Buttons tab (Swipe Next Page, etc)\n"
|
||||||
" 2. PIN pad bindings (make sure Unscramble Keypad patch is applied)).",
|
" 2. PIN pad bindings (make sure Unscramble Keypad patch is applied).",
|
||||||
.type = OptionType::Bool,
|
.type = OptionType::Bool,
|
||||||
.game_name = "Nostalgia",
|
.game_name = "Nostalgia",
|
||||||
.category = "Game Options",
|
.category = "Game Options",
|
||||||
@@ -2639,7 +2776,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 +2785,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 +2794,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 +2802,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 +2812,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 +2821,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,8 +2842,43 @@ 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)",
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
// DisableHighResTimer
|
||||||
|
.title = "Use Legacy Timers",
|
||||||
|
.name = "notimerhacks",
|
||||||
|
.desc = "Disables high resolution timers, reverting to legacy behavior. "
|
||||||
|
"Recommended that you leave this OFF for optimal input latency, "
|
||||||
|
"unless you're on a resource-constrained system (e.g., old cabinet PC)",
|
||||||
|
.type = OptionType::Bool,
|
||||||
|
.category = "Development"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// EnableICMPHook
|
||||||
|
.title = "Enable ICMP Emulation",
|
||||||
|
.name = "icmphook",
|
||||||
|
.desc = "Emulate keepalive ping replies in user mode, so the game does not need to "
|
||||||
|
"open privileged raw ICMP sockets.",
|
||||||
|
.type = OptionType::Bool,
|
||||||
|
.category = "Network (Development)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// AutoElevate
|
||||||
|
.title = "Run as",
|
||||||
|
.name = "runas",
|
||||||
|
.desc = "Controls whether spice will automatically re-launch with administrator privileges at startup.\n\n"
|
||||||
|
"admin (default): automatically re-launch with administrator privileges, "
|
||||||
|
"needed for most games to function properly\n\n"
|
||||||
|
"user: skip elevation, run with current user privileges; game may fail to launch, "
|
||||||
|
"crash, or critical features may silently fail; use at your own risk.",
|
||||||
|
.type = OptionType::Enum,
|
||||||
|
.category = "Development",
|
||||||
|
.elements = {
|
||||||
|
{"admin", ""},
|
||||||
|
{"user", ""},
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::vector<std::string> &launcher::get_categories(Options::OptionsCategory category) {
|
const std::vector<std::string> &launcher::get_categories(Options::OptionsCategory category) {
|
||||||
|
|||||||
@@ -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,
|
||||||
@@ -195,6 +199,7 @@ namespace launcher {
|
|||||||
LoadStubs,
|
LoadStubs,
|
||||||
AdjustOrientation,
|
AdjustOrientation,
|
||||||
spice2x_AutoOrientation,
|
spice2x_AutoOrientation,
|
||||||
|
ChangeResolution,
|
||||||
LogLevel,
|
LogLevel,
|
||||||
EAAutomap,
|
EAAutomap,
|
||||||
EANetdump,
|
EANetdump,
|
||||||
@@ -229,12 +234,16 @@ namespace launcher {
|
|||||||
spice2x_WindowPosition,
|
spice2x_WindowPosition,
|
||||||
spice2x_WindowAlwaysOnTop,
|
spice2x_WindowAlwaysOnTop,
|
||||||
WindowForceScaling,
|
WindowForceScaling,
|
||||||
|
WindowDisableRoundedCorners,
|
||||||
spice2x_IIDXWindowedSubscreenSize,
|
spice2x_IIDXWindowedSubscreenSize,
|
||||||
spice2x_IIDXWindowedSubscreenPosition,
|
spice2x_IIDXWindowedSubscreenPosition,
|
||||||
IIDXWindowedSubscreenBorderless,
|
IIDXWindowedSubscreenBorderless,
|
||||||
IIDXWindowedSubscreenAlwaysOnTop,
|
IIDXWindowedSubscreenAlwaysOnTop,
|
||||||
spice2x_JubeatLegacyTouch,
|
spice2x_JubeatLegacyTouch,
|
||||||
|
JubeatTouchAlgo,
|
||||||
spice2x_RBTouchScale,
|
spice2x_RBTouchScale,
|
||||||
|
RBTouchSize,
|
||||||
|
RBTouchPollRate,
|
||||||
spice2x_AsioForceUnload,
|
spice2x_AsioForceUnload,
|
||||||
spice2x_IIDXNoESpec,
|
spice2x_IIDXNoESpec,
|
||||||
spice2x_IIDXWindowedTDJ,
|
spice2x_IIDXWindowedTDJ,
|
||||||
@@ -243,8 +252,11 @@ namespace launcher {
|
|||||||
DRSRGBCameraHook,
|
DRSRGBCameraHook,
|
||||||
spice2x_IIDXNativeTouch,
|
spice2x_IIDXNativeTouch,
|
||||||
spice2x_IIDXNoSub,
|
spice2x_IIDXNoSub,
|
||||||
|
IIDXSubMonitorOverride,
|
||||||
spice2x_IIDXEmulateSubscreenKeypadTouch,
|
spice2x_IIDXEmulateSubscreenKeypadTouch,
|
||||||
spice2x_AutoCard,
|
spice2x_AutoCard,
|
||||||
|
AutoPinMacroTrigger0,
|
||||||
|
AutoPinMacroTrigger1,
|
||||||
spice2x_LowLatencySharedAudio,
|
spice2x_LowLatencySharedAudio,
|
||||||
spice2x_TapeLedAlgorithm,
|
spice2x_TapeLedAlgorithm,
|
||||||
spice2x_NoNVAPI,
|
spice2x_NoNVAPI,
|
||||||
@@ -273,6 +285,9 @@ namespace launcher {
|
|||||||
LovePlusPrinterJPGQuality,
|
LovePlusPrinterJPGQuality,
|
||||||
OptionConflictResolution,
|
OptionConflictResolution,
|
||||||
OtocaCamHook,
|
OtocaCamHook,
|
||||||
|
DisableHighResTimer,
|
||||||
|
EnableICMPHook,
|
||||||
|
AutoElevate,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class OptionsCategory {
|
enum class OptionsCategory {
|
||||||
|
|||||||
@@ -7,12 +7,14 @@
|
|||||||
#include "easrv/easrv.h"
|
#include "easrv/easrv.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
#include "hooks/audio/audio.h"
|
#include "hooks/audio/audio.h"
|
||||||
|
#include "hooks/graphics/graphics.h"
|
||||||
#include "util/deferlog.h"
|
#include "util/deferlog.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
|
|
||||||
#include "launcher.h"
|
#include "launcher.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "nvapi/nvapi.h"
|
#include "nvapi/nvapi.h"
|
||||||
|
#include "sdk/sdk.h"
|
||||||
|
|
||||||
namespace launcher {
|
namespace launcher {
|
||||||
|
|
||||||
@@ -22,6 +24,11 @@ namespace launcher {
|
|||||||
// therefore, subsystems need to be guarded against multiple unload attempts
|
// therefore, subsystems need to be guarded against multiple unload attempts
|
||||||
log_info("launcher", "stopping subsystems");
|
log_info("launcher", "stopping subsystems");
|
||||||
|
|
||||||
|
sdk::fini_sdk_modules();
|
||||||
|
|
||||||
|
// reset monitor settings
|
||||||
|
reset_monitor_on_exit();
|
||||||
|
|
||||||
// before shutting down logger, dump any deferred log messages
|
// before shutting down logger, dump any deferred log messages
|
||||||
deferredlogs::dump_to_logger();
|
deferredlogs::dump_to_logger();
|
||||||
|
|
||||||
@@ -86,13 +93,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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,9 @@ namespace superexit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool async_key_exit = GetAsyncKeyState(VK_MENU) && GetAsyncKeyState(VK_F4);
|
bool async_key_exit =
|
||||||
|
(GetAsyncKeyState(VK_MENU) & 0x8000) != 0 &&
|
||||||
|
(GetAsyncKeyState(VK_F4) & 0x8000) != 0;
|
||||||
|
|
||||||
bool overlay_exit = false;
|
bool overlay_exit = false;
|
||||||
auto buttons = games::get_buttons_overlay(eamuse_get_game());
|
auto buttons = games::get_buttons_overlay(eamuse_get_game());
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user