mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
Compare commits
101 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e86cb16a2 | |||
| 76f401de95 | |||
| 5947779502 | |||
| 61c97ccaab | |||
| 12d6a88c60 | |||
| 984b41a1ae | |||
| d06b8bd731 | |||
| 0dad96b876 | |||
| def296095b | |||
| 750b1fea80 | |||
| 4c87078807 | |||
| 052698afa3 | |||
| 9e2b04d5db | |||
| 5641e06a19 | |||
| 88ec6dbcee | |||
| 1b614c94a7 | |||
| 37c5913f69 | |||
| 2eca668593 | |||
| aec1da9d16 | |||
| 548b441f1f | |||
| 98bc285d98 | |||
| 553c180bee | |||
| 537252440e | |||
| 13a0877d38 | |||
| 50691b79f1 | |||
| ad229dabdb | |||
| bdff33a420 | |||
| 4b21821aa4 | |||
| 6b47b5db50 | |||
| a10ca5236b | |||
| be2dab9a1a | |||
| 02711cdee1 | |||
| c6b10402f4 | |||
| 58ea6ce17e | |||
| 8cc00c371a | |||
| 8e2985d8d0 | |||
| 8d3b15641f | |||
| 033e9f926a | |||
| da58503b47 | |||
| cffee2b7f8 | |||
| 32801319fa | |||
| afbe013811 | |||
| 862e4717f6 | |||
| d3fa27ae4a | |||
| b487cf2bcf | |||
| 0fc3aa5818 | |||
| 276874f882 | |||
| 1fdd270480 | |||
| 07cb6048a0 | |||
| 61d79f76b2 | |||
| 7335d07e30 | |||
| 9f99b71112 | |||
| 5c3921a99c | |||
| 9fc397b8bc | |||
| ddc8061268 | |||
| c0698a3872 | |||
| 1ed3521091 | |||
| 0dda920448 | |||
| 5f7580b69e | |||
| 3ef1b3ecaa | |||
| 931811da14 | |||
| 7624ded512 | |||
| a5252939dd | |||
| 808ac0c557 | |||
| 4e138b244e | |||
| 3fcd1bdbc9 | |||
| f857e63d5d | |||
| b462c838f6 | |||
| 4d658bf273 | |||
| 67721c416b | |||
| 0b5f7787be | |||
| a554c8f923 | |||
| 6592191624 | |||
| 164592b16d | |||
| 81f879e7ff | |||
| 1af8b3de61 | |||
| c33fef606f | |||
| a9fc6ef1b2 | |||
| d530bfcd65 | |||
| c96f4330d2 | |||
| 26a2a0f481 | |||
| 11b9790e6c | |||
| 1dfcf8c087 | |||
| 5c7ad0d51e | |||
| 587ae86341 | |||
| 926f2ef2a0 | |||
| d262e02f8c | |||
| f6b63473a0 | |||
| 1ad45edd6e | |||
| e79de3b117 | |||
| f783a5a1cd | |||
| 7848d5c237 | |||
| cec735a81b | |||
| 753702e32e | |||
| 7bd7503951 | |||
| d0c38956bf | |||
| 77bca2baee | |||
| 46e8c8a5a5 | |||
| 4d58e5d080 | |||
| 168084d672 | |||
| 740ed90d95 |
@@ -36,6 +36,7 @@ please do not ask for these, as it will never happen here.
|
||||
|
||||
Rules for filing a new issue or adding comments to existing issues in the tracker:
|
||||
|
||||
* Low effort submissions will be simply deleted, and repeated attempts will get you banned.
|
||||
* Check the [known issues](https://github.com/spice2x/spice2x.github.io/wiki/Known-issues) page first before reporting a new issue. If you have some new information or workarounds for a known issue, you can file a documentation bug as well.
|
||||
* Use the search function and see if there is an existing issue.
|
||||
* This is not the place to obtain a guide or receive basic troubleshooting.
|
||||
|
||||
@@ -18,4 +18,9 @@ dist/*
|
||||
|
||||
external/cv2pdb/*
|
||||
|
||||
.ccache/*
|
||||
.ccache/*
|
||||
|
||||
# Visual Studio
|
||||
.vs
|
||||
out
|
||||
CMakeSettings.json
|
||||
|
||||
@@ -80,6 +80,25 @@ if(MSVC)
|
||||
|
||||
# enable COMDAT folding for even smaller release builds
|
||||
add_link_options("$<$<CONFIG:RELEASE,MINSIZEREL>:/OPT:ICF>")
|
||||
|
||||
# always use UTF-8 (fix 4819)
|
||||
add_compile_options("/utf-8")
|
||||
|
||||
# spectre mitigation warning
|
||||
add_compile_options("/wd5045")
|
||||
|
||||
# implicit type convert warnings
|
||||
add_compile_options("/wd4244")
|
||||
add_compile_options("/wd4267")
|
||||
add_compile_options("/wd4305")
|
||||
|
||||
# unreferenced local variable
|
||||
add_compile_options("/wd4101")
|
||||
|
||||
# warning in winbase.h??
|
||||
add_compile_options("/wd5039")
|
||||
|
||||
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
# disable warnings about using non _s variants like strncpy
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||
@@ -338,12 +357,12 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
external/cardio/cardio_runner.cpp
|
||||
|
||||
# external misc
|
||||
external/scard/scard.cpp
|
||||
external/stackwalker/stackwalker.cpp
|
||||
external/tinyxml2/tinyxml2.cpp
|
||||
external/http-parser/http_parser.c
|
||||
external/usbhidusage/usb-hid-usage.c
|
||||
external/toojpeg/toojpeg.cpp
|
||||
external/scard/scard.cpp
|
||||
|
||||
# games
|
||||
games/game.cpp
|
||||
@@ -443,6 +462,9 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
games/mfg/mfg.cpp
|
||||
games/mfg/io.cpp
|
||||
games/mfg/bi2a_hook.cpp
|
||||
games/pc/pc.cpp
|
||||
games/pc/io.cpp
|
||||
games/pc/bi2x_hook.cpp
|
||||
|
||||
# hooks
|
||||
hooks/audio/audio.cpp
|
||||
@@ -588,6 +610,8 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
util/lz77.cpp
|
||||
util/tapeled.cpp
|
||||
util/execexe.cpp
|
||||
util/dependencies.cpp
|
||||
util/deferlog.cpp
|
||||
)
|
||||
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "Source Files" FILES ${SOURCE_FILES})
|
||||
@@ -598,8 +622,9 @@ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "Source Files" FILES ${SOUR
|
||||
set(RESOURCE_FILES build/manifest.manifest build/manifest.rc build/icon.rc cfg/Win32D.rc)
|
||||
add_executable(spicetools_spice ${SOURCE_FILES} ${RESOURCE_FILES})
|
||||
target_link_libraries(spicetools_spice
|
||||
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard winhttp
|
||||
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp
|
||||
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
|
||||
target_link_libraries(spicetools_spice PUBLIC winscard)
|
||||
set_target_properties(spicetools_spice PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_spice PROPERTIES OUTPUT_NAME "spice")
|
||||
|
||||
@@ -613,8 +638,9 @@ endif()
|
||||
set(RESOURCE_FILES build/manifest.manifest build/manifest.rc build/icon.rc cfg/Win32D.rc)
|
||||
add_executable(spicetools_spice_laa ${SOURCE_FILES} ${RESOURCE_FILES})
|
||||
target_link_libraries(spicetools_spice_laa
|
||||
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard winhttp
|
||||
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp
|
||||
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
|
||||
target_link_libraries(spicetools_spice_laa PUBLIC winscard)
|
||||
set_target_properties(spicetools_spice_laa PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_spice_laa PROPERTIES OUTPUT_NAME "spice_laa")
|
||||
target_compile_definitions(spicetools_spice_laa PRIVATE SPICE32_LARGE_ADDRESS_AWARE=1)
|
||||
@@ -623,6 +649,22 @@ IF(NOT MSVC)
|
||||
set_target_properties(spicetools_spice_laa PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32 -Wl,--large-address-aware")
|
||||
endif()
|
||||
|
||||
# spice_linux.exe
|
||||
###########
|
||||
|
||||
set(RESOURCE_FILES build/manifest.manifest build/manifest.rc build/icon.rc cfg/Win32D.rc)
|
||||
add_executable(spicetools_spice_linux ${SOURCE_FILES} ${RESOURCE_FILES})
|
||||
target_link_libraries(spicetools_spice_linux
|
||||
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp
|
||||
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
|
||||
set_target_properties(spicetools_spice_linux PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_spice_linux PROPERTIES OUTPUT_NAME "spice_linux")
|
||||
target_compile_definitions(spicetools_spice_linux PRIVATE NO_SCARD=1 PRIVATE SPICE_LINUX=1)
|
||||
|
||||
IF(NOT MSVC)
|
||||
set_target_properties(spicetools_spice_linux PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
||||
endif()
|
||||
|
||||
# spice64.exe
|
||||
#############
|
||||
|
||||
@@ -631,9 +673,9 @@ add_executable(spicetools_spice64 ${SOURCE_FILES} ${RESOURCE_FILES})
|
||||
|
||||
# do NOT link against: mf, mfplat, mfreadwrite; otherwise unity games will break
|
||||
target_link_libraries(spicetools_spice64
|
||||
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard winhttp mfuuid strmiids dxva2
|
||||
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp mfuuid strmiids dxva2
|
||||
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
|
||||
|
||||
target_link_libraries(spicetools_spice64 PUBLIC winscard)
|
||||
set_target_properties(spicetools_spice64 PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_spice64 PROPERTIES OUTPUT_NAME "spice64")
|
||||
target_compile_definitions(spicetools_spice64 PRIVATE SPICE64=1)
|
||||
@@ -642,6 +684,25 @@ IF(NOT MSVC)
|
||||
set_target_properties(spicetools_spice64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
|
||||
endif()
|
||||
|
||||
# spice64_linux.exe
|
||||
#############
|
||||
|
||||
set(RESOURCE_FILES build/manifest.manifest build/manifest64.rc build/icon.rc cfg/Win32D.rc)
|
||||
add_executable(spicetools_spice64_linux ${SOURCE_FILES} ${RESOURCE_FILES})
|
||||
|
||||
# do NOT link against: mf, mfplat, mfreadwrite; otherwise unity games will break
|
||||
target_link_libraries(spicetools_spice64_linux
|
||||
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp mfuuid strmiids dxva2
|
||||
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
|
||||
set_target_properties(spicetools_spice64_linux PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_spice64_linux PROPERTIES OUTPUT_NAME "spice64_linux")
|
||||
target_compile_definitions(spicetools_spice64_linux PRIVATE SPICE64=1)
|
||||
target_compile_definitions(spicetools_spice64_linux PRIVATE NO_SCARD=1 PRIVATE SPICE_LINUX=1)
|
||||
|
||||
IF(NOT MSVC)
|
||||
set_target_properties(spicetools_spice64_linux PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
|
||||
endif()
|
||||
|
||||
# spicecfg.exe
|
||||
##############
|
||||
|
||||
@@ -649,8 +710,9 @@ set(SOURCE_FILES ${SOURCE_FILES} launcher/options.h launcher/options.cpp)
|
||||
set(RESOURCE_FILES cfg/manifest.manifest cfg/manifest.rc cfg/icon.rc cfg/Win32D.rc)
|
||||
add_executable(spicetools_cfg WIN32 ${SOURCE_FILES} ${RESOURCE_FILES})
|
||||
target_link_libraries(spicetools_cfg
|
||||
PUBLIC ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard winhttp strmiids
|
||||
PUBLIC ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp strmiids
|
||||
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
|
||||
target_link_libraries(spicetools_cfg PUBLIC winscard)
|
||||
set_target_properties(spicetools_cfg PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_cfg PROPERTIES OUTPUT_NAME "spicecfg")
|
||||
target_compile_definitions(spicetools_cfg PRIVATE SPICETOOLS_SPICECFG_STANDALONE=1)
|
||||
@@ -659,6 +721,24 @@ if(NOT MSVC)
|
||||
set_target_properties(spicetools_cfg PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
||||
endif()
|
||||
|
||||
# spicecfg_linux.exe
|
||||
##############
|
||||
|
||||
set(SOURCE_FILES ${SOURCE_FILES} launcher/options.h launcher/options.cpp)
|
||||
set(RESOURCE_FILES cfg/manifest.manifest cfg/manifest.rc cfg/icon.rc cfg/Win32D.rc)
|
||||
add_executable(spicetools_cfg_linux WIN32 ${SOURCE_FILES} ${RESOURCE_FILES})
|
||||
target_link_libraries(spicetools_cfg_linux
|
||||
PUBLIC ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winhttp strmiids
|
||||
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
|
||||
set_target_properties(spicetools_cfg_linux PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_cfg_linux PROPERTIES OUTPUT_NAME "spicecfg_linux")
|
||||
target_compile_definitions(spicetools_cfg_linux PRIVATE SPICETOOLS_SPICECFG_STANDALONE=1)
|
||||
target_compile_definitions(spicetools_cfg_linux PRIVATE NO_SCARD=1 PRIVATE SPICE_LINUX=1)
|
||||
|
||||
if(NOT MSVC)
|
||||
set_target_properties(spicetools_cfg_linux PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
||||
endif()
|
||||
|
||||
# stubs
|
||||
#######
|
||||
|
||||
@@ -752,19 +832,19 @@ endif()
|
||||
####################
|
||||
|
||||
# output config
|
||||
set_target_properties(spicetools_cfg
|
||||
set_target_properties(spicetools_cfg spicetools_cfg_linux
|
||||
PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools")
|
||||
|
||||
# output 32bit
|
||||
set_target_properties(spicetools_spice spicetools_spice_laa 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
|
||||
PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive32"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32")
|
||||
|
||||
# output 64bit
|
||||
set_target_properties(spicetools_spice64 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
|
||||
PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive64"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/64"
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
namespace acio {
|
||||
HINSTANCE DLL_INSTANCE = nullptr;
|
||||
std::vector<acio::ACIOModule *> MODULES;
|
||||
std::atomic<bool> IO_INIT_IN_PROGRESS = false;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -58,6 +59,7 @@ static inline acio::HookMode get_hookmode() {
|
||||
|
||||
void acio::attach() {
|
||||
log_info("acio", "SpiceTools ACIO");
|
||||
IO_INIT_IN_PROGRESS = true;
|
||||
|
||||
// load settings and instance
|
||||
acio::DLL_INSTANCE = LoadLibraryA("libacio.dll");
|
||||
@@ -111,6 +113,8 @@ void acio::attach() {
|
||||
for (auto &module : MODULES) {
|
||||
module->attach();
|
||||
}
|
||||
|
||||
IO_INIT_IN_PROGRESS = false;
|
||||
}
|
||||
|
||||
void acio::attach_icca() {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
|
||||
#include <windows.h>
|
||||
@@ -11,6 +12,7 @@ namespace acio {
|
||||
// globals
|
||||
extern HINSTANCE DLL_INSTANCE;
|
||||
extern std::vector<acio::ACIOModule *> MODULES;
|
||||
extern std::atomic<bool> IO_INIT_IN_PROGRESS;
|
||||
|
||||
void attach();
|
||||
void attach_icca();
|
||||
|
||||
@@ -102,7 +102,7 @@ bool ICCADevice::parse_msg(MessageData *msg_in,
|
||||
// send version data
|
||||
auto msg = this->create_msg(msg_in, MSG_VERSION_SIZE);
|
||||
if (
|
||||
avs::game::is_model({"LDJ", "TBS", "UJK"}) ||
|
||||
avs::game::is_model({"LDJ", "TBS", "UJK", "XIF"}) ||
|
||||
// SDVX Valkyrie cabinet mode
|
||||
(avs::game::is_model("KFC") && (avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H'))
|
||||
) {
|
||||
@@ -373,7 +373,7 @@ void ICCADevice::update_card(int unit) {
|
||||
bool kb_insert_press = false;
|
||||
|
||||
// eamio keypress
|
||||
kb_insert_press |= eamuse_get_keypad_state((size_t) unit) & (1 << EAM_IO_INSERT);
|
||||
kb_insert_press |= static_cast<bool>(eamuse_get_keypad_state((size_t) unit) & (1 << EAM_IO_INSERT));
|
||||
|
||||
// check for card
|
||||
if (this->cards[unit] == nullptr && (eamuse_card_insert_consume(this->node_count, unit) || kb_insert_press)) {
|
||||
@@ -521,7 +521,7 @@ void ICCADevice::update_status(int unit) {
|
||||
buffer[10] = felica ? 0x01 : 0x00;
|
||||
|
||||
} else if (
|
||||
avs::game::is_model({"LDJ", "TBS"}) ||
|
||||
avs::game::is_model({"LDJ", "TBS", "XIF"}) ||
|
||||
// SDVX Valkyrie cabinet mode
|
||||
(avs::game::is_model("KFC") && (avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H'))) {
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include "external/robin_hood.h"
|
||||
#include "launcher/logger.h"
|
||||
#include "launcher/signal.h"
|
||||
#include "util/deferlog.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/libutils.h"
|
||||
@@ -1501,7 +1503,8 @@ namespace avs {
|
||||
" * Do NOT copy over random DLLs from another game installation; DLL must match game version\n"
|
||||
"\n"
|
||||
, DLL_NAME, MODULE_PATH.string()) };
|
||||
log_fatal("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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1736,7 +1739,17 @@ namespace avs {
|
||||
return;
|
||||
}
|
||||
|
||||
static void create_dir(
|
||||
static void avs_dir_err(const std::filesystem::path &src_path)
|
||||
{
|
||||
deferredlogs::defer_error_messages({
|
||||
"AVS filesystem initialization failure was previously detected during boot!",
|
||||
fmt::format(" ERROR: directory could not be created: {}", src_path.string().c_str()),
|
||||
fmt::format(" if you see a crash, it may have been caused by bad <mounttable> contents in {}", avs::core::CFG_PATH.c_str()),
|
||||
" fix the XML file and try again"
|
||||
});
|
||||
}
|
||||
|
||||
static void create_avs_dir(
|
||||
const std::string_view &avs_path,
|
||||
const std::string_view &src_path)
|
||||
{
|
||||
@@ -1745,6 +1758,7 @@ namespace avs {
|
||||
auto real_path = std::filesystem::absolute(src_path, err);
|
||||
|
||||
if (err) {
|
||||
avs_dir_err(real_path);
|
||||
log_warning("avs-core", "failed to resolve '{}' path: {}", avs_path, err.message());
|
||||
return;
|
||||
}
|
||||
@@ -1754,7 +1768,9 @@ namespace avs {
|
||||
if (created) {
|
||||
log_info("avs-core", "created '{}' at '{}'", avs_path, real_path.string());
|
||||
}
|
||||
|
||||
if (err) {
|
||||
avs_dir_err(real_path);
|
||||
log_warning("avs-core", "failed to create '{}' folder at '{}': {}",
|
||||
avs_path,
|
||||
real_path.string(),
|
||||
@@ -1815,7 +1831,7 @@ namespace avs {
|
||||
return;
|
||||
}
|
||||
|
||||
create_dir(avs_path, device_path);
|
||||
create_avs_dir(avs_path, device_path);
|
||||
}
|
||||
|
||||
static void create_avs_config_fs_table(
|
||||
@@ -1870,7 +1886,7 @@ namespace avs {
|
||||
continue;
|
||||
}
|
||||
|
||||
create_dir(dst_path, src_path);
|
||||
create_avs_dir(dst_path, src_path);
|
||||
} while ((vfs_node = property_node_traversal(vfs_node, TRAVERSE_NEXT_SEARCH_RESULT)));
|
||||
}
|
||||
|
||||
@@ -1924,6 +1940,7 @@ namespace avs {
|
||||
static const char *LOG_LEVELS[] = { "disable", "fatal", "warning", "info", "misc", "all" };
|
||||
char current_log_level_buffer[32] { 0 };
|
||||
uint32_t current_log_level = 0;
|
||||
std::string log_level_as_str;
|
||||
|
||||
if (VERSION == AVSLEGACY || VERSION == AVS21360) {
|
||||
auto level_node = property_search(config, config_node, "/log/level");
|
||||
@@ -1939,6 +1956,7 @@ namespace avs {
|
||||
{
|
||||
if (current_log_level < std::size(LOG_LEVELS)) {
|
||||
log_info("avs-core", "log level: {}", LOG_LEVELS[current_log_level]);
|
||||
log_level_as_str = LOG_LEVELS[current_log_level];
|
||||
} else {
|
||||
log_fatal("avs-core", "log level ({}) is invalid!", current_log_level);
|
||||
}
|
||||
@@ -1958,7 +1976,7 @@ namespace avs {
|
||||
level_node = property_node_create(config, config_node, NODE_TYPE_str, "/log/level",
|
||||
LOG_LEVELS[current_log_level]);
|
||||
} else {
|
||||
log_fatal("avs-core", "log level ({}) is invalid!", current_log_level);
|
||||
log_fatal("avs-core", "log level ({}) is invalid! *", current_log_level);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1971,12 +1989,27 @@ namespace avs {
|
||||
if (property_node_refer(config, config_node, "/log/level",
|
||||
NODE_TYPE_str, current_log_level_buffer, sizeof(current_log_level_buffer)) > 0)
|
||||
{
|
||||
log_info("avs-core", "log level: {}", current_log_level_buffer);
|
||||
log_info("avs-core", "log level: {} *", current_log_level_buffer);
|
||||
log_level_as_str = current_log_level_buffer;
|
||||
} else {
|
||||
log_warning("avs-core", "log level: unknown");
|
||||
log_warning("avs-core", "log level: unknown *");
|
||||
}
|
||||
}
|
||||
|
||||
if (log_level_as_str == "disable" ||
|
||||
log_level_as_str == "fatal" ||
|
||||
log_level_as_str == "warning" ||
|
||||
log_level_as_str == "info") {
|
||||
|
||||
deferredlogs::defer_error_messages({
|
||||
fmt::format(
|
||||
"log level is set to `{}` (either in avs-config.xml or using -loglevel)", log_level_as_str),
|
||||
" this log file may have omitted important error messages from the game",
|
||||
" if you are troubleshooting crashes or failures, it is recommended that you",
|
||||
" set AVS Log Level (-loglevel) option to `all`",
|
||||
});
|
||||
}
|
||||
|
||||
// fix time offset
|
||||
auto t_now = std::time(nullptr);
|
||||
auto tm_now = *std::gmtime(&t_now);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace avs {
|
||||
namespace core {
|
||||
|
||||
+16
-2
@@ -41,6 +41,7 @@ namespace avs {
|
||||
int HTTP11 = -1;
|
||||
int URL_SLASH = -1;
|
||||
int PCB_TYPE = -1;
|
||||
bool EA3_DEBUG_VERBOSE = false;
|
||||
|
||||
// handle
|
||||
std::string VERSION_STR = "unknown";
|
||||
@@ -149,7 +150,9 @@ namespace avs {
|
||||
" * Do NOT copy over random DLLs from another game installation; DLL must match game version\n"
|
||||
"\n"
|
||||
, DLL_NAME, MODULE_PATH.string()) };
|
||||
log_fatal("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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,6 +359,12 @@ namespace avs {
|
||||
}
|
||||
}
|
||||
|
||||
// fall back to default PCBID if node is not found
|
||||
if (!EA3_PCBID[0] && PCBID_CUSTOM.empty()) {
|
||||
log_warning("avs-ea3", "no PCBID set, falling back to default PCBID value (04040000000000000000)");
|
||||
PCBID_CUSTOM = "04040000000000000000";
|
||||
}
|
||||
|
||||
// custom PCBID
|
||||
if (!PCBID_CUSTOM.empty()) {
|
||||
|
||||
@@ -399,13 +408,18 @@ namespace avs {
|
||||
avs::core::NODE_TYPE_str, "/ea3/id/accountid", &EA3_ACCOUNTID);
|
||||
}
|
||||
|
||||
// check if PCBID is defined
|
||||
// check if PCBID is defined - should never hit, left for sanity check only
|
||||
if (avs::core::property_search(ea3_config, nullptr, "/ea3/id/pcbid") == nullptr) {
|
||||
log_fatal("avs-ea3", "node not found: /ea3/id/pcbid (try using -p to specify PCBID)");
|
||||
} else if (strlen(EA3_PCBID) == 0) {
|
||||
log_fatal("avs-ea3", "no PCBID set (try using -p to specify PCBID)");
|
||||
}
|
||||
|
||||
// remove <debug> since it can expose pcbid in logged requests
|
||||
if (!EA3_DEBUG_VERBOSE) {
|
||||
avs::core::property_search_remove_safe(ea3_config, nullptr, "/ea3/debug/verbose");
|
||||
}
|
||||
|
||||
// remember IDs
|
||||
char pcbid_buffer[256] { 0 };
|
||||
char accountid_buffer[256] { 0 };
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace avs {
|
||||
extern int HTTP11;
|
||||
extern int URL_SLASH;
|
||||
extern int PCB_TYPE;
|
||||
extern bool EA3_DEBUG_VERBOSE;
|
||||
|
||||
// handle
|
||||
extern std::string VERSION_STR;
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace avs {
|
||||
if (130 <= dll_path_s.length()) {
|
||||
log_warning(
|
||||
"avs-game",
|
||||
"PATH TOO LONG WARNING\n\n\n"
|
||||
"PATH TOO LONG WARNING\n\n"
|
||||
"-------------------------------------------------------------------\n"
|
||||
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
|
||||
"WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING\n"
|
||||
@@ -95,10 +95,27 @@ namespace avs {
|
||||
"-------------------------------------------------------------------\n\n",
|
||||
dll_path_s, dll_path_s.length());
|
||||
}
|
||||
|
||||
// ddr gamemdx.dll user error
|
||||
if (avs::game::is_model("MDX") && DLL_NAME == "gamemdx.dll") {
|
||||
log_warning(
|
||||
"ddr",
|
||||
"BAD GAME DLL ERROR\n\n"
|
||||
"!!! !!!\n"
|
||||
"!!! -exec gamemdx.dll was specified !!!\n"
|
||||
"!!! this is the wrong DLL; the game will not load !!!\n"
|
||||
"!!! remove -exec argument and try again. !!!\n"
|
||||
"!!! !!!\n"
|
||||
);
|
||||
log_fatal("ddr", "BAD GAME DLL ERROR (don't use -exec gamemdx.dll, that's the wrong DLL)");
|
||||
}
|
||||
|
||||
// file not found on disk
|
||||
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", "double check -exec and -modules parameters; unless you know what you're doing, leave them blank");
|
||||
}
|
||||
|
||||
if (fileutils::verify_header_pe(dll_path)) {
|
||||
DLL_INSTANCE = libutils::load_library(dll_path);
|
||||
}
|
||||
@@ -106,7 +123,7 @@ namespace avs {
|
||||
// load entry points
|
||||
dll_entry_init = (ENTRY_INIT_T) libutils::get_proc(DLL_INSTANCE, ENTRY_INIT_NAME);
|
||||
dll_entry_main = (ENTRY_MAIN_T) libutils::get_proc(DLL_INSTANCE, ENTRY_MAIN_NAME);
|
||||
log_info("avs-game", "loaded successfully ({})", fmt::ptr(DLL_INSTANCE));
|
||||
log_info("avs-game", "loaded {} successfully ({})", DLL_NAME, fmt::ptr(DLL_INSTANCE));
|
||||
}
|
||||
|
||||
bool entry_init(char *sid_code, void *app_param) {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#define IDC_STATIC -1
|
||||
#define IDD_DIALOG1 129
|
||||
#define IDR_CHANGELOG 130
|
||||
#define IDR_LICENSES 131
|
||||
#define IDR_PATCHES 132
|
||||
#define IDR_README 133
|
||||
|
||||
@@ -49,6 +49,7 @@ BUILDDIR_64_RELEASE="./cmake-build-release-64"
|
||||
BUILDDIR_64_DEBUG="./cmake-build-debug-64"
|
||||
DEBUG=0
|
||||
OUTDIR="./bin/spice2x"
|
||||
OUTDIR_EXTRAS="./bin/spice2x/extras"
|
||||
|
||||
# disabled UPX since it tends to falsely trigger malware detection
|
||||
UPX_ENABLE=0
|
||||
@@ -59,9 +60,10 @@ INCLUDE_SRC=1
|
||||
DIST_ENABLE=1
|
||||
DIST_FOLDER="./dist"
|
||||
DIST_NAME="spice2x-$(date +%y)-$(date +%m)-$(date +%d).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.'
|
||||
TARGETS_32="spicetools_stubs_kbt spicetools_stubs_kld spicetools_cfg spicetools_spice spicetools_spice_laa spicetools_stubs_cpusbxpkm"
|
||||
TARGETS_64="spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvEncodeAPI64 spicetools_stubs_nvcuvid spicetools_stubs_nvcuda spicetools_spice64"
|
||||
TARGETS_32="spicetools_stubs_kbt spicetools_stubs_kld spicetools_cfg spicetools_cfg_linux spicetools_spice spicetools_spice_laa spicetools_spice_linux spicetools_stubs_cpusbxpkm"
|
||||
TARGETS_64="spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvEncodeAPI64 spicetools_stubs_nvcuvid spicetools_stubs_nvcuda spicetools_spice64 spicetools_spice64_linux"
|
||||
|
||||
# determine build type
|
||||
BUILD_TYPE="Release"
|
||||
@@ -179,11 +181,17 @@ fi
|
||||
|
||||
# copy to output directory
|
||||
echo "Copy files to output directory..."
|
||||
|
||||
rm -rf ${OUTDIR}
|
||||
mkdir -p ${OUTDIR}
|
||||
mkdir -p ${OUTDIR}/stubs/32
|
||||
mkdir -p ${OUTDIR}/stubs/64
|
||||
mkdir -p ${OUTDIR}/extras/largeaddressaware
|
||||
|
||||
rm -rf ${OUTDIR_EXTRAS}
|
||||
mkdir -p ${OUTDIR_EXTRAS}
|
||||
mkdir -p ${OUTDIR_EXTRAS}/largeaddressaware
|
||||
mkdir -p ${OUTDIR_EXTRAS}/linux
|
||||
|
||||
if false # ((DEBUG > 0))
|
||||
then
|
||||
# debug files
|
||||
@@ -204,11 +212,14 @@ then
|
||||
else
|
||||
# release files
|
||||
cp ${BUILDDIR_32}/spicetools/spicecfg.exe ${OUTDIR} 2>/dev/null
|
||||
cp ${BUILDDIR_32}/spicetools/spicecfg_linux.exe ${OUTDIR_EXTRAS}/linux/spicecfg.exe 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/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_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
|
||||
@@ -228,10 +239,10 @@ then
|
||||
fi
|
||||
|
||||
# copy resources
|
||||
rm -rf ${OUTDIR}/api
|
||||
mkdir ${OUTDIR}/api
|
||||
rm -rf ${OUTDIR_EXTRAS}/api
|
||||
mkdir ${OUTDIR_EXTRAS}/api
|
||||
find ./api/resources/python -name "__pycache__" -exec rm -rf {} +
|
||||
cp -r ./api/resources/* ${OUTDIR}/api
|
||||
cp -r ./api/resources/* ${OUTDIR_EXTRAS}/api
|
||||
|
||||
# build distribution archive
|
||||
if ((DIST_ENABLE > 0))
|
||||
@@ -240,7 +251,9 @@ then
|
||||
mkdir -p ${DIST_FOLDER}
|
||||
rm -rf ${DIST_FOLDER}/${DIST_NAME}
|
||||
pushd ${OUTDIR}/.. > /dev/null
|
||||
zip -qrXT9 $OLDPWD/${DIST_FOLDER}/${DIST_NAME} . -z <<< "$DIST_COMMENT"
|
||||
zip -qrXT9 $OLDPWD/${DIST_FOLDER}/${DIST_NAME} spice2x -x "spice2x/extras/*" -z <<< "$DIST_COMMENT"
|
||||
echo "Building extras..."
|
||||
zip -qrXT9 $OLDPWD/${DIST_FOLDER}/${DIST_NAME_EXTRAS} spice2x -z <<< "$DIST_COMMENT"
|
||||
popd > /dev/null
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
del /s /q .ccache
|
||||
del /s /q dist
|
||||
call build_docker.bat
|
||||
@@ -9,7 +9,6 @@
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
IDR_CHANGELOG RCDATA "../changelog.txt"
|
||||
IDR_LICENSES RCDATA "../licenses.txt"
|
||||
IDR_PATCHES RCDATA "../build/patches.json"
|
||||
IDR_README RCDATA "../readme.txt"
|
||||
|
||||
@@ -82,18 +82,23 @@ public:
|
||||
return this->index != 0xFF;
|
||||
}
|
||||
|
||||
inline void clearBindings() {
|
||||
device_identifier = "";
|
||||
index = 0xFF;
|
||||
inline void resetValues() {
|
||||
setSensitivity(1.f);
|
||||
setDeadzone(0.f);
|
||||
invert = false;
|
||||
smoothing = false;
|
||||
deadzone_mirror = false;
|
||||
setMultiplier(1);
|
||||
setRelativeMode(false);
|
||||
setDelayBufferDepth(0);
|
||||
}
|
||||
|
||||
inline void clearBindings() {
|
||||
device_identifier = "";
|
||||
index = 0xFF;
|
||||
resetValues();
|
||||
}
|
||||
|
||||
inline const std::string &getName() const {
|
||||
return this->name;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define IDR_CHANGELOG 130
|
||||
#define IDR_LICENSES 131
|
||||
#define IDR_PATCHES 132
|
||||
#define IDR_README 133
|
||||
|
||||
@@ -87,6 +87,10 @@ namespace cfg {
|
||||
load_float_value(doc, root + prefix + "scale_x", scene.scale_x);
|
||||
load_float_value(doc, root + prefix + "scale_y", scene.scale_y);
|
||||
load_bool_value(doc, root + prefix + "keep_aspect_ratio", scene.keep_aspect_ratio);
|
||||
|
||||
int duplicate = 0;
|
||||
load_int_value(doc, root + prefix + "duplicate", duplicate);
|
||||
scene.duplicate = static_cast<cfg::ScreenDuplicateMode>(duplicate);
|
||||
}
|
||||
|
||||
// windowed settings are always under game settings
|
||||
@@ -201,6 +205,7 @@ namespace cfg {
|
||||
rapidjson::Pointer(root + prefix + "scale_x").Set(doc, scene.scale_x);
|
||||
rapidjson::Pointer(root + prefix + "scale_y").Set(doc, scene.scale_y);
|
||||
rapidjson::Pointer(root + prefix + "keep_aspect_ratio").Set(doc, scene.keep_aspect_ratio);
|
||||
rapidjson::Pointer(root + prefix + "duplicate").Set(doc, scene.duplicate);
|
||||
}
|
||||
|
||||
// windowed mode settings
|
||||
|
||||
@@ -14,12 +14,19 @@ namespace cfg {
|
||||
ResizableFrame = 2
|
||||
};
|
||||
|
||||
enum ScreenDuplicateMode {
|
||||
None = 0,
|
||||
CopyLeft = 1,
|
||||
CopyRight = 2,
|
||||
};
|
||||
|
||||
struct fullscreen_setting {
|
||||
int offset_x = 0;
|
||||
int offset_y = 0;
|
||||
float scale_x = 1.0;
|
||||
float scale_y = 1.0;
|
||||
bool keep_aspect_ratio = true;
|
||||
ScreenDuplicateMode duplicate = ScreenDuplicateMode::None;
|
||||
};
|
||||
|
||||
extern std::optional<std::string> SCREEN_RESIZE_CFG_PATH_OVERRIDE;
|
||||
|
||||
@@ -1,722 +0,0 @@
|
||||
05/31/2025 [spice2x]
|
||||
IIDX: tape LED over API
|
||||
UI tweaks
|
||||
Option to make command line args take precedence
|
||||
|
||||
05/09/2025 [spice2x]
|
||||
Check for window focus when processing input (-inputfocus)
|
||||
More options for IIDX/SDVX windowed subscreen
|
||||
SpiceCompanion screen mirroring quality options
|
||||
Various LovePlus improvements and fixes:
|
||||
Allow camera access
|
||||
Full screen mode
|
||||
Add printer options
|
||||
Add stub for printer DLL (cpusbxpkm.dll)
|
||||
Logging improvements for better troubleshooting
|
||||
|
||||
04/25/2025 [spice2x]
|
||||
LargeAddressAware variant of spice.exe for Metal Gear
|
||||
Save new JSON config files to %appdata%/spice2x
|
||||
Various bug fixes
|
||||
|
||||
04/08/2025 [spice2x]
|
||||
Ensure rawinput touch is default for all games (-wintouch to override)
|
||||
Various overlay UI fixes and improvements, update ImGui library
|
||||
Add missing lights in Museca
|
||||
|
||||
03/29/2025 [spice2x]
|
||||
Add SDVX landscape mode, -forceresswap option
|
||||
Fix bugs in image scaler
|
||||
Configurator UI tweaks
|
||||
|
||||
03/25/2025 [spice2x]
|
||||
Add Mahjong Fight Girl support
|
||||
Add option for custom full screen resolution (-forceres)
|
||||
|
||||
03/24/2025 [spice2x]
|
||||
Auto PIN entry
|
||||
Multiple scenes for image resize
|
||||
API: add resize function, lights.read can now filter lights by name
|
||||
Fix DLL hooks not receiving command-line arguments
|
||||
|
||||
03/16/2025 [spice2x]
|
||||
Fix clipboard copy function truncating last character of string
|
||||
|
||||
03/03/2025 [spice2x]
|
||||
IIDX: add workaround for REVERB EX filter missing in Windows 11 update
|
||||
|
||||
02/25/2025 [spice2x]
|
||||
Add -vsyncbuffer option for forcing double/triple buffering
|
||||
|
||||
02/22/2025 [spice2x]
|
||||
Add -windowscale option for resizing DX9 backbuffer dimensions
|
||||
|
||||
01/26/2025 [spice2x]
|
||||
Expanded main menu in overlay (default bind: esc key)
|
||||
Nostalgia: touch and swipe gestures (-nostpoke)
|
||||
Jubeat: detect root directory boot failure
|
||||
Add "Any Direction" analog type for button binding
|
||||
|
||||
01/16/2025 [spice2x]
|
||||
More MIDI improvements:
|
||||
use v2_drum algorithm instead of v2 by default for drummania & FTT
|
||||
threshold setting for CC when mapped as a button (hi-hat pedals)
|
||||
sustain duration applied to CC OnOff controls (sustain pedals)
|
||||
Nostalgia, Beatstream: fix EA card insertion issue
|
||||
CCJ, QKS: translate some windowed mode settings to Unity engine
|
||||
Fix DEBUG build
|
||||
|
||||
01/08/2025 [spice2x]
|
||||
Better MIDI support; significant improvements for drums
|
||||
Fix various UI bugs in Buttons / Analog tabs
|
||||
Nostalgia: fix velocity handling for MIDI input
|
||||
Show camera names in camera control overlay
|
||||
Game exit dialog (default: Escape key)
|
||||
Button layout help text in Buttons tab
|
||||
|
||||
12/31/2024 [spice2x]
|
||||
Fix videos not playing in Unity-based games
|
||||
Dump audio device info in wrapped WASAPI handler
|
||||
Update DLL load failure message
|
||||
|
||||
12/30/2024 [spice2x]
|
||||
Busou Shinki: Show Cursor option now properly shows cursor in game
|
||||
IIDX: add IIDX NVENC Quality option (-iidxreccqp)
|
||||
IIDX: always enable NVENC hook, not just when -iidx is set
|
||||
IIDX: add help text to keypad overlay in TDJ mode
|
||||
Busou Shinki, QKS, CCJ: Only show cursor by default if no touchscreen detected
|
||||
Ignore invalid HID devices with bad device path
|
||||
|
||||
12/13/2024 [spice2x]
|
||||
Improve error messages for DLL load failures
|
||||
Load cardio module in spicecfg
|
||||
|
||||
12/06/2024 [spice2x]
|
||||
Clean up seldom used features (VR, Lua scripting, layeredfs, KFControl)
|
||||
New card scanner section in Cards tab
|
||||
Add -scardfix option for converting NFC cards into E00401 format
|
||||
Add system information logging, controlled by -sysdump option
|
||||
|
||||
11/20/2024 [spice2x]
|
||||
DRS: add subscreen overlay window (dance floor tape LED display)
|
||||
IIDX: TDJ cam selection options for users with 3+ cameras
|
||||
(-iidxtdjcamhooktop /-iidxtdjcamhookfront)
|
||||
IIDX/SDVX: move some options to Advanced tab
|
||||
Update -nvprofile to set V-Sync settings as Application Controlled
|
||||
|
||||
10/29/2024 [spice2x]
|
||||
(IIDX31+) apply signature patch to force WASAPI by default; use -iidxsounddevice to override
|
||||
Fix SDVX EG song search when using Japanese touch keyboard (when OS is not ja-JP)
|
||||
Nostalgia I/O improvements
|
||||
Expose DDR Gold cabinet tape LEDs via Spice API
|
||||
|
||||
10/14/2024 [spice2x]
|
||||
Apply patches earlier, on DLL load notification
|
||||
SMX dedicab lights improvements
|
||||
|
||||
10/12/2024 [spice2x]
|
||||
SMX dedicab lights support (requires forked SMX.dll - see Issue #228)
|
||||
Option to show FPS overlay on top left (-fpsflip)
|
||||
Allow TDJ camhook to load without -iidx (for cabs)
|
||||
|
||||
09/21/2024 [spice2x]
|
||||
Add Inject Early DLL Hooks option (-z)
|
||||
|
||||
09/14/2024 [spice2x]
|
||||
Add Patch Manager Config Path (-patchcfgpath) option
|
||||
Vertical/horizontal flip for TDJ cam hook
|
||||
Fix mouse button behavior when left/right buttons are swapped
|
||||
|
||||
08/24/2024 [spice2x]
|
||||
Fix Patch Manager failing to import patches when modules path is Unicode
|
||||
|
||||
08/22/2024 [spice2x]
|
||||
UI update for Card Manager in overlay
|
||||
|
||||
08/12/2024 [spice2x]
|
||||
IIDX TDJ camera - bug fixes
|
||||
Prevent users from accidentally enabling -cfg and -kfcontrol in spicecfg
|
||||
|
||||
08/07/2024 [spice2x]
|
||||
IIDX TDJ camera improvements - draw modes (stretch/crop), bug fixes
|
||||
|
||||
08/06/2024 [spice2x]
|
||||
IIDX TDJ camera improvements
|
||||
Automatic cropping for 16:9 resolutions
|
||||
Toggle to keep or override camera parameters
|
||||
Fix Unicode path handling for config files
|
||||
|
||||
08/03/2024 [spice2x]
|
||||
IIDX TDJ camera improvements
|
||||
Camera control overlay (check Overlay tab)
|
||||
Hardware acceleration for camera rendering
|
||||
IIDX TDJ play record improvements
|
||||
NVENC hooks have been added to fix crash on song start; -nod3d9devhook
|
||||
is no longer required
|
||||
|
||||
07/29/2024 [spice2x]
|
||||
Fix IIDX TDJ not launching in fullscreen with two monitors
|
||||
|
||||
07/28/2024 [spice2x]
|
||||
Add more DLL support for -iidxtdjcamhook
|
||||
Add -iidxtdjcamhookratio and -iidxtdjcamhookoffset
|
||||
|
||||
07/25/2024 [spice2x]
|
||||
Add -iidxtdjcamhook option for webcam support in TDJ
|
||||
|
||||
07/17/2024 [spice2x]
|
||||
Disable touch feedback indicators
|
||||
Bug fixes
|
||||
|
||||
07/15/2024 [spice2x]
|
||||
Fix touch and touch emulation in wintouch-based games (Nostalgia, BeatStream...)
|
||||
Fix misaligned SpiceCompanion touches on windowed TDJ subscreen
|
||||
Improvements to TDJ poke feature
|
||||
|
||||
07/13/2024 [spice2x]
|
||||
Fix windowed TDJ subscreen not accepting mouse clicks after card in
|
||||
Allow windowed TDJ/UFC subscreen to minimize, but not close
|
||||
|
||||
07/10/2024 [spice2x]
|
||||
Better gfdm XG2 and XG3 support
|
||||
IIDX TDJ windowed mode rendering improvements
|
||||
IIDX TDJ subscreen poke feature
|
||||
CardIO NumLock toggle option
|
||||
Patch manager UI tweaks
|
||||
|
||||
07/06/2024 [spice2x]
|
||||
Reliability improvements for config file saving
|
||||
Experimental and incomplete support for gfdm XG3
|
||||
Option to specify path for screen resize config
|
||||
Reflec Beat: fix being unable to type into overlay
|
||||
|
||||
07/03/2024 [spice2x]
|
||||
Fix SMX stage compatibility
|
||||
Patch manager: log header in patches JSON, better error handling
|
||||
|
||||
06/26/2024 [spice2x]
|
||||
Fix MDX-003 not retrieving patches from online source
|
||||
|
||||
06/22/2024 [spice2x]
|
||||
Reliability & performance improvements for auto-card-insert
|
||||
Update -sdvxnosub to prevent creation of subscreen in windowed mode
|
||||
Bug fixes
|
||||
|
||||
06/20/2024 [spice2x]
|
||||
Improve experience for windowed SDVX UFC (always launch subscreen window)
|
||||
Add Delay option in analog binds
|
||||
Fix pop'n soft-lock with auto card insert
|
||||
Fix -graphics-single-adapter not working in Beatstream/Nostalgia
|
||||
|
||||
06/14/2024 [spice2x]
|
||||
Improve experience for touch-enabled games in windowed mode (SpiceTouch)
|
||||
|
||||
06/08/2024 [spice2x]
|
||||
Patch manager bug fixes
|
||||
|
||||
06/03/2024 [spice2x]
|
||||
Fix patch status not being saved properly
|
||||
Search tab for finding options
|
||||
|
||||
06/02/2024 [spice2x]
|
||||
Fix BBC and Museca hang on boot when COM ports are present
|
||||
Deprecate -iidxtdjw (use -iidxtdj and -w together instead)
|
||||
|
||||
05/31/2024 [spice2x]
|
||||
Redesign patches tab
|
||||
Bug fixes for patches.json parsing
|
||||
|
||||
05/29/2024 [spice2x]
|
||||
Add support for "number" patch type
|
||||
History for remote patch URLs
|
||||
|
||||
05/28/2024 [spice2x]
|
||||
Update -iidxtdjw to hide useless second window
|
||||
|
||||
05/27/2024 [spice2x]
|
||||
Fix -apiserial crashing when -apiserialbaud is not provided
|
||||
Suppress "failed to acquire subscreen" error when not relevant
|
||||
|
||||
05/22/2024 [spice2x]
|
||||
Add caution string to patches.json format
|
||||
Paste from clipboard button for URL patches import
|
||||
|
||||
05/12/2024 [spice2x]
|
||||
Disable URL patch importing in-game
|
||||
UI tweaks for importing patches
|
||||
|
||||
05/05/2024 [spice2x]
|
||||
UI tweaks for importing patches
|
||||
Add aliases for arguments with -sp2x prefix
|
||||
|
||||
05/04/2024 [spice2x]
|
||||
New patch JSON format, importing patches from URL
|
||||
Add Process Efficiency Class option for hetero CPUs
|
||||
|
||||
04/29/2024 [spice2x]
|
||||
Move -vr to be DANCERUSH only option
|
||||
Fix being unable to type into overlay when -nolegacy is on
|
||||
|
||||
04/08/2024 [spice2x]
|
||||
Fix Discord rich presence app IDs
|
||||
|
||||
03/31/2024 [spice2x]
|
||||
Fix IC CARD UNIT ERROR in UFC when COM ports are present
|
||||
|
||||
03/24/2024 [spice2x]
|
||||
Fix Ongaku Paradise crash on boot due to VFS redirection
|
||||
Address all compiler warnings
|
||||
|
||||
03/17/2024 [spice2x]
|
||||
SMX pad lights output
|
||||
Fix DDR pad input for P4IO / BIO2
|
||||
Option aliases for IIDX/SDVX subscreen disable
|
||||
|
||||
03/16/2024 [spice2x]
|
||||
I/O for DDR white cabinet type (P4IO)
|
||||
Remove background animation from configurator
|
||||
Compiler updates
|
||||
|
||||
03/06/2024 [spice2x]
|
||||
Lights output for DDR gold cabinet type
|
||||
|
||||
02/13/2024 [spice2x]
|
||||
Fix lights not updating over API in configurator
|
||||
CCJ mouse trackball improvements
|
||||
Make IIDX/SDVX native touch the default when -sp2x-nod3d9devhook is on
|
||||
Remove log spam in IIDX
|
||||
|
||||
02/02/2024 [spice2x]
|
||||
Add missing NVCUDA stubs for cuStreamCreate and cuStreamDestroy_v2
|
||||
|
||||
02/01/2024 [spice2x]
|
||||
QuizKnock STADIUM support
|
||||
Busou Shinki analog joystick support
|
||||
|
||||
01/30/2024 [spice2x]
|
||||
Add woofer lights to popn HD mode
|
||||
Prevent crash when both -graphics-force-single-adapter and
|
||||
-sp2x-nod3d9devhook are enabled
|
||||
|
||||
01/27/2024 [spice2x]
|
||||
Add Disable D3D9 Device Hook option (-sp2x-nod3d9devhook)
|
||||
|
||||
01/20/2024 [spice2x]
|
||||
Force Exit overlay hotkey
|
||||
Improve Low Latency Audio to work with more games
|
||||
Charge Machine I/O fixes
|
||||
UI tweaks (API tab, Development tab)
|
||||
|
||||
01/07/2024 [spice2x]
|
||||
Chase Chase Jokers improvements (trackball and vsync fix)
|
||||
|
||||
01/06/2024 [spice2x]
|
||||
Chase Chase Jokers improvements (cmd line args, trackball sensitivity)
|
||||
|
||||
01/05/2024 [spice2x]
|
||||
Chase Chase Jokers support
|
||||
|
||||
01/01/2024 [spice2x]
|
||||
UI tweaks - menu bar in configurator window
|
||||
IIDX: scan for SOUND_OUTPUT_DEVICE in binary and log message
|
||||
Switch from -Ofast to -O2
|
||||
Remove ImGui demo and debug files from release (reduced binary size)
|
||||
|
||||
12/30/2023 [spice2x]
|
||||
Add NVAPI Block option (-sp2x-nonvapi)
|
||||
SDVX: Auto Card Insert disables itself after a timeout to prevent soft lock
|
||||
|
||||
12/27/2023 [spice2x]
|
||||
Prevent SDVX from dumping PATH variable to log
|
||||
Stubs for NVIDIA DLLs (nvcuda.dll, nvcuvid.dll, nvEncodeAPI64.dll)
|
||||
UI tweaks
|
||||
|
||||
12/25/2023 [spice2x]
|
||||
Fix Lock Cursor option not updating capture area in windowed mode
|
||||
Update TDJ rom file hooks
|
||||
|
||||
12/23/2023 [spice2x]
|
||||
Remove the need for hex edits to fix note scroll speed in SDVX VM on NVIDIA
|
||||
Improvements to -graphics-force-refresh
|
||||
|
||||
12/22/2023 [spice2x]
|
||||
HID analog relative axis mode
|
||||
Automatic process affinity fix for Gitadora
|
||||
Fix -cfgpath option not working
|
||||
Buttons tab UI fix for Bind Many
|
||||
|
||||
12/18/2023 [spice2x]
|
||||
Fix Road Fighters 3D I/O error on boot
|
||||
|
||||
12/17/2023 [spice2x]
|
||||
Integer multiplier / divisor for HID analog
|
||||
(alternative to existing sensitivity option)
|
||||
|
||||
12/15/2023 [spice2x]
|
||||
Add hooks to prevent display scaling changes when SDVX launches
|
||||
|
||||
12/12/2023 [spice2x]
|
||||
Add support for keypad in SDVX UFC/VM mode
|
||||
New DX9on12 flag with more options (-sp2x-dx9on12, replaces -9on12)
|
||||
|
||||
12/11/2023 [spice2x]
|
||||
Fix crash at launch on AMD/Intel GPUs when nvapi DLL is present
|
||||
|
||||
12/09/2023 [spice2x]
|
||||
Lights output for Gitadora
|
||||
Lights output for DRS
|
||||
Tape LED averaging algorithm option (-sp2x-tapeledalgo)
|
||||
Fix occasional crash on shutdown
|
||||
|
||||
11/23/2023 [spice2x]
|
||||
Low latency shared audio option (-sp2x-lowlatencysharedaudio)
|
||||
Automatic dev/raw/* folder creation for popn19-21
|
||||
Move overlay bindings to Overlay tab
|
||||
|
||||
11/17/2023 [spice2x]
|
||||
Use E00401 prefix for card number generation
|
||||
Volume API hooks to prevent audio volume change (enabled by default)
|
||||
Small UI tweaks
|
||||
|
||||
11/15/2023 [spice2x]
|
||||
Automatic card insert option (-sp2x-autocard)
|
||||
Dump CPU features to log on startup
|
||||
|
||||
11/12/2023 [spice2x]
|
||||
Fix crash in some games caused by linker changes in 2022
|
||||
(Gitadora Exchain and older, Bone Eater, etc)
|
||||
Add IIDX native touch option (-sp2x-iidxnativetouch)
|
||||
Fix V-Sync issues with auto-screen-orientation
|
||||
|
||||
11/04/2023 [spice2x]
|
||||
Option for NVIDIA GPU optimization (-sp2x-nvprofile)
|
||||
SmartCard fixes (for -scardflip and -scardtoggle)
|
||||
Small reorganization of options in configurator
|
||||
|
||||
10/30/2023 [spice2x]
|
||||
Add touch input support for DRS
|
||||
Add TDJ Windowed mode for IIDX (-sp2x-iidxtdjw)
|
||||
Fix -iidxasio not working in some versions of IIDX
|
||||
|
||||
10/22/2023 [spice2x]
|
||||
Add E-spec I/O emulation for IIDX 30+
|
||||
Fix IIDX TDJ launching at 60Hz for some users
|
||||
Add workaround for buggy ASIO drivers that lock up on close
|
||||
Hide Insert Card overlay by default, add option to show it again
|
||||
|
||||
09/29/2023 [spice2x]
|
||||
Fix subscreen not updating in certain versions of EG.
|
||||
Detect long paths and log a warning message.
|
||||
|
||||
06/08/2023 [spice2x]
|
||||
Improved touch support for Jubeat and Reflec Beat.
|
||||
|
||||
04/23/2023 [spice2x]
|
||||
Update -iidxsounddevice to automatically pick a value (wasapi in most cases)
|
||||
|
||||
04/21/2023 [spice2x]
|
||||
Fix spicecfg not launching in some specific cases
|
||||
|
||||
04/20/2023 [spice2x]
|
||||
Update font for IIDX segment display
|
||||
New option: auto show FPS window
|
||||
|
||||
04/16/2023 [spice2x]
|
||||
New feature: window resize. Added new options to change window size and
|
||||
position on launch. Screen Resize window (F11) also updated with new
|
||||
controls
|
||||
Screen Resize settings are now per-game
|
||||
Misc bug fixes
|
||||
|
||||
04/09/2023 [spice2x]
|
||||
Add IIDX LED ticker (segment display) as subscreen overlay for LDJ
|
||||
Add I/O panel window for all games, special support for IIDX, DDR, GFDM
|
||||
Add options to automatically show certain windows on game launch
|
||||
|
||||
04/03/2023 [spice2x]
|
||||
Move/resize for IIDX/SDVX subscreen overlay window
|
||||
New options to control subscreen overlay window
|
||||
Turn common config mistakes into fatal error messages
|
||||
Various bug fixes for touch handling
|
||||
Revert "pop'n launching at desktop refresh rate" hack from 03/30/2023
|
||||
|
||||
03/30/2023 [spice2x]
|
||||
New option: IIDX Force Definition Type HD (720p)
|
||||
New option: process priority
|
||||
Fix pop'n launching at desktop refresh rate
|
||||
Lower logger thread priority to reduce stutters
|
||||
Misc bug fixes
|
||||
|
||||
03/25/2023 [spice2x]
|
||||
Screen Resize UI update (F11)
|
||||
FPS UI update (F12)
|
||||
New option: processor affinity
|
||||
Misc bug fixes
|
||||
|
||||
03/20/2023 [spice2x]
|
||||
ASIO device override for SDVX (experimental)
|
||||
Fix subscreen overlay for SDVX in Valkyrie mode using BI2X I/O
|
||||
Update text for touch and audio config options
|
||||
UI tweaks and minor bug fixes
|
||||
|
||||
03/17/2023 [spice2x]
|
||||
Disable UPX (file sizes will be larger)
|
||||
Fix regression from 2022 that prevented early patches
|
||||
from being loaded properly (such as audio hex edits)
|
||||
New option: HID lighting brightness
|
||||
New option: display auto-rotate direction
|
||||
New option: custom sleep duration
|
||||
|
||||
03/15/2023 [spice2x]
|
||||
Improve IIDX TT+/- handling (allow alternate key bind)
|
||||
Fix SpiceCompanion touch targets for TDJ FHD
|
||||
Config UI tweaks
|
||||
|
||||
03/13/2023 [spice2x]
|
||||
Fix TDJ FHD launching at wrong refresh rate
|
||||
New IIDX/SDVX options for TT/knob sensitivity when using digital input
|
||||
|
||||
03/12/2023 [spice2x]
|
||||
Fix mouse input for SDVX EG subscreen overlay
|
||||
New unified layout for Options tab
|
||||
Allow multiple DLLs in Inject Hook field
|
||||
|
||||
03/11/2023 [spice2x]
|
||||
Beginning of spice2x fork, based on another fork
|
||||
Ensure TDJ mode runs at 1080p@120Hz for newer games
|
||||
Fix IIDX subscreen overlay in FHD mode
|
||||
Gracefully deal with bad ea3-config.xml
|
||||
|
||||
04/02/2022 - Add lua scripting
|
||||
Add button edit dialog
|
||||
Add button debounce/invert settings
|
||||
Add bind many function
|
||||
Misc bug fixes
|
||||
12/28/2021 - Add DirectX 9On12 wrapper option
|
||||
Add analog deadzone settings
|
||||
Fix SSL handshaking failed issue
|
||||
Misc bug fixes
|
||||
12/19/2021 - Force single graphics adapter option
|
||||
Performance improvements
|
||||
Misc bug fixes
|
||||
10/12/2021 - Ongaku Paradise support
|
||||
Fixed memory leak in Windows 8+ touch handler
|
||||
Fix Gitadora two channel fix for Nextage
|
||||
Fix Charge Machine touch window position
|
||||
IIDX 19 support
|
||||
04/02/2021 - Charge Machine support
|
||||
Fix "unsupported fs type" endless loop
|
||||
Misc bug fixes
|
||||
03/01/2021 - Add DDR HD speaker lights
|
||||
Automatic /dev/nvram and /dev/raw creation for AVS 2.17 and later
|
||||
Fix FlexASIO crash introduced in previous release
|
||||
Fix overlay crash from ImGui navigation button handling
|
||||
Fix AVS configuration reading with binary files
|
||||
Add AVS VFS drive remapping for E: and F:
|
||||
Improve XP compatibility
|
||||
Force dummy audio when using ASIO backend
|
||||
Misc bug fixes
|
||||
12/30/2020 - Metal Gear Arcade lights and fixes
|
||||
Misc bug fixes
|
||||
12/11/2020 - Fix patch manager crashing on invalid address
|
||||
Automatic /dev/nvram and /dev/raw creation for AVS 2.16 and prior
|
||||
Analog sensitivity improvements
|
||||
Locale improvements
|
||||
Misc bug fixes
|
||||
10/27/2020 - Screen Capture support
|
||||
IIDX TDJ support
|
||||
Updated patches
|
||||
Misc bug fixes
|
||||
10/13/2020 - Configurator patches tab
|
||||
KFControl fix cursor wrap
|
||||
Screen resize window
|
||||
Misc bug fixes
|
||||
10/03/2020 - Add KFControl
|
||||
Misc bug fixes
|
||||
09/27/2020 - Reflec limelight & colette (LBR) support
|
||||
LovePlus lights support
|
||||
Fix module loading when spice(64).exe is outside contents
|
||||
Ignore touchpad HID devices in touchscreen detection
|
||||
Keypad bindings
|
||||
SSL support
|
||||
Analog knob preview
|
||||
06/15/2020 - QMA physical keyboard support
|
||||
Misc bug fixes
|
||||
05/19/2020 - Alternate audio backends for exclusive-mode applications
|
||||
Fix screenshot handler crash in KMA and LMA
|
||||
MessageBox hooks (stops fullscreen windows from losing focus)
|
||||
04/25/2020 - pop'n music 19 & 20 support
|
||||
DDR X2 support
|
||||
pop'n & HPM HDMI fix
|
||||
Misc additions, fixes
|
||||
03/13/2020 - Misc bug fixes and improvements
|
||||
03/10/2020 - Winning Eleven support
|
||||
03/04/2020 - Tenkaichi Shogikai support
|
||||
Otoca D'or support
|
||||
Misc bug fixes
|
||||
02/29/2020 - MIDI hotplugging / overlay
|
||||
Misc bug fixes
|
||||
02/04/2020 - Configurator options tab
|
||||
Overlay button bindings
|
||||
Card generator
|
||||
Misc bug fixes
|
||||
01/22/2020 - Audio improvements
|
||||
SDVX print overwrite option
|
||||
PNM HD/SD options
|
||||
Window icons
|
||||
Misc improvements
|
||||
01/11/2020 - New Config Utility (F4)
|
||||
Audio Hooks
|
||||
Misc bug fixes
|
||||
01/03/2020 - DANCERUSH STARDOM support
|
||||
Chinese SDVX support
|
||||
Nostalgia OP2 support
|
||||
LayeredFS now complete
|
||||
Misc bug fixes
|
||||
11/26/2019 - LayeredFS integration
|
||||
Misc bug fixes
|
||||
11/17/2019 - Screenshots (PrtScr Key)
|
||||
DLL Architecture detection
|
||||
SCard Felica support
|
||||
Improve MIDI performance
|
||||
Fix bt5api error handling
|
||||
Fix XP support
|
||||
11/05/2019 - Gitadora XG3 support
|
||||
Guitar motor support
|
||||
Gitadora cabinet setting
|
||||
DDR HD->SD light support
|
||||
ESP8266/NodeMCU API support
|
||||
Misc bug fixes
|
||||
10/13/2019 - Serial API support
|
||||
Fullscreen monitor setting
|
||||
Misc bug fixes
|
||||
09/30/2019 - Fixed verbose D3D9 logging
|
||||
MiniMaid support
|
||||
EA-Dev functions
|
||||
Metal Gear bug fixes
|
||||
09/26/2019 - Steel Chronicle support
|
||||
ACIO buffer view/editor
|
||||
Misc bug fixes
|
||||
09/21/2019 - IIDX Effector View
|
||||
Performance improvements
|
||||
PCBID now hidden in log file
|
||||
Misc bug fixes
|
||||
09/16/2019 - Introducing SpiceOverlay
|
||||
- Virtual Keypads/Cards
|
||||
- Patch Manager
|
||||
- FPS/Log/Control Windows
|
||||
Pop'n Music fancy window mode
|
||||
Optional app-config.xml
|
||||
Misc improvements
|
||||
Misc bug fixes
|
||||
09/09/2019 - DDR BIO2 support
|
||||
NVIDIA Optimus support
|
||||
Reflec Beat faster loading
|
||||
Performance improvements
|
||||
Misc bug fixes
|
||||
08/24/2019 - Automatic G-Sync disable
|
||||
Fixed Scotto inverted keypad
|
||||
Fixed SDVX Gen1 input drops
|
||||
Performance improvements
|
||||
08/19/2019 - Scotto support
|
||||
Mahjong Fight Club support
|
||||
FutureTomTom Kinect fix
|
||||
Fixed AVS heap size defaults
|
||||
Fixed signal hook crash
|
||||
Fixed early boot logging
|
||||
08/14/2019 - HELLO! Pop'n Music lights
|
||||
Improve performance
|
||||
08/13/2019 - Add -cfgpath option
|
||||
Misc additions/fixes
|
||||
08/10/2019 - HELLO! Pop'n Music support
|
||||
LovePlus support
|
||||
Discord Rich Presence support
|
||||
API shutdown/reboot
|
||||
Add -nolegacy option
|
||||
Colored logging
|
||||
07/31/2019 - QMA windowed mode fix
|
||||
QMA network check fix
|
||||
Logging improvements
|
||||
07/22/2019 - DDR SD mode reader fix
|
||||
Misc bugs fixed with ACIO
|
||||
Beatstream lights/mouse
|
||||
Beatstream -ea support
|
||||
07/19/2019 - FutureTomTom support
|
||||
Reflec Beat lights
|
||||
Dance Evolution lights
|
||||
Nostalgia lights
|
||||
SuperExit on ALT-F4
|
||||
07/16/2019 - Hook DLLs load earlier
|
||||
Heap size argument alias
|
||||
API restart function
|
||||
SmartEA cmd line option
|
||||
Reader keypad support
|
||||
Initial MFC support
|
||||
Misc bug fixes
|
||||
06/22/2019 - Touchpad button binding fix
|
||||
MIDI buttons/analogs support
|
||||
Misc bug fixes
|
||||
05/18/2019 - API WebSocket support
|
||||
05/16/2019 - Device Hotplugging
|
||||
USB-Stick card inserts
|
||||
Cursor capture fix
|
||||
EA server fix for old AVS
|
||||
05/07/2019 - Reflec 1 support
|
||||
Gitadora custom PCBID
|
||||
Touchscreen improvements
|
||||
Touchscreen support on XP
|
||||
Higher RB input polling rate
|
||||
L/R-Shift/Control support
|
||||
04/13/2019 - DDR SD Mode lights
|
||||
Sextet (LIT Board) support
|
||||
PIUIO hardware support
|
||||
Windowed mode improvements
|
||||
03/14/2019 - IIDX API module
|
||||
EA server maintenance toggle
|
||||
SDVX BI2A support
|
||||
02/19/2019 - Network API server
|
||||
Run outside of modules dir
|
||||
Misc bug fixes
|
||||
01/27/2019 - Jubeat Knit support
|
||||
Quiz Magic Academy support
|
||||
Museca/Jubeat lights
|
||||
Smartcard NumLock toggle
|
||||
01/11/2019 - Open Source!
|
||||
Jubeat Copious support
|
||||
Bishi Bashi Channel support
|
||||
PacDrive/PocketVoltex support
|
||||
KBT/KLD now are optional
|
||||
Stacktraces, quiet crash
|
||||
Huge code refactoring
|
||||
Stability improvements/fixes
|
||||
11/03/2018 - IIDX 25 (io, cams, light)
|
||||
IIDX effector bindings
|
||||
HID lighting output support
|
||||
Button Multibinding
|
||||
HID readers e.g. zyp's cardio
|
||||
Bug fixes as usual
|
||||
08/17/2018 - DDR X3, Nostalgia, Network
|
||||
06/25/2018 - HID smartcard readers
|
||||
06/10/2018 - DDR support
|
||||
06/07/2018 - Gitadora/HID/EA bug fixes
|
||||
05/18/2018 - Gitadora and MIDI devices
|
||||
05/07/2018 - Support for hat switches
|
||||
04/21/2018 - Card path configuration
|
||||
04/09/2018 - HID/Analog/RawInput support
|
||||
03/13/2018 - SDVX printer, felica, BT5API
|
||||
01/23/2018 - MUSECA analog slowdown, fixes
|
||||
01/21/2018 - Many fixed bugs/crashes
|
||||
11/15/2017 - Fixed the MUSECA sound issue
|
||||
11/14/2017 - Added MUSECA, Pop'n Music
|
||||
Wavepass reader support
|
||||
09/11/2017 - Added Road Fighters 3D
|
||||
08/29/2017 - Custom modules path,
|
||||
UAC admin manifest, netfix
|
||||
08/28/2017 - Bug fixes
|
||||
08/21/2017 - 64bit builds, beatstream
|
||||
More command line options
|
||||
07/28/2017 - Fixed I/O for IIDX and SDVX
|
||||
07/26/2017 - Configuration Tool, Jubeat
|
||||
Touch module, more options
|
||||
06/01/2017 - Added DEA support
|
||||
05/31/2017 - Public release
|
||||
+4
@@ -1527,8 +1527,10 @@ FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg<Context> {
|
||||
return arg;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdangling-reference"
|
||||
#endif
|
||||
|
||||
// The type template parameter is there to avoid an ODR violation when using
|
||||
// a fallback formatter in one translation unit and an implicit conversion in
|
||||
@@ -1544,7 +1546,9 @@ FMT_CONSTEXPR FMT_INLINE auto make_arg(const T& val) -> value<Context> {
|
||||
return arg;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
template <bool IS_PACKED, typename Context, type, typename T,
|
||||
FMT_ENABLE_IF(!IS_PACKED)>
|
||||
|
||||
Vendored
+3
@@ -23,6 +23,7 @@
|
||||
* Modified version.
|
||||
*/
|
||||
|
||||
#ifndef NO_SCARD
|
||||
|
||||
#include "scard.h"
|
||||
#include <windows.h>
|
||||
@@ -409,3 +410,5 @@ void scard_threadstart() {
|
||||
void scard_fini() {
|
||||
should_exit = true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace games::ccj {
|
||||
bool MOUSE_TRACKBALL_USE_TOGGLE = false;
|
||||
uint8_t TRACKBALL_SENSITIVITY = 10;
|
||||
|
||||
static HANDLE fakeHandle = (HANDLE)0xDEADBEEF;
|
||||
static HANDLE fakeHandle = (HANDLE)0xDEADBEEFull;
|
||||
static HWND hWnd = nullptr;
|
||||
static WNDPROC wndProc = nullptr;
|
||||
static std::thread *tbThread = nullptr;
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace games::ddr {
|
||||
|
||||
// settings
|
||||
bool SDMODE = false;
|
||||
bool NO_CODEC_REGISTRATION = false;
|
||||
|
||||
uint8_t DDR_TAPELEDS[TAPELED_DEVICE_COUNT][50][3] {};
|
||||
|
||||
@@ -49,12 +50,82 @@ namespace games::ddr {
|
||||
return SendMessage_real(hWnd, Msg, wParam, lParam);
|
||||
}
|
||||
|
||||
bool contains_only_ascii(const std::string& str) {
|
||||
for (auto c: str) {
|
||||
if (static_cast<unsigned char>(c) > 127) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
DDRGame::DDRGame() : Game("Dance Dance Revolution") {
|
||||
}
|
||||
|
||||
void DDRGame::register_codecs() {
|
||||
// find where spice.exe / spice64.exe is located
|
||||
const auto &spice_bin_path = libutils::module_file_name(nullptr).parent_path();
|
||||
|
||||
// find the com directory
|
||||
std::filesystem::path dir = "";
|
||||
if (MODULE_PATH == spice_bin_path) {
|
||||
// try: \com
|
||||
dir = spice_bin_path / "com";
|
||||
} else {
|
||||
// try: modules\..\com
|
||||
dir = MODULE_PATH / ".." / "com";
|
||||
}
|
||||
|
||||
if (fileutils::dir_exists(dir)) {
|
||||
log_info("ddr", "looking for codecs in this directory: {}", dir.string());
|
||||
} else {
|
||||
log_info("ddr", "codecs directory not found: {}", dir.string());
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &file : std::filesystem::directory_iterator(dir)) {
|
||||
const auto &filename = file.path().filename();
|
||||
const auto extension = strtolower(filename.extension().string());
|
||||
|
||||
if (extension != ".dll") {
|
||||
continue;
|
||||
}
|
||||
|
||||
log_info("ddr", "found DLL: {}, size: {} bytes", filename.string(), file.file_size());
|
||||
if (filename == "k-clvsd.dll" || filename.string().find("xactengine") == 0) {
|
||||
const std::wstring wcmd = L"regsvr32.exe /s \"" + file.path().wstring() + L"\"";
|
||||
const std::string cmd = "regsvr32.exe /s \"" + file.path().string() + "\"";
|
||||
|
||||
int result = 0;
|
||||
std::thread t([wcmd, &result]() {
|
||||
result = _wsystem(wcmd.c_str());
|
||||
});
|
||||
t.join();
|
||||
|
||||
if (result == 0) {
|
||||
log_info("ddr", "`{}` returned {}", cmd, result);
|
||||
} else {
|
||||
log_warning("ddr", "`{}` failed, returned {}", cmd, result);
|
||||
}
|
||||
|
||||
if (!contains_only_ascii(file.path().string())) {
|
||||
log_warning(
|
||||
"ddr",
|
||||
"BAD PATH ERROR\n\n\n"
|
||||
"!!! !!!\n"
|
||||
"!!! filesystem path to codec contains non-ASCII characters! !!!\n"
|
||||
"!!! this may cause the game to crash! !!!\n"
|
||||
"!!! !!!\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DDRGame::pre_attach() {
|
||||
if (!cfg::CONFIGURATOR_STANDALONE && avs::game::is_model("TDX")) {
|
||||
log_fatal(
|
||||
log_warning(
|
||||
"ddr",
|
||||
"BAD MODEL NAME ERROR\n\n\n"
|
||||
"!!! model name set to TDX, this is WRONG and will break your game !!!\n"
|
||||
@@ -65,6 +136,8 @@ namespace games::ddr {
|
||||
"!!! !!!\n"
|
||||
"!!! model name set to TDX, this is WRONG and will break your game !!!\n\n\n"
|
||||
);
|
||||
|
||||
log_fatal("ddr", "BAD MODEL NAME ERROR - TDX specified, should be MDX instead");
|
||||
}
|
||||
|
||||
if (!cfg::CONFIGURATOR_STANDALONE && avs::game::DEST[0] == 'U') {
|
||||
@@ -87,6 +160,17 @@ namespace games::ddr {
|
||||
"!!! !!!\n\n\n"
|
||||
);
|
||||
}
|
||||
|
||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||
if (!NO_CODEC_REGISTRATION) {
|
||||
this->register_codecs();
|
||||
} else {
|
||||
log_warning(
|
||||
"ddr",
|
||||
"skipping codec registration (-ddrnocodec), "
|
||||
"game may crash if you didn't register codecs before launching the game");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DDRGame::attach() {
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace games::ddr {
|
||||
|
||||
// settings
|
||||
extern bool SDMODE;
|
||||
extern bool NO_CODEC_REGISTRATION;
|
||||
|
||||
// Buffers to store RGB data for tape LEDs on gold cabinets
|
||||
const size_t TAPELED_DEVICE_COUNT = 11;
|
||||
@@ -18,5 +19,8 @@ namespace games::ddr {
|
||||
virtual void pre_attach() override;
|
||||
virtual void attach() override;
|
||||
virtual void detach() override;
|
||||
|
||||
private:
|
||||
void register_codecs();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,35 +22,31 @@
|
||||
static std::filesystem::path dll_path;
|
||||
static HMODULE iidx_module;
|
||||
static uintptr_t addr_hook_a = 0;
|
||||
static uintptr_t addr_hook_b = 0;
|
||||
static uintptr_t addr_textures = 0;
|
||||
static uintptr_t addr_camera_manager = 0;
|
||||
static uintptr_t addr_device_offset = 0;
|
||||
static uintptr_t addr_afp_texture_offset = 0;
|
||||
|
||||
typedef void **(__fastcall *camera_hook_a_t)(PBYTE);
|
||||
typedef LPDIRECT3DTEXTURE9 (*camera_hook_b_t)(void*, int);
|
||||
static camera_hook_a_t camera_hook_a_orig = nullptr;
|
||||
static camera_hook_b_t camera_hook_b_orig = nullptr;
|
||||
auto static hook_a_init = std::once_flag {};
|
||||
|
||||
static LPDIRECT3DDEVICE9EX device;
|
||||
static LPDIRECT3DTEXTURE9 *texture_a = nullptr;
|
||||
static LPDIRECT3DTEXTURE9 *texture_b = nullptr;
|
||||
static LPDIRECT3DTEXTURE9 *camera_texture_a = nullptr;
|
||||
static LPDIRECT3DTEXTURE9 *camera_texture_b = nullptr;
|
||||
static LPDIRECT3DTEXTURE9 *preview_texture_a = nullptr;
|
||||
static LPDIRECT3DTEXTURE9 *preview_texture_b = nullptr;
|
||||
|
||||
struct PredefinedHook {
|
||||
std::string pe_identifier;
|
||||
uintptr_t hook_a;
|
||||
uintptr_t hook_b;
|
||||
uintptr_t hook_textures;
|
||||
uintptr_t hook_camera_manager;
|
||||
uintptr_t hook_device_offset;
|
||||
uintptr_t hook_afp_texture_offset;
|
||||
};
|
||||
|
||||
PredefinedHook g_predefinedHooks[] =
|
||||
{
|
||||
// 27 003
|
||||
{ "5f713b52_6d4090", 0x005971b0, 0x005fb2c0, 0x04e49898, 0x000000d0 },
|
||||
// 27 010
|
||||
{ "5f713946_7f52b0", 0x006b89e0, 0x0071c950, 0x04fd08b8, 0x000000d0 },
|
||||
};
|
||||
PredefinedHook g_predefinedHooks[] = {};
|
||||
|
||||
const DWORD g_predefinedHooksLength = ARRAYSIZE(g_predefinedHooks);
|
||||
|
||||
@@ -75,8 +71,8 @@ namespace games::iidx {
|
||||
|
||||
if (sscanf(
|
||||
s.c_str(),
|
||||
"%i,%i,%i,%i",
|
||||
(int*)&addr_hook_a, (int*)&addr_hook_b, (int*)&addr_textures, (int*)&addr_device_offset) == 4) {
|
||||
"%i,%i,%i,%i,%i",
|
||||
(int*)&addr_hook_a, (int*)&addr_textures, (int*)&addr_camera_manager, (int*)&addr_device_offset, (int*)&addr_afp_texture_offset) == 5) {
|
||||
|
||||
return true;
|
||||
|
||||
@@ -101,8 +97,8 @@ namespace games::iidx {
|
||||
|
||||
// there are three different ways we try to hook the camera entry points
|
||||
// 1) user-provided override via cmd line
|
||||
// 2) predefined offsets using PE header matching (needed for iidx27 and few others)
|
||||
// 3) signature match (should work for most iidx28-31)
|
||||
// 2) predefined offsets using PE header matching
|
||||
// 3) signature match (should work for most iidx27-33)
|
||||
|
||||
// method 1: user provided
|
||||
if (TDJ_CAMERA_OVERRIDE.has_value() && parse_cmd_params()) {
|
||||
@@ -117,10 +113,11 @@ namespace games::iidx {
|
||||
for (DWORD i = 0; i < g_predefinedHooksLength; i++) {
|
||||
if (pe.compare(g_predefinedHooks[i].pe_identifier) == 0) {
|
||||
log_misc("iidx:camhook", "Found predefined addresses");
|
||||
addr_hook_a = g_predefinedHooks[i].hook_a;
|
||||
addr_hook_b = g_predefinedHooks[i].hook_b;
|
||||
addr_textures = g_predefinedHooks[i].hook_textures;
|
||||
addr_device_offset = g_predefinedHooks[i].hook_device_offset;
|
||||
addr_hook_a = g_predefinedHooks[i].hook_a;
|
||||
addr_textures = g_predefinedHooks[i].hook_textures;
|
||||
addr_camera_manager = g_predefinedHooks[i].hook_camera_manager;
|
||||
addr_device_offset = g_predefinedHooks[i].hook_device_offset;
|
||||
addr_afp_texture_offset = g_predefinedHooks[i].hook_afp_texture_offset;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@@ -131,8 +128,8 @@ namespace games::iidx {
|
||||
// --- addr_hook_a ---
|
||||
uint8_t *addr_hook_a_ptr = reinterpret_cast<uint8_t *>(find_pattern(
|
||||
iidx_module,
|
||||
"488BC4565741564883EC5048C740D8FEFFFFFF",
|
||||
"XXXXXXXXXXXXXXXXXXX",
|
||||
"E800000000488B8F0000000048894D",
|
||||
"X????XXX????XXX",
|
||||
0, 0));
|
||||
|
||||
if (addr_hook_a_ptr == nullptr) {
|
||||
@@ -141,24 +138,10 @@ namespace games::iidx {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
addr_hook_a = (uint64_t) (addr_hook_a_ptr - (uint8_t*) iidx_module);
|
||||
|
||||
// addr_hook_b
|
||||
uint8_t* addr_hook_b_ptr = reinterpret_cast<uint8_t *>(find_pattern(
|
||||
iidx_module,
|
||||
"E8000000004885C07439E8",
|
||||
"X????XXXXXX",
|
||||
0, 0));
|
||||
|
||||
if (addr_hook_b_ptr == nullptr) {
|
||||
log_warning("iidx:camhook", "failed to find hook: addr_hook_b");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// displace with the content of wildcard bytes
|
||||
int32_t disp_b = *((int32_t *) (addr_hook_b_ptr + 1));
|
||||
int32_t disp_a = *((int32_t *) (addr_hook_a_ptr + 1));
|
||||
|
||||
addr_hook_b = (addr_hook_b_ptr - (uint8_t*) iidx_module) + disp_b + 5;
|
||||
addr_hook_a = (addr_hook_a_ptr - (uint8_t*) iidx_module) + disp_a + 5;
|
||||
|
||||
// --- addr_textures ---
|
||||
uint8_t* addr_textures_ptr = reinterpret_cast<uint8_t *>(find_pattern(
|
||||
@@ -194,6 +177,28 @@ namespace games::iidx {
|
||||
|
||||
addr_textures = (addr_textures_ptr - (uint8_t*) iidx_module) + disp_textures + 7;
|
||||
|
||||
// --- addr_camera_manager ---
|
||||
uint8_t *addr_camera_manager_ptr = reinterpret_cast<uint8_t *>(find_pattern(
|
||||
iidx_module,
|
||||
"E80000000033FF488B58",
|
||||
"X????XXXXX",
|
||||
0, 0));
|
||||
|
||||
if (addr_camera_manager_ptr == nullptr) {
|
||||
log_warning("iidx:camhook", "failed to find hook: addr_get_camera_manager");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// displace with the content of wildcard bytes
|
||||
int32_t disp_camera_manager = *((int32_t *) (addr_camera_manager_ptr + 1));
|
||||
|
||||
addr_camera_manager_ptr = addr_camera_manager_ptr + disp_camera_manager + 5;
|
||||
|
||||
// once more to get the final address
|
||||
disp_camera_manager = *((int32_t *) (addr_camera_manager_ptr + 3));
|
||||
|
||||
addr_camera_manager = (addr_camera_manager_ptr - (uint8_t*) iidx_module) + disp_camera_manager + 7;
|
||||
|
||||
// addr_device_offset
|
||||
search_from = (addr_hook_a_ptr - (uint8_t*) iidx_module);
|
||||
uint8_t *addr_device_ptr = reinterpret_cast<uint8_t *>(find_pattern_from(
|
||||
@@ -201,39 +206,47 @@ namespace games::iidx {
|
||||
"488B89",
|
||||
"XXX",
|
||||
3, 0, search_from));
|
||||
addr_device_offset = *addr_device_ptr;
|
||||
|
||||
if (addr_textures_ptr == nullptr) {
|
||||
log_warning("iidx:camhook", "failed to find hook: addr_textures (part 3)");
|
||||
if (addr_device_ptr == nullptr) {
|
||||
log_warning("iidx:camhook", "failed to find hook: addr_device_ptr");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
addr_device_offset = *addr_device_ptr;
|
||||
|
||||
// --- addr_afp_texture_offset ---
|
||||
uint8_t *addr_afp_texture_ptr = reinterpret_cast<uint8_t *>(find_pattern(
|
||||
iidx_module,
|
||||
"488B41004885C07400488D5424",
|
||||
"XXX?XXXX?XXXX",
|
||||
0, 0));
|
||||
|
||||
if (addr_afp_texture_ptr == nullptr) {
|
||||
log_warning("iidx:camhook", "failed to find hook: addr_afp_texture_offset");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
addr_afp_texture_offset = *(addr_afp_texture_ptr + 3);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void **__fastcall camera_hook_a(PBYTE a1) {
|
||||
std::call_once(hook_a_init, [&]{
|
||||
device = *reinterpret_cast<LPDIRECT3DDEVICE9EX*>(a1 + addr_device_offset);
|
||||
auto const textures = *reinterpret_cast<LPDIRECT3DTEXTURE9**>((uint8_t*)iidx_module + addr_textures);
|
||||
auto const preview = *reinterpret_cast<LPDIRECT3DTEXTURE9**>((uint8_t*)iidx_module + addr_textures);
|
||||
auto const manager = reinterpret_cast<Camera::CCameraManager2*>((uint8_t*)iidx_module + addr_camera_manager);
|
||||
|
||||
texture_a = textures;
|
||||
texture_b = textures + 2;
|
||||
camera_texture_a = manager->cameras->a->d3d9_texture(addr_afp_texture_offset);
|
||||
camera_texture_b = manager->cameras->b->d3d9_texture(addr_afp_texture_offset);
|
||||
preview_texture_a = preview;
|
||||
preview_texture_b = preview + 2;
|
||||
|
||||
init_local_camera();
|
||||
});
|
||||
return camera_hook_a_orig(a1);
|
||||
}
|
||||
|
||||
static LPDIRECT3DTEXTURE9 camera_hook_b(void* a1, int idx) {
|
||||
if (idx == 0 && top_camera) {
|
||||
return top_camera->GetTexture();
|
||||
}
|
||||
if (idx == 1 && front_camera) {
|
||||
return front_camera->GetTexture();
|
||||
}
|
||||
return camera_hook_b_orig(a1, idx);
|
||||
}
|
||||
|
||||
bool create_hooks() {
|
||||
auto *hook_a_ptr = reinterpret_cast<uint16_t *>(
|
||||
reinterpret_cast<intptr_t>(iidx_module) + addr_hook_a);
|
||||
@@ -246,17 +259,6 @@ namespace games::iidx {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
auto *hook_b_ptr = reinterpret_cast<uint16_t *>(
|
||||
reinterpret_cast<intptr_t>(iidx_module) + addr_hook_b);
|
||||
|
||||
if (!detour::trampoline(
|
||||
reinterpret_cast<camera_hook_b_t>(hook_b_ptr),
|
||||
camera_hook_b,
|
||||
&camera_hook_b_orig)) {
|
||||
log_warning("iidx:camhook", "failed to trampoline hook_b");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -443,7 +445,7 @@ namespace games::iidx {
|
||||
}
|
||||
|
||||
IIDXLocalCamera* add_top_camera(IMFActivate* pActivate) {
|
||||
auto top_camera = new IIDXLocalCamera("top", TDJ_CAMERA_PREFER_16_9, pActivate, s_pD3DManager, device, texture_a);
|
||||
auto top_camera = new IIDXLocalCamera("top", TDJ_CAMERA_PREFER_16_9, pActivate, s_pD3DManager, device, camera_texture_a, preview_texture_a);
|
||||
if (top_camera->m_initialized) {
|
||||
LOCAL_CAMERA_LIST.push_back(top_camera);
|
||||
} else {
|
||||
@@ -454,7 +456,7 @@ namespace games::iidx {
|
||||
}
|
||||
|
||||
IIDXLocalCamera* add_front_camera(IMFActivate* pActivate) {
|
||||
auto front_camera = new IIDXLocalCamera("front", TDJ_CAMERA_PREFER_16_9, pActivate, s_pD3DManager, device, texture_b);
|
||||
auto front_camera = new IIDXLocalCamera("front", TDJ_CAMERA_PREFER_16_9, pActivate, s_pD3DManager, device, camera_texture_b, preview_texture_b);
|
||||
if (front_camera->m_initialized) {
|
||||
LOCAL_CAMERA_LIST.push_back(front_camera);
|
||||
} else {
|
||||
@@ -469,8 +471,8 @@ namespace games::iidx {
|
||||
if (result) {
|
||||
log_misc(
|
||||
"iidx:camhook",
|
||||
"found hooks:\n hook_a 0x{:x}\n hook_b 0x{:x}\n textures 0x{:x}\n device_offset 0x{:x}",
|
||||
addr_hook_a, addr_hook_b, addr_textures, addr_device_offset);
|
||||
"found hooks:\n hook_a 0x{:x}\n textures 0x{:x}\n camera_manager 0x{:x}\n device_offset 0x{:x}\n afp_texture_offset 0x{:x}",
|
||||
addr_hook_a, addr_textures, addr_camera_manager, addr_device_offset, addr_afp_texture_offset);
|
||||
result = create_hooks();
|
||||
if (result) {
|
||||
init_mf_library();
|
||||
|
||||
+113
-25
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "games/iidx/legacy_camera.h"
|
||||
|
||||
#include "hooks/audio/audio.h"
|
||||
#include "hooks/avshook.h"
|
||||
#include "hooks/cfgmgr32hook.h"
|
||||
#include "hooks/devicehook.h"
|
||||
@@ -22,11 +23,13 @@
|
||||
#include "misc/wintouchemu.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/deferlog.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/memutils.h"
|
||||
#include "util/sigscan.h"
|
||||
#include "util/utils.h"
|
||||
#include "launcher/signal.h"
|
||||
|
||||
#include "external/robin_hood.h"
|
||||
|
||||
@@ -50,7 +53,7 @@ namespace games::iidx {
|
||||
|
||||
// settings
|
||||
bool FLIP_CAMS = false;
|
||||
bool DISABLE_CAMS = false;
|
||||
std::optional<bool> DISABLE_CAMS;
|
||||
bool TDJ_CAMERA = false;
|
||||
bool TDJ_CAMERA_PREFER_16_9 = true;
|
||||
bool TDJ_MODE = false;
|
||||
@@ -232,6 +235,9 @@ namespace games::iidx {
|
||||
} else if (data.find(" W:touch: missing trigger:") != std::string::npos) {
|
||||
out.clear();
|
||||
return true;
|
||||
} else if (data.find("SuperstepSound: Audio device is not available") != std::string::npos) {
|
||||
deferredlogs::defer_error_messages(deferredlogs::SUPERSTEP_SOUND_ERROR_MESSAGE);
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -328,11 +334,6 @@ namespace games::iidx {
|
||||
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
|
||||
wintouchemu::hook_title_ends("beatmania IIDX", "main", avs::game::DLL_INSTANCE);
|
||||
}
|
||||
|
||||
// prevent crash on TDJ mode without correct DLL
|
||||
if (!GetModuleHandle("nvcuda.dll")) {
|
||||
DISABLE_CAMS = true;
|
||||
}
|
||||
}
|
||||
|
||||
// insert BI2X hooks
|
||||
@@ -372,7 +373,11 @@ namespace games::iidx {
|
||||
}
|
||||
}
|
||||
|
||||
apply_audio_hacks();
|
||||
if (hooks::audio::ENABLED) {
|
||||
apply_audio_hacks();
|
||||
} else {
|
||||
log_warning("iidx", "skipping audio hooks; disabled by user");
|
||||
}
|
||||
|
||||
// ASIO device hook
|
||||
RegCloseKey_orig = detour::iat_try(
|
||||
@@ -389,7 +394,10 @@ namespace games::iidx {
|
||||
"RegQueryValueExA", RegQueryValueExA_hook, avs::game::DLL_INSTANCE);
|
||||
|
||||
// check if cam hook should be enabled
|
||||
if (!DISABLE_CAMS) {
|
||||
if (!DISABLE_CAMS.has_value()) {
|
||||
log_fatal("iidx", "assertion failure - DISABLE_CAMS not set during attach");
|
||||
}
|
||||
if (!DISABLE_CAMS.value()) {
|
||||
init_legacy_camera_hook(FLIP_CAMS);
|
||||
}
|
||||
|
||||
@@ -399,19 +407,31 @@ namespace games::iidx {
|
||||
|
||||
void IIDXGame::pre_attach() {
|
||||
Game::pre_attach();
|
||||
auto options = games::get_options(eamuse_get_game());
|
||||
|
||||
// environment variables must be set before the DLL is loaded as the VC++ runtime copies all
|
||||
// environment variables at startup
|
||||
if (DISABLE_CAMS) {
|
||||
SetEnvironmentVariable("CONNECT_CAMERA", "0");
|
||||
}
|
||||
|
||||
if (SCREEN_MODE.has_value()) {
|
||||
log_misc("iidx", "SCREEN_MODE env var set to {}", SCREEN_MODE.value().c_str());
|
||||
SetEnvironmentVariable("SCREEN_MODE", SCREEN_MODE.value().c_str());
|
||||
}
|
||||
|
||||
// check for cab camera access for the second time (first time was in launcher.cpp)
|
||||
// this time, we are inside -iidx module hook, which means the user is likely NOT on a cab
|
||||
// therefore, start with cams OFF by default, and allow user to forcibly override to ON
|
||||
if (!games::iidx::DISABLE_CAMS.has_value()) {
|
||||
games::iidx::DISABLE_CAMS = true;
|
||||
if (options->at(launcher::Options::IIDXCabCamAccess).is_active() &&
|
||||
options->at(launcher::Options::IIDXCabCamAccess).value_text() == "on") {
|
||||
games::iidx::DISABLE_CAMS = false;
|
||||
}
|
||||
if (games::iidx::DISABLE_CAMS.value()) {
|
||||
log_misc("iidx", "CONNECT_CAMERA env var set to 0");
|
||||
SetEnvironmentVariable("CONNECT_CAMERA", "0");
|
||||
}
|
||||
}
|
||||
|
||||
// windowed subscreen, enabled by default, unless turned off by user
|
||||
auto options = games::get_options(eamuse_get_game());
|
||||
if (GRAPHICS_WINDOWED && !options->at(launcher::Options::spice2x_IIDXNoSub).value_bool()) {
|
||||
GRAPHICS_IIDX_WSUB = true;
|
||||
}
|
||||
@@ -422,7 +442,7 @@ namespace games::iidx {
|
||||
|
||||
// check bad model name
|
||||
if (!cfg::CONFIGURATOR_STANDALONE && avs::game::is_model("TDJ")) {
|
||||
log_fatal(
|
||||
log_warning(
|
||||
"iidx",
|
||||
"BAD MODEL NAME ERROR\n\n\n"
|
||||
"!!! model name set to TDJ, this is WRONG and will break your game !!!\n"
|
||||
@@ -431,7 +451,7 @@ namespace games::iidx {
|
||||
"!!! please do the following instead: !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! Revert your changes to XML file so it says !!!\n"
|
||||
"!!! <model __type=\"str\">LDJ</model> !!!\n"
|
||||
"!!! <model __type=\"str\">LDJ</model> !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! In SpiceCfg, enable 'IIDX TDJ Mode' or provide -iidxtdj flag !!!\n"
|
||||
"!!! in command line !!!\n"
|
||||
@@ -440,6 +460,34 @@ namespace games::iidx {
|
||||
"!!! !!!\n"
|
||||
"!!! model name set to TDJ, this is WRONG and will break your game !!!\n\n\n"
|
||||
);
|
||||
|
||||
log_fatal("iidx", "BAD MODEL NAME ERROR - TDJ specified, must be LDJ instead");
|
||||
}
|
||||
|
||||
// check -monitor + TDJ mode
|
||||
if (!GRAPHICS_WINDOWED &&
|
||||
options->at(launcher::Options::DisplayAdapter).is_active() &&
|
||||
TDJ_MODE) {
|
||||
log_warning(
|
||||
"iidx",
|
||||
"\n\n!!! using -monitor option with TDJ 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 NOT use -monitor and instead set the !!!\n"
|
||||
"!!! primary monitor in Windows settings before launching the game !!!\n\n"
|
||||
);
|
||||
|
||||
deferredlogs::defer_error_messages({
|
||||
"-monitor option is NOT recommended when running with TDJ mode",
|
||||
" 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",
|
||||
" recommended fix is to NOT use -monitor and instead set the primary",
|
||||
" monitor in Windows settings before launching the game"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -762,26 +810,32 @@ namespace games::iidx {
|
||||
0);
|
||||
|
||||
// attempt to detect ASIO support
|
||||
// <=24 : 32-bit only
|
||||
// 25-26: has neither (no patch needed - WASAPI Exclusive by default)
|
||||
// 27-30: has both (envvar will be respected, ASIO or WASAPI)
|
||||
// 31+: only has XONAR (ASIO by default, signature patch can be used to force WASAPI - for now)
|
||||
// 31-32: only has XONAR (ASIO by default, signature patch can be used to force WASAPI - for now)
|
||||
// 33 : early versions only have XONAR, but later datecodes have both; SOUND_OUTPUT_DEVICE
|
||||
// returned and it works again when set as env var
|
||||
|
||||
log_info(
|
||||
"iidx",
|
||||
"has_SOUND_OUTPUT_DEVICE: {}, has_XONAR_SOUND_CARD: {}",
|
||||
(has_SOUND_OUTPUT_DEVICE != 0),
|
||||
(has_XONAR_SOUND_CARD != 0));
|
||||
|
||||
if (!has_SOUND_OUTPUT_DEVICE && !has_XONAR_SOUND_CARD) {
|
||||
// iidx 25-26
|
||||
log_info("iidx", "This game only uses WASAPI audio engine");
|
||||
return;
|
||||
}
|
||||
|
||||
if (has_SOUND_OUTPUT_DEVICE && has_XONAR_SOUND_CARD) {
|
||||
log_info("iidx", "This game accepts SOUND_OUTPUT_DEVICE environment variable");
|
||||
return;
|
||||
}
|
||||
|
||||
log_info("iidx", "This game supports ASIO but does not accept SOUND_OUTPUT_DEVICE environment variable");
|
||||
|
||||
if (SOUND_OUTPUT_DEVICE_IN_EFFECT.has_value() && SOUND_OUTPUT_DEVICE_IN_EFFECT.value() == "wasapi") {
|
||||
// if the game doesn't accept SOUND_OUTPUT_DEVICE, patch game to force wasapi
|
||||
if (!has_SOUND_OUTPUT_DEVICE &&
|
||||
SOUND_OUTPUT_DEVICE_IN_EFFECT.has_value() &&
|
||||
SOUND_OUTPUT_DEVICE_IN_EFFECT.value() == "wasapi") {
|
||||
intptr_t result = replace_pattern(
|
||||
avs::game::DLL_INSTANCE,
|
||||
"FF5008E8????ECFF83780803740D",
|
||||
"FF5008E8??????FF83780803740D",
|
||||
"??????BB00000000EB169090????",
|
||||
0, 0);
|
||||
|
||||
@@ -798,6 +852,40 @@ namespace games::iidx {
|
||||
}
|
||||
}
|
||||
|
||||
// patch iidx32+ for asio compatibility
|
||||
// only do this if NOT wasapi (as opposed to checking if it's asio)
|
||||
// the patch is only really needed for (some) non-XONAR devices but since people sometimes disguise
|
||||
// other devices as a XONAR, don't check for the exact string (common ASIO workaround for INF)
|
||||
if (avs::game::is_ext(2024090100, MAXINT) &&
|
||||
!(SOUND_OUTPUT_DEVICE_IN_EFFECT.has_value() &&
|
||||
SOUND_OUTPUT_DEVICE_IN_EFFECT.value() == "wasapi")) {
|
||||
|
||||
// in iidx32 final:
|
||||
// ff 50 08 call QWORD PTR [rax+0x8] ; ASIO instance AddRef
|
||||
// 48 8b 4b 08 mov rcx,QWORD PTR [rbx+0x8]
|
||||
// 48 8b 01 mov rax,QWORD PTR [rcx]
|
||||
// ff 50 08 call QWORD PTR [rax+0x8] ; ASIO instance AddRef
|
||||
|
||||
intptr_t result = replace_pattern(
|
||||
avs::game::DLL_INSTANCE,
|
||||
"FF50????????????????FF50??4533C94533C0418D51",
|
||||
"????????????????????909090??????????????????",
|
||||
0, 0);
|
||||
|
||||
if (result == 0) {
|
||||
log_warning(
|
||||
"iidx",
|
||||
"Failed to apply ASIO compatibility fix for iidx32+. "
|
||||
"Unless patches are applied, your ASIO device ({}) may hang or fail to work",
|
||||
ASIO_DRIVER->c_str());
|
||||
} else {
|
||||
log_info(
|
||||
"iidx",
|
||||
"Successfully applied ASIO compatibility fix for iidx32+ using signature matching @ 0x{:x}.",
|
||||
result);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace games::iidx {
|
||||
// settings
|
||||
|
||||
extern bool FLIP_CAMS;
|
||||
extern bool DISABLE_CAMS;
|
||||
extern std::optional<bool> DISABLE_CAMS;
|
||||
extern bool TDJ_CAMERA;
|
||||
extern bool TDJ_CAMERA_PREFER_16_9;
|
||||
extern std::optional<std::string> TDJ_CAMERA_OVERRIDE;
|
||||
|
||||
@@ -213,6 +213,8 @@ namespace games::iidx {
|
||||
}
|
||||
|
||||
void init_legacy_camera_hook(bool flip_cams) {
|
||||
log_info("iidx::cam", "initializing legacy camera hook");
|
||||
|
||||
should_flip_cams = flip_cams;
|
||||
|
||||
// camera media framework hook
|
||||
|
||||
@@ -46,14 +46,17 @@ namespace games::iidx {
|
||||
IMFActivate *pActivate,
|
||||
IDirect3DDeviceManager9 *pD3DManager,
|
||||
LPDIRECT3DDEVICE9EX device,
|
||||
LPDIRECT3DTEXTURE9 *texture_target
|
||||
LPDIRECT3DTEXTURE9 *camera_texture_target,
|
||||
LPDIRECT3DTEXTURE9 *preview_texture_target
|
||||
):
|
||||
m_nRefCount(1),
|
||||
m_name(name),
|
||||
m_prefer_16_by_9(prefer_16_by_9),
|
||||
m_device(device),
|
||||
m_texture_target(texture_target),
|
||||
m_texture_original(*texture_target)
|
||||
m_camera_texture_target(camera_texture_target),
|
||||
m_preview_texture_target(preview_texture_target),
|
||||
m_camera_texture_original(*camera_texture_target),
|
||||
m_preview_texture_original(*preview_texture_target)
|
||||
{
|
||||
InitializeCriticalSection(&m_critsec);
|
||||
|
||||
@@ -85,12 +88,19 @@ namespace games::iidx {
|
||||
&m_pwszSymbolicLink,
|
||||
&m_cchSymbolicLink
|
||||
);
|
||||
if (FAILED(hr)) { goto done; }
|
||||
if (FAILED(hr)) {
|
||||
log_warning("iidx:camhook", "[{}] GetAllocatedString failed with {:#x}", m_name, (ULONG)hr);
|
||||
goto done;
|
||||
}
|
||||
|
||||
log_misc("iidx:camhook", "[{}] Symlink: {}", m_name, GetSymLink());
|
||||
|
||||
// Create the media source object.
|
||||
hr = pActivate->ActivateObject(IID_PPV_ARGS(&m_pSource));
|
||||
if (FAILED(hr)) { goto done; }
|
||||
if (FAILED(hr)) {
|
||||
log_warning("iidx:camhook", "[{}] ActivateObject failed with {:#x}", m_name, (ULONG)hr);
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Retain reference to the camera
|
||||
m_pSource->AddRef();
|
||||
@@ -98,13 +108,22 @@ namespace games::iidx {
|
||||
|
||||
// Create an attribute store to hold initialization settings.
|
||||
hr = WrappedMFCreateAttributes(&pAttributes, 2);
|
||||
if (FAILED(hr)) { goto done; }
|
||||
if (FAILED(hr)) {
|
||||
log_warning("iidx:camhook", "[{}] MFCreateAttributes failed with {:#x}", m_name, (ULONG)hr);
|
||||
goto done;
|
||||
}
|
||||
|
||||
hr = pAttributes->SetUnknown(MF_SOURCE_READER_D3D_MANAGER, pD3DManager);
|
||||
if (FAILED(hr)) { goto done; }
|
||||
if (FAILED(hr)) {
|
||||
log_warning("iidx:camhook", "[{}] SetUnknown(MF_SOURCE_READER_D3D_MANAGER) failed with {:#x}", m_name, (ULONG)hr);
|
||||
goto done;
|
||||
}
|
||||
|
||||
hr = pAttributes->SetUINT32(MF_SOURCE_READER_DISABLE_DXVA, FALSE);
|
||||
if (FAILED(hr)) { goto done; }
|
||||
if (FAILED(hr)) {
|
||||
log_warning("iidx:camhook", "[{}] SetUINT32(MF_SOURCE_READER_DISABLE_DXVA) failed with {:#x}", m_name, (ULONG)hr);
|
||||
goto done;
|
||||
}
|
||||
|
||||
// TODO: Color space conversion
|
||||
// if (SUCCEEDED(hr)) {
|
||||
@@ -120,11 +139,18 @@ namespace games::iidx {
|
||||
pAttributes,
|
||||
&m_pSourceReader
|
||||
);
|
||||
if (FAILED(hr)) { goto done; }
|
||||
if (FAILED(hr)) {
|
||||
log_warning("iidx:camhook", "[{}] MFCreateSourceReaderFromMediaSource failed with {:#x}", m_name, (ULONG)hr);
|
||||
goto done;
|
||||
}
|
||||
|
||||
log_misc("iidx:camhook", "[{}] Created source reader", m_name);
|
||||
|
||||
hr = InitTargetTexture();
|
||||
if (FAILED(hr)) { goto done; }
|
||||
if (FAILED(hr)) {
|
||||
log_warning("iidx:camhook", "[{}] InitTargetTexture failed with {:#x}", m_name, (ULONG)hr);
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Camera should be still usable even if camera control is not supported
|
||||
InitCameraControl();
|
||||
@@ -134,7 +160,7 @@ namespace games::iidx {
|
||||
m_initialized = true;
|
||||
log_misc("iidx:camhook", "[{}] Initialized", m_name);
|
||||
} else {
|
||||
log_warning("iidx:camhook", "[{}] Failed to create camera: {}", m_name, hr);
|
||||
log_warning("iidx:camhook", "[{}] Failed to create camera: {:#x}", m_name, (ULONG)hr);
|
||||
}
|
||||
SafeRelease(&pAttributes);
|
||||
LeaveCriticalSection(&m_critsec);
|
||||
@@ -165,7 +191,7 @@ namespace games::iidx {
|
||||
|
||||
if (FAILED(hr)) {
|
||||
if (hr != MF_E_NO_MORE_TYPES) {
|
||||
log_warning("iidx:camhook", "[{}] Cannot get media type {} {}", m_name, i, hr);
|
||||
log_warning("iidx:camhook", "[{}] Cannot get media type {} {:#x}", m_name, i, (ULONG)hr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -584,7 +610,7 @@ namespace games::iidx {
|
||||
* Return values:
|
||||
* S_OK: this is a "better" media type than the existing one
|
||||
* S_FALSE: valid media type, but not "better"
|
||||
* E_*: invalid meia type
|
||||
* E_*: invalid media type
|
||||
*/
|
||||
HRESULT IIDXLocalCamera::TryMediaType(IMFMediaType *pType, UINT32 *pBestWidth, double *pBestFrameRate) {
|
||||
HRESULT hr = S_OK;
|
||||
@@ -595,13 +621,13 @@ namespace games::iidx {
|
||||
hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
log_warning("iidx:camhook", "[{}] Failed to get subtype: {}", m_name, hr);
|
||||
log_warning("iidx:camhook", "[{}] Failed to get subtype: {:#x}", m_name, (ULONG)hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height);
|
||||
if (FAILED(hr)) {
|
||||
log_warning("iidx:camhook", "[{}] Failed to get frame size: {}", m_name, hr);
|
||||
log_warning("iidx:camhook", "[{}] Failed to get frame size: {:#x}", m_name, (ULONG)hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
@@ -619,7 +645,7 @@ namespace games::iidx {
|
||||
(UINT32*)&frameRate.Denominator
|
||||
);
|
||||
if (FAILED(hr)) {
|
||||
log_warning("iidx:camhook", "[{}] Failed to get frame rate: {}", m_name, hr);
|
||||
log_warning("iidx:camhook", "[{}] Failed to get frame rate: {:#x}", m_name, (ULONG)hr);
|
||||
return hr;
|
||||
}
|
||||
double frameRateValue = frameRate.Numerator / frameRate.Denominator;
|
||||
@@ -668,7 +694,8 @@ namespace games::iidx {
|
||||
if (FAILED(hr)) { goto done; }
|
||||
|
||||
// Make the game use this new texture as camera stream source
|
||||
*m_texture_target = m_texture;
|
||||
*m_camera_texture_target = m_texture;
|
||||
*m_preview_texture_target = m_texture;
|
||||
m_active = TRUE;
|
||||
|
||||
// Create texture for colour conversion
|
||||
@@ -696,7 +723,7 @@ namespace games::iidx {
|
||||
if (SUCCEEDED(hr)) {
|
||||
log_misc("iidx:camhook", "[{}] Created texture", m_name);
|
||||
} else {
|
||||
log_warning("iidx:camhook", "[{}] Failed to create texture: {}", m_name, hr);
|
||||
log_warning("iidx:camhook", "[{}] Failed to create texture: {:#x}", m_name, (ULONG)hr);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
@@ -785,7 +812,7 @@ namespace games::iidx {
|
||||
FlushDrawCommands();
|
||||
|
||||
if (FAILED(hr)) {
|
||||
log_warning("iidx:camhook", "Error in DrawSample {}", hr);
|
||||
log_warning("iidx:camhook", "Error in DrawSample {:#x}", (ULONG)hr);
|
||||
}
|
||||
SafeRelease(&pCameraSurf);
|
||||
LeaveCriticalSection(&m_critsec);
|
||||
@@ -837,7 +864,8 @@ namespace games::iidx {
|
||||
|
||||
ULONG uCount = InterlockedDecrement(&m_nRefCount);
|
||||
|
||||
*m_texture_target = m_texture_original;
|
||||
*m_camera_texture_target = m_camera_texture_original;
|
||||
*m_preview_texture_target = m_preview_texture_original;
|
||||
|
||||
for (size_t i = 0; i < m_mediaTypeInfos.size(); i++) {
|
||||
SafeRelease(&(m_mediaTypeInfos.at(i).p_mediaType));
|
||||
|
||||
@@ -66,6 +66,25 @@ extern std::string CAMERA_CONTROL_LABELS[];
|
||||
extern std::string DRAW_MODE_LABELS[];
|
||||
|
||||
namespace games::iidx {
|
||||
namespace Camera {
|
||||
struct PlayVideoCamera {
|
||||
IDirect3DTexture9** d3d9_texture(const uintptr_t offset) {
|
||||
auto const afp_texture = *reinterpret_cast<uint8_t**>
|
||||
(reinterpret_cast<uint8_t*>(this) + offset);
|
||||
return reinterpret_cast<IDirect3DTexture9**>(afp_texture + 0x8);
|
||||
}
|
||||
};
|
||||
|
||||
struct CCameraManager2 {
|
||||
using camera_pointers = struct {
|
||||
PlayVideoCamera* a;
|
||||
PlayVideoCamera* b;
|
||||
};
|
||||
void* vftbl;
|
||||
camera_pointers* cameras;
|
||||
};
|
||||
}
|
||||
|
||||
class IIDXLocalCamera {
|
||||
protected:
|
||||
virtual ~IIDXLocalCamera() {};
|
||||
@@ -98,8 +117,9 @@ namespace games::iidx {
|
||||
// DirectX9 DeviceEx
|
||||
LPDIRECT3DDEVICE9EX m_device;
|
||||
|
||||
// Address to hook camera texture onto the game
|
||||
LPDIRECT3DTEXTURE9 *m_texture_target = nullptr;
|
||||
// Address to hook camera textures onto the game
|
||||
LPDIRECT3DTEXTURE9 *m_camera_texture_target = nullptr;
|
||||
LPDIRECT3DTEXTURE9 *m_preview_texture_target = nullptr;
|
||||
|
||||
// Target texture (to be shown in the game)
|
||||
LPDIRECT3DTEXTURE9 m_texture = nullptr;
|
||||
@@ -117,8 +137,9 @@ namespace games::iidx {
|
||||
LPDIRECT3DTEXTURE9 m_transformResultTexture = nullptr;
|
||||
IDirect3DSurface9 *m_pTransformResultSurf = nullptr;
|
||||
|
||||
// Storing original texture for clean up
|
||||
LPDIRECT3DTEXTURE9 m_texture_original = nullptr;
|
||||
// Storing original textures for clean up
|
||||
LPDIRECT3DTEXTURE9 m_camera_texture_original = nullptr;
|
||||
LPDIRECT3DTEXTURE9 m_preview_texture_original = nullptr;
|
||||
|
||||
IAMCameraControl *m_pCameraControl = nullptr;
|
||||
|
||||
@@ -154,7 +175,8 @@ namespace games::iidx {
|
||||
IMFActivate *pActivate,
|
||||
IDirect3DDeviceManager9 *pD3DManager,
|
||||
LPDIRECT3DDEVICE9EX device,
|
||||
LPDIRECT3DTEXTURE9 *texture_target
|
||||
LPDIRECT3DTEXTURE9 *camera_texture_target,
|
||||
LPDIRECT3DTEXTURE9 *preview_texture_target
|
||||
);
|
||||
LPDIRECT3DTEXTURE9 GetTexture();
|
||||
ULONG Release();
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "ccj/io.h"
|
||||
#include "qks/io.h"
|
||||
#include "mfg/io.h"
|
||||
#include "pc/io.h"
|
||||
|
||||
namespace games {
|
||||
|
||||
@@ -313,6 +314,15 @@ namespace games {
|
||||
buttons.insert({ mfg, mfg::get_buttons() });
|
||||
buttons_help.insert({ mfg, mfg::get_buttons_help() });
|
||||
file_hints[mfg].emplace_back("game/MFGClient_Data");
|
||||
|
||||
// Polaris Chord
|
||||
const std::string pc("Polaris Chord");
|
||||
games.push_back(pc);
|
||||
buttons.insert({ pc, pc::get_buttons() });
|
||||
buttons_help.insert({ pc, pc::get_buttons_help() });
|
||||
analogs.insert({ pc, pc::get_analogs() });
|
||||
lights.insert({ pc, pc::get_lights() });
|
||||
file_hints[pc].emplace_back("game/svm_Data");
|
||||
}
|
||||
|
||||
const std::vector<std::string> &get_games() {
|
||||
|
||||
@@ -169,7 +169,7 @@ namespace games::jb {
|
||||
const auto current_path = std::filesystem::current_path();
|
||||
log_misc("jubeat", "current working directory: {}", current_path.string());
|
||||
if (current_path.parent_path() == current_path.root_path()) {
|
||||
log_fatal(
|
||||
log_warning(
|
||||
"jubeat",
|
||||
"\n\nInvalid path error; jubeat cannot run from a directory in the drive root\n"
|
||||
"The game will overflow the stack and silently fail to boot\n\n"
|
||||
@@ -179,6 +179,10 @@ namespace games::jb {
|
||||
"To fix this, create a new directory and move ALL game files there.\n\n"
|
||||
"Your current working directory: {}\n",
|
||||
current_path.string());
|
||||
|
||||
log_fatal(
|
||||
"jubeat",
|
||||
"Invalid path error; jubeat cannot run from a directory in the drive root");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "avs/game.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "misc/wintouchemu.h"
|
||||
#include "touch/touch.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/libutils.h"
|
||||
@@ -32,6 +33,8 @@ namespace games::mfc {
|
||||
static uint8_t CARD_TYPE = 0;
|
||||
static uint8_t CARD_UID[8];
|
||||
|
||||
bool HG_MODE = false;
|
||||
|
||||
typedef int (__cdecl *inifile_param_num_t)(const char *, int *);
|
||||
static inifile_param_num_t inifile_param_num_real;
|
||||
|
||||
@@ -50,8 +53,10 @@ namespace games::mfc {
|
||||
static void __cdecl touch_init(int width, int height) {
|
||||
log_info("mfc", "call touch_init(width: {}, height: {})", width, height);
|
||||
|
||||
// attach touch module
|
||||
if (!TOUCH_ATTACHED) {
|
||||
if (HG_MODE) {
|
||||
wintouchemu::ADD_TOUCH_FLAG_PRIMARY = true;
|
||||
wintouchemu::hook("MFC9", avs::game::DLL_INSTANCE);
|
||||
} else if (!TOUCH_ATTACHED) { // attach touch module
|
||||
|
||||
// store touch size specification
|
||||
TOUCH_MAX_X = width;
|
||||
@@ -288,6 +293,21 @@ namespace games::mfc {
|
||||
MFCGame::MFCGame() : Game("Mahjong Fight Club") {
|
||||
}
|
||||
|
||||
static bool spam_remover(void *user, const std::string &data, logger::Style style, std::string &out) {
|
||||
if (data.empty() || data[0] != '[') {
|
||||
return false;
|
||||
}
|
||||
if (data.find("W:mutex: lock: mid 00000000 != AVS_DTYPE_MUTEX") != std::string::npos) {
|
||||
out = "";
|
||||
return true;
|
||||
}
|
||||
if (data.find("W:mutex: unlock: mid 00000000 != AVS_DTYPE_MUTEX") != std::string::npos) {
|
||||
out = "";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MFCGame::attach() {
|
||||
Game::attach();
|
||||
|
||||
@@ -308,6 +328,17 @@ namespace games::mfc {
|
||||
auto allinone_module = libutils::try_module("allinone.dll");
|
||||
auto system_module = libutils::try_module("system.dll");
|
||||
|
||||
// Mahjong Fight Club - 2025?
|
||||
// they removed allinone.dll and moved i/o into system.dll
|
||||
if (allinone_module == nullptr) {
|
||||
log_misc("mfc", "using system.dll instead of allinone.dll for i/o hooks");
|
||||
allinone_module = system_module;
|
||||
|
||||
if (avs::game::SPEC[0] == 'F') {
|
||||
HG_MODE = true;
|
||||
}
|
||||
}
|
||||
|
||||
// network fix
|
||||
detour::iat_try("?nic_dhcp_maybe_exist@@YAEXZ", nic_dhcp_maybe_exist, system_module);
|
||||
|
||||
@@ -378,5 +409,7 @@ namespace games::mfc {
|
||||
detour::inline_hook((void *) mouse_utl_step, libutils::try_proc(
|
||||
allinone_module, "?mouse_utl_step@@YAXXZ"));
|
||||
}
|
||||
|
||||
logger::hook_add(spam_remover, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
namespace games::mfc {
|
||||
|
||||
extern bool HG_MODE;
|
||||
|
||||
struct joystick_state {
|
||||
bool up = false;
|
||||
bool down = false;
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace games::mfg {
|
||||
SetEnvironmentVariableA("VFG_CABINET_TYPE", MFG_CABINET_TYPE.c_str());
|
||||
|
||||
// add card reader
|
||||
portName = MFG_CABINET_TYPE == "UKS" ? L"\\\\.\\COM1" : L"\\\\.\\COM3";
|
||||
portName = (MFG_CABINET_TYPE == "UKS") ? std::wstring(L"\\\\.\\COM1") : std::wstring(L"\\\\.\\COM3");
|
||||
acioHandle = new acioemu::ACIOHandle(portName.c_str(), 1);
|
||||
devicehook_init_trampoline();
|
||||
devicehook_add(acioHandle);
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace games::otoca {
|
||||
bool kb_insert_press = false;
|
||||
|
||||
// eamio keypress
|
||||
kb_insert_press |= eamuse_get_keypad_state(0) & (1 << EAM_IO_INSERT);
|
||||
kb_insert_press |= static_cast<bool>(eamuse_get_keypad_state(0) & (1 << EAM_IO_INSERT));
|
||||
|
||||
// check for card
|
||||
if (CARD_DATA == nullptr && (eamuse_card_insert_consume(1, 0) || kb_insert_press)) {
|
||||
|
||||
@@ -0,0 +1,739 @@
|
||||
#include "bi2x_hook.h"
|
||||
|
||||
#include "util/execexe.h"
|
||||
#include "util/logging.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "games/io.h"
|
||||
#include "io.h"
|
||||
#include "util/tapeled.h"
|
||||
#include "pc.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
namespace games::pc {
|
||||
bool PC_KNOB_MODE = false;
|
||||
|
||||
constexpr float RELATIVE_DECAY = 0.02f;
|
||||
struct FADER_RELATIVE {
|
||||
// ranges from 0.f to 1.f
|
||||
float previous_raw = 0.5f;
|
||||
|
||||
// ranges from -1.f to +1.f
|
||||
float effective_value = 0.f;
|
||||
};
|
||||
|
||||
FADER_RELATIVE fader_l_relative;
|
||||
FADER_RELATIVE fader_r_relative;
|
||||
|
||||
float calculate_analog_raw_delta(float old_value, float new_value) {
|
||||
// assumed that values are between 0.f and 1.f
|
||||
|
||||
float delta = 0.f;
|
||||
if ((old_value < 0.25f) && (0.75f < new_value)) {
|
||||
// wrapped around: going left
|
||||
delta = -((1.f - new_value) + old_value);
|
||||
|
||||
} else if ((new_value < 0.25f) && (0.75f < old_value)) {
|
||||
// wrapped around: going right
|
||||
delta = (1.f - old_value) + new_value;
|
||||
|
||||
} else {
|
||||
delta = new_value - old_value;
|
||||
}
|
||||
return delta;
|
||||
}
|
||||
|
||||
float apply_decay(float val) {
|
||||
// return to center over time
|
||||
if (RELATIVE_DECAY < val) {
|
||||
return val - RELATIVE_DECAY;
|
||||
} else if (val < -RELATIVE_DECAY) {
|
||||
return val + RELATIVE_DECAY;
|
||||
} else {
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
float get_fader_knob_mode_value(float raw_analog, FADER_RELATIVE &fader) {
|
||||
// add new value...
|
||||
const float delta = calculate_analog_raw_delta(fader.previous_raw, raw_analog);
|
||||
fader.effective_value += (delta * 20.f);
|
||||
fader.previous_raw = raw_analog;
|
||||
|
||||
// capture the result after adding new input value
|
||||
fader.effective_value = CLAMP(fader.effective_value, -1.f, 1.f);
|
||||
const float result = fader.effective_value;
|
||||
|
||||
// post-process for next iteration: apply decay (return to center over time)
|
||||
fader.effective_value = apply_decay(fader.effective_value);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* class definitions
|
||||
*/
|
||||
|
||||
struct AIO_SCI_COMM {
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB2 {
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1 {
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1__SETTING {
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_WRFIRM {
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1__INPUT
|
||||
{
|
||||
uint8_t DevIoCounter;
|
||||
uint8_t bExIoAErr;
|
||||
uint8_t bExIoBErr;
|
||||
uint8_t bPcPowerOn;
|
||||
uint8_t bPcPowerCheck;
|
||||
uint8_t bCoin1Jam;
|
||||
uint8_t bCoin2Jam;
|
||||
uint8_t bCoin3Jam;
|
||||
uint8_t bCoin4Jam;
|
||||
uint8_t Coin1Count;
|
||||
uint8_t Coin2Count;
|
||||
uint8_t Coin3Count;
|
||||
uint8_t Coin4Count;
|
||||
uint16_t AnalogCh1;
|
||||
uint16_t AnalogCh2;
|
||||
uint16_t AnalogCh3;
|
||||
uint16_t AnalogCh4;
|
||||
uint8_t CN8_8;
|
||||
uint8_t CN8_9;
|
||||
uint8_t CN8_10;
|
||||
uint8_t CN9_8;
|
||||
uint8_t CN9_9;
|
||||
uint8_t CN9_10;
|
||||
uint8_t CN11_11;
|
||||
uint8_t CN11_12;
|
||||
uint8_t CN11_13;
|
||||
uint8_t CN11_14;
|
||||
uint8_t CN11_15;
|
||||
uint8_t CN11_16;
|
||||
uint8_t CN11_17;
|
||||
uint8_t CN11_18;
|
||||
uint8_t CN11_19;
|
||||
uint8_t CN11_20;
|
||||
uint8_t CN12_11;
|
||||
uint8_t CN12_12;
|
||||
uint8_t CN12_13;
|
||||
uint8_t CN12_14;
|
||||
uint8_t CN12_15;
|
||||
uint8_t CN12_16;
|
||||
uint8_t CN12_17;
|
||||
uint8_t CN12_18;
|
||||
uint8_t CN12_19;
|
||||
uint8_t CN12_20;
|
||||
uint8_t CN12_21;
|
||||
uint8_t CN12_22;
|
||||
uint8_t CN12_23;
|
||||
uint8_t CN12_24;
|
||||
uint8_t CN15_3;
|
||||
uint8_t CN15_4;
|
||||
uint8_t CN15_5;
|
||||
uint8_t CN15_6;
|
||||
uint8_t CN15_7;
|
||||
uint8_t CN15_8;
|
||||
uint8_t CN15_9;
|
||||
uint8_t CN15_10;
|
||||
uint8_t CN15_11;
|
||||
uint8_t CN15_12;
|
||||
uint8_t CN15_13;
|
||||
uint8_t CN15_14;
|
||||
uint8_t CN15_15;
|
||||
uint8_t CN15_16;
|
||||
uint8_t CN15_17;
|
||||
uint8_t CN15_18;
|
||||
uint8_t CN15_19;
|
||||
uint8_t CN15_20;
|
||||
uint8_t CN19_8;
|
||||
uint8_t CN19_9;
|
||||
uint8_t CN19_10;
|
||||
uint8_t CN19_11;
|
||||
uint8_t CN19_12;
|
||||
uint8_t CN19_13;
|
||||
uint8_t CN19_14;
|
||||
uint8_t CN19_15;
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1__INPUTDATA {
|
||||
uint8_t data[247];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1__OUTPUTDATA {
|
||||
uint8_t data[48];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1__ICNPIN {
|
||||
uint16_t Ain[4];
|
||||
uint64_t CnPin;
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1__DEVSTATUS {
|
||||
uint8_t InputCounter;
|
||||
uint8_t OutputCounter;
|
||||
uint8_t IoResetCounter;
|
||||
uint8_t TapeLedCounter;
|
||||
uint8_t TapeLedRate[8];
|
||||
AIO_IOB2_BI2X_AC1__INPUT Input;
|
||||
AIO_IOB2_BI2X_AC1__INPUTDATA InputData;
|
||||
AIO_IOB2_BI2X_AC1__OUTPUTDATA OutputData;
|
||||
AIO_IOB2_BI2X_AC1__ICNPIN ICnPinHist[20];
|
||||
};
|
||||
|
||||
/*
|
||||
* typedefs
|
||||
*/
|
||||
|
||||
// libaio-iob2_video.dll
|
||||
typedef AIO_IOB2_BI2X_AC1* (__fastcall *aioIob2Bi2xAC1_Create_t)(AIO_NMGR_IOB2 *i_pNodeMgr, uint32_t i_DevId,
|
||||
AIO_IOB2_BI2X_AC1__SETTING *i_Setting);
|
||||
typedef void (__fastcall *aioIob2Bi2xAC1_GetDeviceStatus_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl,
|
||||
AIO_IOB2_BI2X_AC1__DEVSTATUS *o_DevStatus);
|
||||
typedef void (__fastcall *aioIob2Bi2xAC1_SetWatchDogTimer_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint8_t i_Count);
|
||||
typedef void (__fastcall *aioIob2Bi2xAC1_AddCounter_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_Counter,
|
||||
uint32_t i_Count);
|
||||
typedef void (__fastcall *aioIob2Bi2xAC1_SetOutputData_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_CnPin,
|
||||
uint8_t i_Data);
|
||||
typedef void (__fastcall *aioIob2Bi2xAC1_SetTapeLedDataPart_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_TapeLedCh,
|
||||
uint32_t i_Offset, uint8_t *i_pData,
|
||||
uint32_t i_cntTapeLed, bool i_bReverse);
|
||||
typedef void (__fastcall *aioIob2Bi2x_SetTapeLedDataGroup_t)(AIO_IOB2_BI2X_AC1* i_pNodeCtl, uint32_t i_bfGroup);
|
||||
typedef void (__fastcall *aioIob2Bi2x_SetTapeLedDataLimit_t)(AIO_IOB2_BI2X_AC1* i_pNodeCtl, uint32_t i_Channel,
|
||||
uint8_t i_Scale, uint8_t i_Limit);
|
||||
typedef AIO_IOB2_BI2X_WRFIRM *(__fastcall *aioIob2Bi2x_CreateWriteFirmContext_t)(uint32_t i_SerialNumber,
|
||||
uint32_t i_bfIob);
|
||||
typedef AIO_SCI_COMM* (__fastcall *aioIob2Bi2x_OpenSciUsbCdc_t)(uint32_t i_SerialNumber);
|
||||
typedef void (__fastcall *aioIob2Bi2x_DestroyWriteFirmContext_t)(AIO_IOB2_BI2X_WRFIRM *i_pWrFirm);
|
||||
typedef int32_t (__fastcall *aioIob2Bi2x_WriteFirmGetState_t)(AIO_IOB2_BI2X_WRFIRM *i_pWrFirm);
|
||||
typedef bool (__fastcall *aioIob2Bi2x_WriteFirmIsCompleted_t)(int32_t i_State);
|
||||
typedef bool (__fastcall *aioIob2Bi2x_WriteFirmIsError_t)(int32_t i_State);
|
||||
|
||||
// libaio-iob.dll
|
||||
typedef AIO_NMGR_IOB2 *(__fastcall *aioNMgrIob2_Create_t)(AIO_SCI_COMM *i_pSci, uint32_t i_bfMode);
|
||||
typedef void (__fastcall *aioNMgrIob_BeginManage_t)(AIO_NMGR_IOB2 *i_pNodeMgr);
|
||||
|
||||
// libaio.dll
|
||||
typedef void (__fastcall *aioSci_Destroy_t)(AIO_SCI_COMM *i_pNodeMgr);
|
||||
typedef void (__fastcall *aioNodeMgr_Destroy_t)(AIO_NMGR_IOB2 *i_pNodeMgr);
|
||||
typedef int32_t (__fastcall *aioNodeMgr_GetState_t)(AIO_NMGR_IOB2 *i_pNodeMgr);
|
||||
typedef bool (__fastcall *aioNodeMgr_IsReady_t)(AIO_NMGR_IOB2 *i_pNodeMgr, int32_t i_State);
|
||||
typedef bool (__fastcall *aioNodeMgr_IsError_t)(AIO_NMGR_IOB2 *i_pNodeMgr, int32_t i_State);
|
||||
typedef void (__fastcall *aioNodeCtl_Destroy_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl);
|
||||
typedef int32_t (__fastcall *aioNodeCtl_GetState_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl);
|
||||
typedef bool (__fastcall *aioNodeCtl_IsReady_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, int32_t i_State);
|
||||
typedef bool (__fastcall *aioNodeCtl_IsError_t)(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, int32_t i_State);
|
||||
|
||||
/*
|
||||
* function pointers
|
||||
*/
|
||||
|
||||
// libaio-iob2_video.dll
|
||||
static aioIob2Bi2xAC1_Create_t aioIob2Bi2xAC1_Create_orig = nullptr;
|
||||
static aioIob2Bi2xAC1_GetDeviceStatus_t aioIob2Bi2xAC1_GetDeviceStatus_orig = nullptr;
|
||||
static aioIob2Bi2xAC1_SetWatchDogTimer_t aioIob2Bi2xAC1_SetWatchDogTimer_orig = nullptr;
|
||||
static aioIob2Bi2xAC1_AddCounter_t aioIob2Bi2xAC1_AddCounter_orig = nullptr;
|
||||
static aioIob2Bi2xAC1_SetOutputData_t aioIob2Bi2xAC1_SetOutputData_orig = nullptr;
|
||||
static aioIob2Bi2xAC1_SetTapeLedDataPart_t aioIob2Bi2xAC1_SetTapeLedDataPart_orig = nullptr;
|
||||
static aioIob2Bi2x_SetTapeLedDataGroup_t aioIob2Bi2x_SetTapeLedDataGroup_orig = nullptr;
|
||||
static aioIob2Bi2x_SetTapeLedDataLimit_t aioIob2Bi2x_SetTapeLedDataLimit_orig = nullptr;
|
||||
static aioIob2Bi2x_OpenSciUsbCdc_t aioIob2Bi2x_OpenSciUsbCdc_orig = nullptr;
|
||||
static aioIob2Bi2x_CreateWriteFirmContext_t aioIob2Bi2x_CreateWriteFirmContext_orig = nullptr;
|
||||
static aioIob2Bi2x_DestroyWriteFirmContext_t aioIob2Bi2x_DestroyWriteFirmContext_orig = nullptr;
|
||||
static aioIob2Bi2x_WriteFirmGetState_t aioIob2Bi2x_WriteFirmGetState_orig = nullptr;
|
||||
static aioIob2Bi2x_WriteFirmIsCompleted_t aioIob2Bi2x_WriteFirmIsCompleted_orig = nullptr;
|
||||
static aioIob2Bi2x_WriteFirmIsError_t aioIob2Bi2x_WriteFirmIsError_orig = nullptr;
|
||||
|
||||
// libaio-iob.dll
|
||||
static aioNMgrIob2_Create_t aioNMgrIob2_Create_orig = nullptr;
|
||||
static aioNMgrIob_BeginManage_t aioNMgrIob_BeginManage_orig = nullptr;
|
||||
|
||||
// libaio.dll
|
||||
static aioSci_Destroy_t aioSci_Destroy_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;
|
||||
|
||||
/*
|
||||
* variables
|
||||
*/
|
||||
|
||||
static AIO_IOB2_BI2X_AC1 *aioIob2Bi2xAc1;
|
||||
static AIO_SCI_COMM *aioSciComm;
|
||||
static AIO_NMGR_IOB2 *aioNmgrIob2;
|
||||
static AIO_IOB2_BI2X_WRFIRM *aioIob2Bi2xWrfirm;
|
||||
|
||||
/*
|
||||
* implementations
|
||||
*/
|
||||
|
||||
// libaio-iob2_video.dll
|
||||
|
||||
static AIO_IOB2_BI2X_AC1* __fastcall aioIob2Bi2xAC1_Create(AIO_NMGR_IOB2 *i_pNodeMgr, uint32_t i_DevId,
|
||||
AIO_IOB2_BI2X_AC1__SETTING *i_Setting) {
|
||||
if (i_pNodeMgr == aioNmgrIob2) {
|
||||
log_info("bi2x_hook", "aioIob2Bi2xAC1_Create hook hit");
|
||||
aioIob2Bi2xAc1 = new AIO_IOB2_BI2X_AC1;
|
||||
return aioIob2Bi2xAc1;
|
||||
} else {
|
||||
return aioIob2Bi2xAC1_Create_orig(i_pNodeMgr, i_DevId, i_Setting);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIob2Bi2xAC1_GetDeviceStatus(AIO_IOB2_BI2X_AC1 *i_pNodeCtl,
|
||||
AIO_IOB2_BI2X_AC1__DEVSTATUS *o_DevStatus) {
|
||||
RI_MGR->devices_flush_output();
|
||||
|
||||
if (i_pNodeCtl != aioIob2Bi2xAc1) {
|
||||
return aioIob2Bi2xAC1_GetDeviceStatus_orig(i_pNodeCtl, o_DevStatus);
|
||||
}
|
||||
|
||||
memset(o_DevStatus, 0x00, sizeof(AIO_IOB2_BI2X_AC1__DEVSTATUS));
|
||||
|
||||
auto &buttons = get_buttons();
|
||||
o_DevStatus->Input.CN8_8 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Test]) ? 0 : 0xFF;
|
||||
o_DevStatus->Input.CN8_9 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Service]) ? 0 : 0xFF;
|
||||
o_DevStatus->Input.CN8_10 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::CoinMech]) ? 0 : 0xFF;
|
||||
o_DevStatus->Input.CN15_10 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Headphone]) ? 0 : 0xFF;
|
||||
o_DevStatus->Input.CN15_12 = 0xFF; // Recorder off; present in I/O test menu but unused in game
|
||||
|
||||
o_DevStatus->Input.CN12_11 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button1]) ? 0 : 0xFF;
|
||||
o_DevStatus->Input.CN12_12 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button2]) ? 0 : 0xFF;
|
||||
o_DevStatus->Input.CN12_13 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button3]) ? 0 : 0xFF;
|
||||
o_DevStatus->Input.CN12_14 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button4]) ? 0 : 0xFF;
|
||||
o_DevStatus->Input.CN12_15 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button5]) ? 0 : 0xFF;
|
||||
o_DevStatus->Input.CN12_16 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button6]) ? 0 : 0xFF;
|
||||
o_DevStatus->Input.CN12_17 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button7]) ? 0 : 0xFF;
|
||||
o_DevStatus->Input.CN12_18 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button8]) ? 0 : 0xFF;
|
||||
o_DevStatus->Input.CN12_19 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button9]) ? 0 : 0xFF;
|
||||
o_DevStatus->Input.CN12_20 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button10]) ? 0 : 0xFF;
|
||||
o_DevStatus->Input.CN12_21 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button11]) ? 0 : 0xFF;
|
||||
o_DevStatus->Input.CN12_22 = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Button12]) ? 0 : 0xFF;
|
||||
|
||||
auto &analogs = get_analogs();
|
||||
|
||||
// FADER-L
|
||||
float val = 0.f;
|
||||
if (analogs[Analogs::FaderL].isSet()) {
|
||||
if (PC_KNOB_MODE) {
|
||||
val = get_fader_knob_mode_value(
|
||||
GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderL]),
|
||||
fader_l_relative);
|
||||
} else {
|
||||
val = (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderL]) - 0.5f) * 2;
|
||||
}
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Left]) &&
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Right])) {
|
||||
val = 0.f;
|
||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Left])) {
|
||||
val = -1.0f;
|
||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Right])) {
|
||||
val = +1.0f;
|
||||
}
|
||||
o_DevStatus->Input.CN15_7 = (val < 0.2f) ? 0xFF : 0;
|
||||
o_DevStatus->Input.CN15_6 = (val > -0.85f && val < 0.35f) ? 0xFF : 0;
|
||||
o_DevStatus->Input.CN15_5 = (val > -0.6f && val < 0.6f) ? 0xFF : 0;
|
||||
o_DevStatus->Input.CN15_4 = (val > -0.35f && val < 0.85f) ? 0xFF : 0;
|
||||
o_DevStatus->Input.CN15_3 = (val > -0.2f) ? 0xFF : 0;
|
||||
|
||||
// FADER-R
|
||||
val = 0.f;
|
||||
if (analogs[Analogs::FaderR].isSet()) {
|
||||
if (PC_KNOB_MODE) {
|
||||
val = get_fader_knob_mode_value(
|
||||
GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderR]),
|
||||
fader_r_relative);
|
||||
} else {
|
||||
val = (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderR]) - 0.5f) * 2;
|
||||
}
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Left]) &&
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Right])) {
|
||||
val = 0.f;
|
||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Left])) {
|
||||
val = -1.0f;
|
||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Right])) {
|
||||
val = +1.0f;
|
||||
}
|
||||
o_DevStatus->Input.CN11_20 = (val < 0.2f) ? 0xFF : 0;
|
||||
o_DevStatus->Input.CN11_19 = (val > -0.85f && val < 0.35f) ? 0xFF : 0;
|
||||
o_DevStatus->Input.CN9_10 = (val > -0.6f && val < 0.6f) ? 0xFF : 0;
|
||||
o_DevStatus->Input.CN9_9 = (val > -0.35f && val < 0.85f) ? 0xFF : 0;
|
||||
o_DevStatus->Input.CN9_8 = (val > -0.2f) ? 0xFF : 0;
|
||||
|
||||
// coin
|
||||
o_DevStatus->Input.Coin1Count = eamuse_coin_get_stock();
|
||||
}
|
||||
|
||||
void __fastcall aioIob2Bi2xAC1_SetWatchDogTimer(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint8_t i_Count) {
|
||||
if (i_pNodeCtl != aioIob2Bi2xAc1) {
|
||||
return aioIob2Bi2xAC1_SetWatchDogTimer_orig(i_pNodeCtl, i_Count);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIob2Bi2xAC1_AddCounter(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_Counter, uint32_t i_Count) {
|
||||
if (i_pNodeCtl == aioIob2Bi2xAc1 && i_Count == 0) {
|
||||
eamuse_coin_set_stock((uint16_t) i_Count);
|
||||
} else {
|
||||
return aioIob2Bi2xAC1_AddCounter_orig(i_pNodeCtl, i_Counter, i_Count);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIob2Bi2xAC1_SetOutputData(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_CnPin, uint8_t i_Data) {
|
||||
if (i_pNodeCtl != aioIob2Bi2xAc1) {
|
||||
return aioIob2Bi2xAC1_SetOutputData_orig(i_pNodeCtl, i_CnPin, i_Data);
|
||||
}
|
||||
|
||||
if (i_CnPin == 0x1) {
|
||||
eamuse_coin_set_block(i_Data == 0xFF);
|
||||
return;
|
||||
}
|
||||
|
||||
auto &lights = get_lights();
|
||||
if (i_CnPin == 0x11) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(Lights::IC_READER_R), i_Data / 255.0f);
|
||||
} else if (i_CnPin == 0x12) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(Lights::IC_READER_G), i_Data / 255.0f);
|
||||
} else if (i_CnPin == 0x13) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(Lights::IC_READER_B), i_Data / 255.0f);
|
||||
}
|
||||
}
|
||||
|
||||
struct PolarisChordLight {
|
||||
int data_index;
|
||||
games::pc::Lights::pc_lights_t light;
|
||||
PolarisChordLight(
|
||||
int data_index, games::pc::Lights::pc_lights_t light) :
|
||||
data_index(data_index), light(light) {}
|
||||
};
|
||||
|
||||
static void set_led_value(games::pc::Lights::pc_lights_t light, uint8_t value) {
|
||||
auto &lights = games::pc::get_lights();
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(light), value / 255.f);
|
||||
}
|
||||
|
||||
void __fastcall aioIob2Bi2xAC1_SetTapeLedDataPart(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, uint32_t i_TapeLedCh,
|
||||
uint32_t i_Offset, uint8_t *i_pData,
|
||||
uint32_t i_cntTapeLed, bool i_bReverse) {
|
||||
if (i_pNodeCtl != aioIob2Bi2xAc1) {
|
||||
return aioIob2Bi2xAC1_SetTapeLedDataPart_orig(i_pNodeCtl, i_TapeLedCh, i_Offset, i_pData, i_cntTapeLed, i_bReverse);
|
||||
}
|
||||
|
||||
// log_info(
|
||||
// "pc",
|
||||
// "lamp [{}] [{}] = {},{},{}, cnt={}",
|
||||
// i_TapeLedCh,
|
||||
// i_Offset,
|
||||
// i_pData[0], i_pData[1], i_pData[2],
|
||||
// i_cntTapeLed);
|
||||
|
||||
// [channel 0]
|
||||
// these are button lamps; there are 12 buttons (columns) with this layout:
|
||||
//
|
||||
// 0 5 8 11 14 16 | 18 20 22 25 28 31
|
||||
// 1 6 9 12 15 17 | 19 21 23 26 29 32
|
||||
// 2 7 10 13 | 24 27 30 33
|
||||
// 3 | 34
|
||||
// 4 | 35
|
||||
//
|
||||
// 36 RGB lamps * 3 = 108 LEDs
|
||||
// each value ranges from [0, 255]
|
||||
// (we only care about the top row)
|
||||
|
||||
static PolarisChordLight button_lights_mapping[] = {
|
||||
{(0 * 3) + 0, games::pc::Lights::pc_lights_t::Button1_R},
|
||||
{(0 * 3) + 1, games::pc::Lights::pc_lights_t::Button1_G},
|
||||
{(0 * 3) + 2, games::pc::Lights::pc_lights_t::Button1_B},
|
||||
|
||||
{(5 * 3) + 0, games::pc::Lights::pc_lights_t::Button2_R},
|
||||
{(5 * 3) + 1, games::pc::Lights::pc_lights_t::Button2_G},
|
||||
{(5 * 3) + 2, games::pc::Lights::pc_lights_t::Button2_B},
|
||||
|
||||
{(8 * 3) + 0, games::pc::Lights::pc_lights_t::Button3_R},
|
||||
{(8 * 3) + 1, games::pc::Lights::pc_lights_t::Button3_G},
|
||||
{(8 * 3) + 2, games::pc::Lights::pc_lights_t::Button3_B},
|
||||
|
||||
{(11 * 3) + 0, games::pc::Lights::pc_lights_t::Button4_R},
|
||||
{(11 * 3) + 1, games::pc::Lights::pc_lights_t::Button4_G},
|
||||
{(11 * 3) + 2, games::pc::Lights::pc_lights_t::Button4_B},
|
||||
|
||||
{(14 * 3) + 0, games::pc::Lights::pc_lights_t::Button5_R},
|
||||
{(14 * 3) + 1, games::pc::Lights::pc_lights_t::Button5_G},
|
||||
{(14 * 3) + 2, games::pc::Lights::pc_lights_t::Button5_B},
|
||||
|
||||
{(16 * 3) + 0, games::pc::Lights::pc_lights_t::Button6_R},
|
||||
{(16 * 3) + 1, games::pc::Lights::pc_lights_t::Button6_G},
|
||||
{(16 * 3) + 2, games::pc::Lights::pc_lights_t::Button6_B},
|
||||
|
||||
{(18 * 3) + 0, games::pc::Lights::pc_lights_t::Button7_R},
|
||||
{(18 * 3) + 1, games::pc::Lights::pc_lights_t::Button7_G},
|
||||
{(18 * 3) + 2, games::pc::Lights::pc_lights_t::Button7_B},
|
||||
|
||||
{(20 * 3) + 0, games::pc::Lights::pc_lights_t::Button8_R},
|
||||
{(20 * 3) + 1, games::pc::Lights::pc_lights_t::Button8_G},
|
||||
{(20 * 3) + 2, games::pc::Lights::pc_lights_t::Button8_B},
|
||||
|
||||
{(22 * 3) + 0, games::pc::Lights::pc_lights_t::Button9_R},
|
||||
{(22 * 3) + 1, games::pc::Lights::pc_lights_t::Button9_G},
|
||||
{(22 * 3) + 2, games::pc::Lights::pc_lights_t::Button9_B},
|
||||
|
||||
{(25 * 3) + 0, games::pc::Lights::pc_lights_t::Button10_R},
|
||||
{(25 * 3) + 1, games::pc::Lights::pc_lights_t::Button10_G},
|
||||
{(25 * 3) + 2, games::pc::Lights::pc_lights_t::Button10_B},
|
||||
|
||||
{(28 * 3) + 0, games::pc::Lights::pc_lights_t::Button11_R},
|
||||
{(28 * 3) + 1, games::pc::Lights::pc_lights_t::Button11_G},
|
||||
{(28 * 3) + 2, games::pc::Lights::pc_lights_t::Button11_B},
|
||||
|
||||
{(31 * 3) + 0, games::pc::Lights::pc_lights_t::Button12_R},
|
||||
{(31 * 3) + 1, games::pc::Lights::pc_lights_t::Button12_G},
|
||||
{(31 * 3) + 2, games::pc::Lights::pc_lights_t::Button12_B},
|
||||
};
|
||||
|
||||
if (i_TapeLedCh == 0 && i_Offset == 0 && i_cntTapeLed == 108) {
|
||||
for (const auto &map : button_lights_mapping) {
|
||||
set_led_value(map.light, i_pData[map.data_index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIob2Bi2x_SetTapeLedDataGroup(AIO_IOB2_BI2X_AC1* i_pNodeCtl, uint32_t i_bfGroup) {
|
||||
if (i_pNodeCtl != aioIob2Bi2xAc1) {
|
||||
return aioIob2Bi2x_SetTapeLedDataGroup_orig(i_pNodeCtl, i_bfGroup);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIob2Bi2x_SetTapeLedDataLimit(AIO_IOB2_BI2X_AC1* i_pNodeCtl, uint32_t i_Channel,
|
||||
uint8_t i_Scale, uint8_t i_Limit) {
|
||||
if (i_pNodeCtl != aioIob2Bi2xAc1) {
|
||||
return aioIob2Bi2x_SetTapeLedDataLimit_orig(i_pNodeCtl, i_Channel, i_Scale, i_Limit);
|
||||
}
|
||||
}
|
||||
|
||||
static AIO_SCI_COMM *__fastcall aioIob2Bi2x_OpenSciUsbCdc(uint32_t i_SerialNumber) {
|
||||
aioSciComm = new AIO_SCI_COMM;
|
||||
return aioSciComm;
|
||||
}
|
||||
|
||||
static AIO_IOB2_BI2X_WRFIRM *__fastcall aioIob2Bi2x_CreateWriteFirmContext(
|
||||
uint32_t i_SerialNumber, uint32_t i_bfIob) {
|
||||
|
||||
aioIob2Bi2xWrfirm = new AIO_IOB2_BI2X_WRFIRM;
|
||||
return aioIob2Bi2xWrfirm;
|
||||
}
|
||||
|
||||
static void __fastcall aioIob2Bi2x_DestroyWriteFirmContext(AIO_IOB2_BI2X_WRFIRM *i_pWrFirm) {
|
||||
if (i_pWrFirm == aioIob2Bi2xWrfirm) {
|
||||
delete aioIob2Bi2xWrfirm;
|
||||
aioIob2Bi2xWrfirm = nullptr;
|
||||
} else {
|
||||
return aioIob2Bi2x_DestroyWriteFirmContext_orig(i_pWrFirm);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t __fastcall aioIob2Bi2x_WriteFirmGetState(AIO_IOB2_BI2X_WRFIRM *i_pWrFirm) {
|
||||
if (i_pWrFirm == aioIob2Bi2xWrfirm) {
|
||||
return 8;
|
||||
} else {
|
||||
return aioIob2Bi2x_WriteFirmGetState_orig(i_pWrFirm);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __fastcall aioIob2Bi2x_WriteFirmIsCompleted(int32_t i_State) {
|
||||
if (aioIob2Bi2xWrfirm != nullptr) {
|
||||
return true;
|
||||
} else {
|
||||
return aioIob2Bi2x_WriteFirmIsCompleted_orig(i_State);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __fastcall aioIob2Bi2x_WriteFirmIsError(int32_t i_State) {
|
||||
if (aioIob2Bi2xWrfirm != nullptr) {
|
||||
return false;
|
||||
} else {
|
||||
return aioIob2Bi2x_WriteFirmIsError_orig(i_State);
|
||||
}
|
||||
}
|
||||
|
||||
// libaio-iob.dll
|
||||
|
||||
static AIO_NMGR_IOB2 *__fastcall aioNMgrIob2_Create(AIO_SCI_COMM *i_pSci, uint32_t i_bfMode) {
|
||||
if (i_pSci == aioSciComm) {
|
||||
aioNmgrIob2 = new AIO_NMGR_IOB2;
|
||||
return aioNmgrIob2;
|
||||
} else {
|
||||
return aioNMgrIob2_Create_orig(i_pSci, i_bfMode);
|
||||
}
|
||||
}
|
||||
|
||||
static void __fastcall aioNMgrIob_BeginManage(AIO_NMGR_IOB2 *i_pNodeMgr) {
|
||||
if (i_pNodeMgr != aioNmgrIob2) {
|
||||
return aioNMgrIob_BeginManage_orig(i_pNodeMgr);
|
||||
}
|
||||
}
|
||||
|
||||
// libaio.dll
|
||||
|
||||
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_IOB2 *i_pNodeMgr) {
|
||||
if (i_pNodeMgr == aioNmgrIob2) {
|
||||
delete aioNmgrIob2;
|
||||
aioNmgrIob2 = nullptr;
|
||||
} else {
|
||||
return aioNodeMgr_Destroy_orig(i_pNodeMgr);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t __fastcall aioNodeMgr_GetState(AIO_NMGR_IOB2 *i_pNodeMgr) {
|
||||
if (i_pNodeMgr == aioNmgrIob2) {
|
||||
return 1;
|
||||
} else {
|
||||
return aioNodeMgr_GetState_orig(i_pNodeMgr);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __fastcall aioNodeMgr_IsReady(AIO_NMGR_IOB2 *i_pNodeMgr, int32_t i_State) {
|
||||
if (i_pNodeMgr == aioNmgrIob2) {
|
||||
return true;
|
||||
} else {
|
||||
return aioNodeMgr_IsReady_orig(i_pNodeMgr, i_State);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __fastcall aioNodeMgr_IsError(AIO_NMGR_IOB2 *i_pNodeMgr, int32_t i_State) {
|
||||
if (i_pNodeMgr == aioNmgrIob2) {
|
||||
return false;
|
||||
} else {
|
||||
return aioNodeMgr_IsError_orig(i_pNodeMgr, i_State);
|
||||
}
|
||||
}
|
||||
|
||||
static void __fastcall aioNodeCtl_Destroy(AIO_IOB2_BI2X_AC1 *i_pNodeCtl) {
|
||||
if (i_pNodeCtl == aioIob2Bi2xAc1) {
|
||||
delete aioIob2Bi2xAc1;
|
||||
aioIob2Bi2xAc1 = nullptr;
|
||||
} else {
|
||||
return aioNodeCtl_Destroy_orig(i_pNodeCtl);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t __fastcall aioNodeCtl_GetState(AIO_IOB2_BI2X_AC1 *i_pNodeCtl) {
|
||||
if (i_pNodeCtl == aioIob2Bi2xAc1) {
|
||||
return 1;
|
||||
} else {
|
||||
return aioNodeCtl_GetState_orig(i_pNodeCtl);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __fastcall aioNodeCtl_IsReady(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, int32_t i_State) {
|
||||
if (i_pNodeCtl == aioIob2Bi2xAc1) {
|
||||
return true;
|
||||
} else {
|
||||
return aioNodeCtl_IsReady_orig(i_pNodeCtl, i_State);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __fastcall aioNodeCtl_IsError(AIO_IOB2_BI2X_AC1 *i_pNodeCtl, int32_t i_State) {
|
||||
if (i_pNodeCtl == aioIob2Bi2xAc1) {
|
||||
return false;
|
||||
} else {
|
||||
return aioNodeCtl_IsError_orig(i_pNodeCtl, i_State);
|
||||
}
|
||||
}
|
||||
|
||||
void bi2x_hook_init() {
|
||||
// avoid double init
|
||||
static bool initialized = false;
|
||||
if (initialized) {
|
||||
return;
|
||||
} else {
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
// announce
|
||||
log_info("bi2x_hook", "init");
|
||||
|
||||
// libaio-iob2_video.dll
|
||||
const auto libaioIob2VideoDll = "libaio-iob2_video.dll";
|
||||
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_Create",
|
||||
aioIob2Bi2xAC1_Create, &aioIob2Bi2xAC1_Create_orig);
|
||||
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_GetDeviceStatus",
|
||||
aioIob2Bi2xAC1_GetDeviceStatus, &aioIob2Bi2xAC1_GetDeviceStatus_orig);
|
||||
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_SetWatchDogTimer",
|
||||
aioIob2Bi2xAC1_SetWatchDogTimer, &aioIob2Bi2xAC1_SetWatchDogTimer_orig);
|
||||
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_AddCounter",
|
||||
aioIob2Bi2xAC1_AddCounter, &aioIob2Bi2xAC1_AddCounter_orig);
|
||||
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_SetOutputData",
|
||||
aioIob2Bi2xAC1_SetOutputData, &aioIob2Bi2xAC1_SetOutputData_orig);
|
||||
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2xAC1_SetTapeLedDataPart",
|
||||
aioIob2Bi2xAC1_SetTapeLedDataPart, &aioIob2Bi2xAC1_SetTapeLedDataPart_orig);
|
||||
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_SetTapeLedDataGroup",
|
||||
aioIob2Bi2x_SetTapeLedDataGroup, &aioIob2Bi2x_SetTapeLedDataGroup_orig);
|
||||
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_SetTapeLedDataLimit",
|
||||
aioIob2Bi2x_SetTapeLedDataLimit, &aioIob2Bi2x_SetTapeLedDataLimit_orig);
|
||||
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_OpenSciUsbCdc",
|
||||
aioIob2Bi2x_OpenSciUsbCdc, &aioIob2Bi2x_OpenSciUsbCdc_orig);
|
||||
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_CreateWriteFirmContext",
|
||||
aioIob2Bi2x_CreateWriteFirmContext, &aioIob2Bi2x_CreateWriteFirmContext_orig);
|
||||
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_DestroyWriteFirmContext",
|
||||
aioIob2Bi2x_DestroyWriteFirmContext, &aioIob2Bi2x_DestroyWriteFirmContext_orig);
|
||||
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_WriteFirmGetState",
|
||||
aioIob2Bi2x_WriteFirmGetState, &aioIob2Bi2x_WriteFirmGetState_orig);
|
||||
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_WriteFirmIsCompleted",
|
||||
aioIob2Bi2x_WriteFirmIsCompleted, &aioIob2Bi2x_WriteFirmIsCompleted_orig);
|
||||
execexe::trampoline_try(libaioIob2VideoDll, "aioIob2Bi2x_WriteFirmIsError",
|
||||
aioIob2Bi2x_WriteFirmIsError, &aioIob2Bi2x_WriteFirmIsError_orig);
|
||||
|
||||
// libaio-iob.dll
|
||||
const auto libaioIobDll = "libaio-iob.dll";
|
||||
execexe::trampoline_try(libaioIobDll, "aioNMgrIob2_Create",
|
||||
aioNMgrIob2_Create, &aioNMgrIob2_Create_orig);
|
||||
execexe::trampoline_try(libaioIobDll, "aioNMgrIob_BeginManage",
|
||||
aioNMgrIob_BeginManage, &aioNMgrIob_BeginManage_orig);
|
||||
|
||||
// libaio.dll
|
||||
const auto libaioDll = "libaio.dll";
|
||||
execexe::trampoline_try(libaioDll, "aioSci_Destroy",
|
||||
aioSci_Destroy, &aioSci_Destroy_orig);
|
||||
execexe::trampoline_try(libaioDll, "aioNodeMgr_Destroy",
|
||||
aioNodeMgr_Destroy, &aioNodeMgr_Destroy_orig);
|
||||
execexe::trampoline_try(libaioDll, "aioNodeMgr_GetState",
|
||||
aioNodeMgr_GetState, &aioNodeMgr_GetState_orig);
|
||||
execexe::trampoline_try(libaioDll, "aioNodeMgr_IsReady",
|
||||
aioNodeMgr_IsReady, &aioNodeMgr_IsReady_orig);
|
||||
execexe::trampoline_try(libaioDll, "aioNodeMgr_IsError",
|
||||
aioNodeMgr_IsError, &aioNodeMgr_IsError_orig);
|
||||
execexe::trampoline_try(libaioDll, "aioNodeCtl_Destroy",
|
||||
aioNodeCtl_Destroy, &aioNodeCtl_Destroy_orig);
|
||||
execexe::trampoline_try(libaioDll, "aioNodeCtl_GetState",
|
||||
aioNodeCtl_GetState, &aioNodeCtl_GetState_orig);
|
||||
execexe::trampoline_try(libaioDll, "aioNodeCtl_IsReady",
|
||||
aioNodeCtl_IsReady, &aioNodeCtl_IsReady_orig);
|
||||
execexe::trampoline_try(libaioDll, "aioNodeCtl_IsError",
|
||||
aioNodeCtl_IsError, &aioNodeCtl_IsError_orig);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace games::pc {
|
||||
void bi2x_hook_init();
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
#include "io.h"
|
||||
|
||||
std::vector<Button> &games::pc::get_buttons() {
|
||||
static std::vector<Button> buttons;
|
||||
|
||||
if (buttons.empty()) {
|
||||
buttons = GameAPI::Buttons::getButtons("Polaris Chord");
|
||||
|
||||
GameAPI::Buttons::sortButtons(
|
||||
&buttons,
|
||||
"Service",
|
||||
"Test",
|
||||
"Coin Mech",
|
||||
"Button 1",
|
||||
"Button 2",
|
||||
"Button 3",
|
||||
"Button 4",
|
||||
"Button 5",
|
||||
"Button 6",
|
||||
"Button 7",
|
||||
"Button 8",
|
||||
"Button 9",
|
||||
"Button 10",
|
||||
"Button 11",
|
||||
"Button 12",
|
||||
"Fader-L Left",
|
||||
"Fader-L Right",
|
||||
"Fader-R Left",
|
||||
"Fader-R Right",
|
||||
"Headphone"
|
||||
);
|
||||
}
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
std::string games::pc::get_buttons_help() {
|
||||
// keep to max 100 characters wide
|
||||
return
|
||||
" FADER-L FADER-R \n"
|
||||
"\n"
|
||||
" B1 B2 B3 ... B11 B12\n"
|
||||
"\n"
|
||||
" Most menu interactions are on the touch screen; you can use your mouse.\n"
|
||||
;
|
||||
}
|
||||
|
||||
std::vector<Analog> &games::pc::get_analogs() {
|
||||
static std::vector<Analog> analogs;
|
||||
|
||||
if (analogs.empty()) {
|
||||
analogs = GameAPI::Analogs::getAnalogs("Polaris Chord");
|
||||
|
||||
GameAPI::Analogs::sortAnalogs(
|
||||
&analogs,
|
||||
"Fader-L",
|
||||
"Fader-R"
|
||||
);
|
||||
}
|
||||
|
||||
return analogs;
|
||||
}
|
||||
|
||||
std::vector<Light> &games::pc::get_lights() {
|
||||
static std::vector<Light> lights;
|
||||
|
||||
if (lights.empty()) {
|
||||
lights = GameAPI::Lights::getLights("Polaris Chord");
|
||||
|
||||
GameAPI::Lights::sortLights(
|
||||
&lights,
|
||||
"IC Reader R",
|
||||
"IC Reader G",
|
||||
"IC Reader B",
|
||||
|
||||
"Button 1 R",
|
||||
"Button 1 G",
|
||||
"Button 1 B",
|
||||
|
||||
"Button 2 R",
|
||||
"Button 2 G",
|
||||
"Button 2 B",
|
||||
|
||||
"Button 3 R",
|
||||
"Button 3 G",
|
||||
"Button 3 B",
|
||||
|
||||
"Button 4 R",
|
||||
"Button 4 G",
|
||||
"Button 4 B",
|
||||
|
||||
"Button 5 R",
|
||||
"Button 5 G",
|
||||
"Button 5 B",
|
||||
|
||||
"Button 6 R",
|
||||
"Button 6 G",
|
||||
"Button 6 B",
|
||||
|
||||
"Button 7 R",
|
||||
"Button 7 G",
|
||||
"Button 7 B",
|
||||
|
||||
"Button 8 R",
|
||||
"Button 8 G",
|
||||
"Button 8 B",
|
||||
|
||||
"Button 9 R",
|
||||
"Button 9 G",
|
||||
"Button 9 B",
|
||||
|
||||
"Button 10 R",
|
||||
"Button 10 G",
|
||||
"Button 10 B",
|
||||
|
||||
"Button 11 R",
|
||||
"Button 11 G",
|
||||
"Button 11 B",
|
||||
|
||||
"Button 12 R",
|
||||
"Button 12 G",
|
||||
"Button 12 B"
|
||||
);
|
||||
}
|
||||
|
||||
return lights;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "cfg/api.h"
|
||||
|
||||
namespace games::pc {
|
||||
namespace Buttons {
|
||||
enum {
|
||||
Service,
|
||||
Test,
|
||||
CoinMech,
|
||||
Button1,
|
||||
Button2,
|
||||
Button3,
|
||||
Button4,
|
||||
Button5,
|
||||
Button6,
|
||||
Button7,
|
||||
Button8,
|
||||
Button9,
|
||||
Button10,
|
||||
Button11,
|
||||
Button12,
|
||||
FaderL_Left,
|
||||
FaderL_Right,
|
||||
FaderR_Left,
|
||||
FaderR_Right,
|
||||
Headphone
|
||||
};
|
||||
}
|
||||
|
||||
namespace Analogs {
|
||||
enum {
|
||||
FaderL,
|
||||
FaderR,
|
||||
};
|
||||
}
|
||||
|
||||
namespace Lights {
|
||||
typedef enum {
|
||||
IC_READER_R,
|
||||
IC_READER_G,
|
||||
IC_READER_B,
|
||||
|
||||
Button1_R,
|
||||
Button1_G,
|
||||
Button1_B,
|
||||
|
||||
Button2_R,
|
||||
Button2_G,
|
||||
Button2_B,
|
||||
|
||||
Button3_R,
|
||||
Button3_G,
|
||||
Button3_B,
|
||||
|
||||
Button4_R,
|
||||
Button4_G,
|
||||
Button4_B,
|
||||
|
||||
Button5_R,
|
||||
Button5_G,
|
||||
Button5_B,
|
||||
|
||||
Button6_R,
|
||||
Button6_G,
|
||||
Button6_B,
|
||||
|
||||
Button7_R,
|
||||
Button7_G,
|
||||
Button7_B,
|
||||
|
||||
Button8_R,
|
||||
Button8_G,
|
||||
Button8_B,
|
||||
|
||||
Button9_R,
|
||||
Button9_G,
|
||||
Button9_B,
|
||||
|
||||
Button10_R,
|
||||
Button10_G,
|
||||
Button10_B,
|
||||
|
||||
Button11_R,
|
||||
Button11_G,
|
||||
Button11_B,
|
||||
|
||||
Button12_R,
|
||||
Button12_G,
|
||||
Button12_B
|
||||
|
||||
} pc_lights_t;
|
||||
}
|
||||
|
||||
// getters
|
||||
std::vector<Button> &get_buttons();
|
||||
std::string get_buttons_help();
|
||||
std::vector<Analog> &get_analogs();
|
||||
std::vector<Light> &get_lights();
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
#include "pc.h"
|
||||
|
||||
#include <format>
|
||||
|
||||
#include "bi2x_hook.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/unity_player.h"
|
||||
#include "util/execexe.h"
|
||||
#include "acioemu/handle.h"
|
||||
#include "misc/wintouchemu.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
|
||||
namespace games::pc {
|
||||
std::string PC_INJECT_ARGS = "";
|
||||
bool PC_NO_IO = false;
|
||||
|
||||
static acioemu::ACIOHandle *acioHandle = nullptr;
|
||||
static std::wstring portName = L"COM1";
|
||||
|
||||
static decltype(RegisterRawInputDevices) *RegisterRawInputDevices_orig = nullptr;
|
||||
static decltype(EnumDisplaySettingsW) *EnumDisplaySettingsW_orig = nullptr;
|
||||
static decltype(QueryDisplayConfig) *QueryDisplayConfig_orig = nullptr;
|
||||
|
||||
static BOOL WINAPI RegisterRawInputDevices_hook(PCRAWINPUTDEVICE pRawInputDevices, UINT uiNumDevices, UINT cbSize) {
|
||||
|
||||
// if the caller is spice itself, then pass through.
|
||||
if (pRawInputDevices &&
|
||||
(uiNumDevices > 0) &&
|
||||
(pRawInputDevices[0].hwndTarget == RI_MGR->input_hwnd)) {
|
||||
|
||||
return RegisterRawInputDevices_orig(pRawInputDevices, uiNumDevices, cbSize);
|
||||
}
|
||||
|
||||
// otherwise, it must be the game; prevent the game from registering for raw input
|
||||
// and hijacking WM_INPUT messages.
|
||||
SetLastError(0xDEADBEEF);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOL WINAPI EnumDisplaySettingsW_hook(LPCWSTR lpszDeviceName, DWORD iModeNum, DEVMODEW* lpDevMode) {
|
||||
auto result = EnumDisplaySettingsW_orig(lpszDeviceName, iModeNum, lpDevMode);
|
||||
|
||||
// https://docs.unity3d.com/2022.3/Documentation/ScriptReference/Screen-currentResolution.html
|
||||
// "If the player is running in windowed mode, this returns the current dimensions of the game window in pixels and the display refresh rate."
|
||||
// Override it to 1920x1080 to avoid broken UI under Windowed mode
|
||||
if (result && iModeNum == ENUM_CURRENT_SETTINGS) {
|
||||
// Actually make it returns size of the game window, Can I?
|
||||
lpDevMode->dmPelsWidth = 1920;
|
||||
lpDevMode->dmPelsHeight = 1080;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
LONG WINAPI QueryDisplayConfig_hook(UINT32, UINT32*, DISPLAYCONFIG_PATH_INFO*, UINT32*, DISPLAYCONFIG_MODE_INFO*, DISPLAYCONFIG_TOPOLOGY_ID*) {
|
||||
// make unity fallback to EnumDisplaySettingsW as I don't
|
||||
// want to deal with this api which is way more complex
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
void PCGame::attach() {
|
||||
Game::attach();
|
||||
|
||||
// add card reader
|
||||
acioHandle = new acioemu::ACIOHandle(portName.c_str(), 1);
|
||||
devicehook_init_trampoline();
|
||||
devicehook_add(acioHandle);
|
||||
|
||||
execexe::init();
|
||||
execexe::init_deferred([] () {
|
||||
execexe::init_port_hook(portName, acioHandle);
|
||||
execexe::load_library("libaio.dll");
|
||||
execexe::load_library("libaio-iob.dll");
|
||||
execexe::load_library("libaio-iob_video.dll");
|
||||
execexe::load_library("libaio-iob2_video.dll");
|
||||
execexe::load_library("win10actlog.dll");
|
||||
|
||||
if (!PC_NO_IO) {
|
||||
bi2x_hook_init();
|
||||
}
|
||||
});
|
||||
|
||||
const auto user32Dll = "user32.dll";
|
||||
detour::trampoline_try(user32Dll, "RegisterRawInputDevices",
|
||||
RegisterRawInputDevices_hook, &RegisterRawInputDevices_orig);
|
||||
detour::trampoline_try(user32Dll, "QueryDisplayConfig",
|
||||
QueryDisplayConfig_hook, &QueryDisplayConfig_orig);
|
||||
detour::trampoline_try(user32Dll, "EnumDisplaySettingsW",
|
||||
EnumDisplaySettingsW_hook, &EnumDisplaySettingsW_orig);
|
||||
|
||||
if (GRAPHICS_SHOW_CURSOR) {
|
||||
unity_utils::force_show_cursor(true);
|
||||
}
|
||||
|
||||
unity_utils::set_args(
|
||||
std::format("{} {}{}",
|
||||
GetCommandLineA(),
|
||||
PC_INJECT_ARGS,
|
||||
unity_utils::get_unity_player_args()));
|
||||
}
|
||||
|
||||
void PCGame::detach() {
|
||||
Game::detach();
|
||||
|
||||
devicehook_dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "games/game.h"
|
||||
|
||||
namespace games::pc {
|
||||
extern std::string PC_INJECT_ARGS;
|
||||
extern bool PC_NO_IO;
|
||||
extern bool PC_KNOB_MODE;
|
||||
|
||||
class PCGame : public games::Game {
|
||||
public:
|
||||
PCGame() : Game("Polaris Chord") {}
|
||||
|
||||
virtual void attach() override;
|
||||
virtual void detach() override;
|
||||
};
|
||||
}
|
||||
@@ -14,6 +14,7 @@ namespace games::pcm {
|
||||
static double BILL_DELAY = 2.0;
|
||||
|
||||
static int bill_kind = 0;
|
||||
static int accept_bill_kind = 0;
|
||||
|
||||
static int __cdecl BillVali_GetEscrowBillKind() {
|
||||
return bill_kind;
|
||||
@@ -74,6 +75,12 @@ namespace games::pcm {
|
||||
BILL_IN_TIME = get_performance_seconds();
|
||||
} else if ((BILL_IN_TIME != 0) && ((BILL_IN_TIME + BILL_DELAY) <= get_performance_seconds())) {
|
||||
BILL_IN_TIME = 0;
|
||||
|
||||
if (accept_bill_kind != 0 && accept_bill_kind != bill_kind) {
|
||||
bill_kind = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (bill_kind) {
|
||||
case 1:
|
||||
return 1;
|
||||
@@ -98,7 +105,8 @@ namespace games::pcm {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl BillVali_SetAcceptBill(int) {
|
||||
static bool __cdecl BillVali_SetAcceptBill(int a1) {
|
||||
accept_bill_kind = a1;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -148,5 +156,13 @@ namespace games::pcm {
|
||||
system, "?GetEscrowBillKind@GsBillVali@@SAHXZ"));
|
||||
detour::inline_hook(BillVali_SetAcceptBill, libutils::try_proc(
|
||||
system, "?SetAcceptBill@GsBillVali@@SA_NH@Z"));
|
||||
|
||||
// NOTE: for 2024091200 or newer
|
||||
detour::inline_hook(BillVali_ReceiveBill, libutils::try_proc(
|
||||
system, "?ReceiveBill@GsBillVali@@SA_NXZ"));
|
||||
detour::inline_hook(BillVali_GetEscrowBillKind, libutils::try_proc(
|
||||
system, "?GetEscrowBillKind@GsBillVali@@SA?AW4E_BILLKIND@@XZ"));
|
||||
detour::inline_hook(BillVali_SetAcceptBill, libutils::try_proc(
|
||||
system, "?SetAcceptBill@GsBillVali@@SA_NW4E_BILLKIND@@@Z"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,134 +14,24 @@
|
||||
#include <mfidl.h>
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "hooks/cfgmgr32hook.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/memutils.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
static VTBL_TYPE(IMFActivate, GetAllocatedString) GetAllocatedString_orig = nullptr;
|
||||
|
||||
static decltype(MFEnumDeviceSources) *MFEnumDeviceSources_orig = nullptr;
|
||||
|
||||
namespace games::sdvx {
|
||||
|
||||
static std::wstring CAMERA0_ID;
|
||||
|
||||
static HRESULT WINAPI GetAllocatedString_hook(IMFActivate* This, REFGUID guidKey, LPWSTR *ppwszValue,
|
||||
UINT32 *pcchLength) {
|
||||
// call the original
|
||||
HRESULT result = GetAllocatedString_orig(This, guidKey, ppwszValue, pcchLength);
|
||||
|
||||
// try first camera
|
||||
wchar_t *pwc = nullptr;
|
||||
if (CAMERA0_ID.length() == 23)
|
||||
pwc = wcsstr(*ppwszValue, CAMERA0_ID.c_str());
|
||||
|
||||
// check if camera could be identified
|
||||
if (pwc) {
|
||||
|
||||
// fake the USB IDs
|
||||
pwc[4] = L'2';
|
||||
pwc[5] = L'8';
|
||||
pwc[6] = L'8';
|
||||
pwc[7] = L'c';
|
||||
pwc[13] = L'0';
|
||||
pwc[14] = L'0';
|
||||
pwc[15] = L'0';
|
||||
pwc[16] = L'2';
|
||||
pwc[21] = L'0';
|
||||
pwc[22] = L'0';
|
||||
}
|
||||
|
||||
// return original result
|
||||
return result;
|
||||
}
|
||||
|
||||
static void hook_camera(IMFActivate* camera, size_t no, std::wstring camera_id, std::string camera_instance) {
|
||||
|
||||
// don't hook if camera 0 is already hooked
|
||||
if (CAMERA0_ID.length() > 0)
|
||||
return;
|
||||
|
||||
// save the camera ID
|
||||
CAMERA0_ID = camera_id;
|
||||
|
||||
// cfgmgr hook
|
||||
CFGMGR32_HOOK_SETTING camera_setting;
|
||||
camera_setting.device_instance = 0xDEADBEEF;
|
||||
camera_setting.parent_instance = ~camera_setting.device_instance;
|
||||
camera_setting.device_id = "USB\\VEN_1022&DEV_7908";
|
||||
camera_setting.device_node_id = "USB\\VID_288C&PID_0002&MI_00\\?&????????&?&????";
|
||||
if (camera_instance.length() == 17) {
|
||||
for (int i = 0; i < 17; i++) {
|
||||
camera_setting.device_node_id[28 + i] = camera_instance[i];
|
||||
}
|
||||
}
|
||||
cfgmgr32hook_add(camera_setting);
|
||||
|
||||
// save original method for later use
|
||||
if (GetAllocatedString_orig == nullptr) {
|
||||
GetAllocatedString_orig = camera->lpVtbl->GetAllocatedString;
|
||||
}
|
||||
|
||||
// hook allocated string method for camera identification
|
||||
memutils::VProtectGuard camera_guard(camera->lpVtbl);
|
||||
camera->lpVtbl->GetAllocatedString = GetAllocatedString_hook;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI MFEnumDeviceSources_hook(IMFAttributes *pAttributes, IMFActivate ***pppSourceActivate,
|
||||
UINT32 *pcSourceActivate) {
|
||||
|
||||
// call original function
|
||||
HRESULT result_orig = MFEnumDeviceSources_orig(pAttributes, pppSourceActivate, pcSourceActivate);
|
||||
|
||||
// check for capture devices
|
||||
if (FAILED(result_orig) || !*pcSourceActivate) {
|
||||
return result_orig;
|
||||
}
|
||||
|
||||
// iterate cameras
|
||||
size_t cam_hook_num = 0;
|
||||
for (size_t cam_num = 0; cam_num < *pcSourceActivate && cam_hook_num < 1; cam_num++) {
|
||||
|
||||
// flip
|
||||
size_t cam_num_flipped = cam_num;
|
||||
|
||||
// get camera link
|
||||
IMFActivate *camera = (*pppSourceActivate)[cam_num_flipped];
|
||||
LPWSTR camera_link_lpwstr;
|
||||
UINT32 camera_link_length;
|
||||
if (SUCCEEDED(camera->lpVtbl->GetAllocatedString(
|
||||
camera,
|
||||
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
|
||||
&camera_link_lpwstr,
|
||||
&camera_link_length))) {
|
||||
|
||||
// cut name to make ID
|
||||
std::wstring camera_link_ws = std::wstring(camera_link_lpwstr);
|
||||
std::wstring camera_id = camera_link_ws.substr(8, 23);
|
||||
|
||||
// get camera instance
|
||||
std::string camera_link = ws2s(camera_link_ws);
|
||||
std::string camera_instance = camera_link.substr(32, 17);
|
||||
|
||||
// hook the camera
|
||||
hook_camera(camera, cam_hook_num, camera_id, camera_instance);
|
||||
|
||||
// increase camera hook number
|
||||
cam_hook_num++;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// return result
|
||||
return result_orig;
|
||||
*pppSourceActivate = nullptr;
|
||||
*pcSourceActivate = 0;
|
||||
log_misc("sdvx", "MFEnumDeviceSources_hook called, returning 0 cameras");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void camera_init() {
|
||||
|
||||
// camera media framework hook
|
||||
MFEnumDeviceSources_orig = detour::iat_try(
|
||||
"MFEnumDeviceSources", MFEnumDeviceSources_hook, avs::game::DLL_INSTANCE);
|
||||
log_info("sdvx", "installing camera hooks...");
|
||||
detour::iat_try("MFEnumDeviceSources", MFEnumDeviceSources_hook, avs::game::DLL_INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/shared/lcdhandle.h"
|
||||
#include "games/io.h"
|
||||
#include "hooks/audio/audio.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "hooks/devicehook.h"
|
||||
@@ -12,7 +13,9 @@
|
||||
#include "hooks/powrprof.h"
|
||||
#include "hooks/sleephook.h"
|
||||
#include "hooks/winuser.h"
|
||||
#include "launcher/options.h"
|
||||
#include "touch/touch.h"
|
||||
#include "util/deferlog.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/sigscan.h"
|
||||
@@ -24,6 +27,7 @@
|
||||
#include "io.h"
|
||||
#include "acioemu/handle.h"
|
||||
#include "cfg/configurator.h"
|
||||
#include "launcher/signal.h"
|
||||
|
||||
static decltype(RegCloseKey) *RegCloseKey_orig = nullptr;
|
||||
static decltype(RegEnumKeyA) *RegEnumKeyA_orig = nullptr;
|
||||
@@ -39,7 +43,6 @@ namespace games::sdvx {
|
||||
const char *ORIGINAL_ASIO_DEVICE_NAME = "XONAR SOUND CARD(64)";
|
||||
|
||||
// settings
|
||||
bool DISABLECAMS = false;
|
||||
bool NATIVETOUCH = false;
|
||||
uint8_t DIGITAL_KNOB_SENS = 16;
|
||||
SdvxOverlayPosition OVERLAY_POS = SDVX_OVERLAY_BOTTOM;
|
||||
@@ -105,8 +108,8 @@ namespace games::sdvx {
|
||||
if (ENABLE_COM_PORT_SCAN_HOOK &&
|
||||
lpSubKey != nullptr && phkResult != nullptr &&
|
||||
_stricmp(lpSubKey, "HARDWARE\\DEVICEMAP\\SERIALCOMM") == 0) {
|
||||
log_info("sdvx::io", "failing HKLM\\HARDWARE\\DEVICEMAP\\SERIALCOMM to force COM1 ICCA");
|
||||
return 2; //ERROR_FILE_NOT_FOUND
|
||||
log_info("sdvx::io", "hook access to HKLM\\HARDWARE\\DEVICEMAP\\SERIALCOMM to force COM1 ICCA");
|
||||
return 2; // ERROR_FILE_NOT_FOUND
|
||||
}
|
||||
|
||||
return RegOpenKeyExA_orig(hKey, lpSubKey, ulOptions, samDesired, phkResult);
|
||||
@@ -210,6 +213,19 @@ namespace games::sdvx {
|
||||
out = "";
|
||||
return true;
|
||||
}
|
||||
if (data.find("SuperstepSound: Audio device is not available") != std::string::npos) {
|
||||
deferredlogs::defer_error_messages(deferredlogs::SUPERSTEP_SOUND_ERROR_MESSAGE);
|
||||
return false;
|
||||
}
|
||||
if (data.find("W:ea3-pos: ea3_report_posev: no such node:") != std::string::npos) {
|
||||
deferredlogs::defer_error_messages({
|
||||
"bad prop\\ea3-config.xml detected by game",
|
||||
std::string(" ") + data,
|
||||
" this will most likely cause Server Busy errors in certain conditions",
|
||||
" follow the FAQ to fix your ea3-config.xml and try again"
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -263,7 +279,7 @@ namespace games::sdvx {
|
||||
AUTO_INSERT_CARD_COOLDOWN = 15.f;
|
||||
// check bad model name
|
||||
if (!cfg::CONFIGURATOR_STANDALONE && avs::game::is_model("UFC")) {
|
||||
log_fatal(
|
||||
log_warning(
|
||||
"sdvx",
|
||||
"BAD MODEL NAME ERROR\n\n\n"
|
||||
"!!! model name set to UFC, this is WRONG and will break your game !!!\n"
|
||||
@@ -273,7 +289,41 @@ namespace games::sdvx {
|
||||
"!!! !!!\n"
|
||||
"!!! model name set to UFC, this is WRONG and will break your game !!!\n\n\n"
|
||||
);
|
||||
}
|
||||
log_fatal(
|
||||
"sdvx",
|
||||
"BAD MODEL NAME ERROR - model name set to UFC, must be KFC instead");
|
||||
}
|
||||
|
||||
#ifdef SPICE64 // SDVX5+ specific code
|
||||
bool isValkyrieCabinetMode = avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H';
|
||||
auto options = games::get_options(eamuse_get_game());
|
||||
// check -monitor + UFC mode
|
||||
if (!GRAPHICS_WINDOWED &&
|
||||
options->at(launcher::Options::DisplayAdapter).is_active() &&
|
||||
isValkyrieCabinetMode) {
|
||||
log_warning(
|
||||
"sdvx",
|
||||
"\n\n!!! using -monitor option with VM mode is NOT recommended due to !!!\n"
|
||||
"!!! known 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 NOT use -monitor and instead set the !!!\n"
|
||||
"!!! primary monitor in Windows settings before launching the game !!!\n\n"
|
||||
);
|
||||
|
||||
deferredlogs::defer_error_messages({
|
||||
"-monitor option is NOT recommended when running with Valkyrie mode",
|
||||
" 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",
|
||||
" recommended fix is to NOT use -monitor and instead set the primary",
|
||||
" monitor in Windows settings before launching the game"
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void SDVXGame::attach() {
|
||||
@@ -369,17 +419,17 @@ namespace games::sdvx {
|
||||
winuser_hook_init(avs::game::DLL_INSTANCE);
|
||||
|
||||
// hook camera
|
||||
if (!DISABLECAMS) {
|
||||
camera_init();
|
||||
}
|
||||
camera_init();
|
||||
|
||||
// RGB CAMERA error ignore
|
||||
if (!replace_pattern(
|
||||
// RGB CAMERA error ignore for SDVX5
|
||||
// SDVX5: boot sequence triggers camera error if camera is not detected
|
||||
// SDVX6: boots fine, but game title screen in attract loop will have camera error (cosmetic only)
|
||||
if (replace_pattern(
|
||||
avs::game::DLL_INSTANCE,
|
||||
"418D480484C074218D51FD",
|
||||
"????????????9090??????",
|
||||
0, 0)) {
|
||||
log_info("sdvx", "did not find matching signature for camera error patch");
|
||||
log_info("sdvx", "applied camera error patch (sdvx5)");
|
||||
}
|
||||
|
||||
// remove log spam
|
||||
|
||||
@@ -17,7 +17,6 @@ namespace games::sdvx {
|
||||
};
|
||||
|
||||
// settings
|
||||
extern bool DISABLECAMS;
|
||||
extern bool NATIVETOUCH;
|
||||
extern uint8_t DIGITAL_KNOB_SENS;
|
||||
extern std::optional<std::string> ASIO_DRIVER;
|
||||
|
||||
@@ -15,16 +15,19 @@
|
||||
#include "cfg/screen_resize.h"
|
||||
#include "games/iidx/iidx.h"
|
||||
#include "games/sdvx/sdvx.h"
|
||||
#include "games/mfc/mfc.h"
|
||||
#include "games/io.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "launcher/options.h"
|
||||
#include "launcher/signal.h"
|
||||
#include "launcher/shutdown.h"
|
||||
#include "misc/clipboard.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "misc/wintouchemu.h"
|
||||
#include "overlay/overlay.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/deferlog.h"
|
||||
#include "util/flags_helper.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
@@ -203,15 +206,50 @@ static std::string presentation_interval2s(UINT presentation_interval) {
|
||||
|
||||
static void update_backbuffer_dimensions(D3DPRESENT_PARAMETERS *params);
|
||||
|
||||
static void log_create_device_failure(HRESULT hresult) {
|
||||
// only print once since some games will try CreateDevice multiple times on failure
|
||||
static std::once_flag printed;
|
||||
std::call_once(printed, [hresult]() {
|
||||
// special case for popn
|
||||
if (avs::game::is_model("M39")) {
|
||||
deferredlogs::defer_error_messages({
|
||||
fmt::format("D3D9 CreateDevice/CreateDeviceEx failed with {:#x}!", (UINT)hresult),
|
||||
" possible popn music HD mode resolution issue",
|
||||
" popn HD mode launches at 1360x768 (and NOT 1366x768) which is often",
|
||||
" unsupported by many monitors; here are some possible workarounds:",
|
||||
" * enable GPU resolution scaling in your GPU settings",
|
||||
" * use Force Full Screen Resolution option, combined with image scaling (F11)",
|
||||
" * run in windowed mode",
|
||||
" * run in SD mode"
|
||||
});
|
||||
} else {
|
||||
deferredlogs::defer_error_messages({
|
||||
fmt::format("D3D9 CreateDevice/CreateDeviceEx failed with {:#x}!", (UINT)hresult),
|
||||
" this is a common graphics / monitor issue",
|
||||
" * double check any graphics options you configured in spicecfg",
|
||||
" * double check that your monitor supports the resolution + refresh rate",
|
||||
" combination that the game requires",
|
||||
" * enable GPU-side resolution scaling in your GPU options as needed",
|
||||
" * if you have three or more monitors, try unplugging them down to one or two,",
|
||||
" or enable -graphics-force-single-adapter option",
|
||||
" * failing all that, see if enabling windowed mode helps"
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static bool is_dx9_on_12_enabled() {
|
||||
bool result = false;
|
||||
switch (GRAPHICS_9_ON_12_STATE) {
|
||||
case DX9ON12_FORCE_OFF:
|
||||
log_info("graphics::d3d9", "DirectX 9on12: forced OFF by user (-dx9on12)");
|
||||
return false;
|
||||
result = false;
|
||||
break;
|
||||
|
||||
case DX9ON12_FORCE_ON:
|
||||
log_info("graphics::d3d9", "DirectX 9on12: forced ON by user (-9on12 or -dx9on12)");
|
||||
return true;
|
||||
result = true;
|
||||
break;
|
||||
|
||||
case DX9ON12_AUTO:
|
||||
default:
|
||||
@@ -219,14 +257,33 @@ static bool is_dx9_on_12_enabled() {
|
||||
log_info(
|
||||
"graphics::d3d9",
|
||||
"DirectX 9on12: enabled automatically for current game (tip: use -dx9on12 to force on or off)");
|
||||
return true;
|
||||
result = true;
|
||||
} else {
|
||||
log_info(
|
||||
"graphics::d3d9",
|
||||
"DirectX 9on12: disabled by default, using DX9 (tip: use -dx9on12 to force on or off)");
|
||||
return false;
|
||||
result = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (GRAPHICS_9_ON_12_STATE == DX9ON12_FORCE_ON) {
|
||||
if (avs::game::is_model("LDJ") && avs::game::is_ext(2023091500, MAXINT)) {
|
||||
deferredlogs::defer_error_messages({
|
||||
"dx9on12 was force enabled by user (-dx9on12)",
|
||||
" IIDX31+ is known to be incompatible with DX 9on12, leading to blank screen or crashes",
|
||||
" try again with -dx9on12 option set to default value"
|
||||
});
|
||||
} else {
|
||||
deferredlogs::defer_error_messages({
|
||||
"dx9on12 was force enabled by user (-dx9on12)",
|
||||
" not very game is compatible with this, and can lead to crashes",
|
||||
" try without force enabling this if you are seeing issues"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static IDirect3D9 *WINAPI Direct3DCreate9_hook(UINT SDKVersion) {
|
||||
@@ -775,6 +832,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDevice(
|
||||
|
||||
// log error
|
||||
log_info("graphics::d3d9", "IDirect3D9::CreateDevice failed, hr={}", FMT_HRESULT(ret));
|
||||
log_create_device_failure(ret);
|
||||
} else if (!D3D9_DEVICE_HOOK_DISABLE) {
|
||||
graphics_hook_window(hFocusWindow, pPresentationParameters);
|
||||
|
||||
@@ -994,6 +1052,8 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
||||
|
||||
// log error
|
||||
log_warning("graphics::d3d9", "CreateDeviceEx failed, hr={}", FMT_HRESULT(result));
|
||||
log_create_device_failure(result);
|
||||
|
||||
} else if (!D3D9_DEVICE_HOOK_DISABLE) {
|
||||
graphics_hook_window(hFocusWindow, pPresentationParameters);
|
||||
|
||||
@@ -1301,6 +1361,11 @@ void graphics_d3d9_on_present(
|
||||
graphics_d3d9_ldj_on_present(wrapped_device);
|
||||
}
|
||||
|
||||
const bool is_mfc = avs::game::is_model("KK9") && games::mfc::HG_MODE;
|
||||
if (is_mfc) {
|
||||
wintouchemu::update();
|
||||
}
|
||||
|
||||
// check screenshot key
|
||||
static bool trigger_last = false;
|
||||
auto buttons = games::get_buttons_overlay(eamuse_get_game());
|
||||
|
||||
@@ -733,6 +733,16 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||
const int rectTop = param.BackBufferHeight;
|
||||
const int w = param.BackBufferWidth;
|
||||
const int h = param.BackBufferHeight;
|
||||
|
||||
if (cfg::SCREENRESIZE->enable_screen_resize) {
|
||||
RECT rect {
|
||||
0,
|
||||
0,
|
||||
(LONG)topSurface_width,
|
||||
(LONG)topSurface_height,
|
||||
};
|
||||
pReal->ColorFill(topSurface, &rect, D3DCOLOR_XRGB(0, 0, 0));
|
||||
}
|
||||
|
||||
D3DLOCKED_RECT rect;
|
||||
HRESULT hr;
|
||||
@@ -750,6 +760,7 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||
(LONG)(rectLeft + w),
|
||||
(LONG)(rectTop + h),
|
||||
};
|
||||
|
||||
if (GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||
originRect.left = (topSurface_width / 2) - (GRAPHICS_FS_ORIGINAL_WIDTH / 2);
|
||||
originRect.top = (topSurface_height / 2) - (GRAPHICS_FS_ORIGINAL_HEIGHT / 2);
|
||||
@@ -757,6 +768,21 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||
originRect.bottom = originRect.top + GRAPHICS_FS_ORIGINAL_HEIGHT;
|
||||
}
|
||||
|
||||
bool duplicate = false;
|
||||
RECT originRect2 = originRect;
|
||||
if (cfg::SCREENRESIZE->enable_screen_resize) {
|
||||
auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene];
|
||||
if (scene.duplicate == cfg::ScreenDuplicateMode::CopyLeft) {
|
||||
duplicate = true;
|
||||
originRect2.right = originRect.left;
|
||||
originRect2.left = (LONG)(originRect2.left - (originRect.right - originRect.left));
|
||||
} else if (scene.duplicate == cfg::ScreenDuplicateMode::CopyRight) {
|
||||
duplicate = true;
|
||||
originRect2.left = originRect.right;
|
||||
originRect2.right = (LONG)(originRect2.left + (originRect.right - originRect.left));
|
||||
}
|
||||
}
|
||||
|
||||
// log_misc(
|
||||
// "graphics::d3d9", "originRect: {} {} {} {}",
|
||||
// originRect.left, originRect.top, originRect.right, originRect.bottom);
|
||||
@@ -768,9 +794,21 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||
if (hr != D3D_OK) {
|
||||
log_warning(
|
||||
"graphics::d3d9",
|
||||
"SurfaceHook - StretchRect backbuffer failed, rect: {} {} {} {}",
|
||||
"SurfaceHook - StretchRect backbuffer failed for main image, rect: {} {} {} {}",
|
||||
originRect.left, originRect.top, originRect.right, originRect.bottom);
|
||||
}
|
||||
if (duplicate) {
|
||||
hr = pReal->StretchRect(
|
||||
backbuffer, nullptr,
|
||||
topSurface, &originRect2,
|
||||
D3DTEXF_LINEAR);
|
||||
if (hr != D3D_OK) {
|
||||
log_warning(
|
||||
"graphics::d3d9",
|
||||
"SurfaceHook - StretchRect backbuffer failed for the duplicate, rect: {} {} {} {}",
|
||||
originRect2.left, originRect2.top, originRect2.right, originRect2.bottom);
|
||||
}
|
||||
}
|
||||
topSurface->UnlockRect();
|
||||
|
||||
// phase 2 - viewport calculation - do the actual zoom / offset math
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <ntstatus.h>
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/iidx/iidx.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
@@ -18,9 +19,11 @@ constexpr UINT CODEPAGE_SHIFT_JIS = 932;
|
||||
static decltype(GetACP) *GetACP_orig = nullptr;
|
||||
static decltype(GetOEMCP) *GetOEMCP_orig = nullptr;
|
||||
static decltype(MultiByteToWideChar) *MultiByteToWideChar_orig = nullptr;
|
||||
static decltype(GetLocaleInfoEx) *GetLocaleInfoEx_orig = nullptr;
|
||||
|
||||
#ifdef SPICE64
|
||||
static decltype(GetSystemDefaultLCID) *GetSystemDefaultLCID_orig = nullptr;
|
||||
static decltype(IsDBCSLeadByte) *IsDBCSLeadByte_orig = nullptr;
|
||||
#endif
|
||||
|
||||
static NTSTATUS NTAPI RtlMultiByteToUnicodeN_hook(
|
||||
@@ -119,6 +122,59 @@ static int WINAPI MultiByteToWideChar_hook(
|
||||
cchWideChar);
|
||||
}
|
||||
|
||||
static int WINAPI GetLocaleInfoEx_hook (
|
||||
LPCWSTR lpLocaleName,
|
||||
LCTYPE LCType,
|
||||
LPWSTR lpLCData,
|
||||
int cchData)
|
||||
{
|
||||
|
||||
#if 0
|
||||
if (lpLocaleName == LOCALE_NAME_INVARIANT) {
|
||||
log_misc("hooks::lang", "GetLocaleInfoEx_hook hit (LOCALE_NAME_INVARIANT), {}, {}", LCType, cchData);
|
||||
} else if (lpLocaleName == LOCALE_NAME_SYSTEM_DEFAULT) {
|
||||
log_misc("hooks::lang", "GetLocaleInfoEx_hook hit (LOCALE_NAME_SYSTEM_DEFAULT), {}, {}", LCType, cchData);
|
||||
} else if (lpLocaleName == LOCALE_NAME_USER_DEFAULT) {
|
||||
log_misc("hooks::lang", "GetLocaleInfoEx_hook hit (LOCALE_NAME_USER_DEFAULT), {}, {}", LCType, cchData);
|
||||
} else {
|
||||
log_misc("hooks::lang", "GetLocaleInfoEx_hook hit ({}), {}, {}", ws2s(lpLocaleName), LCType, cchData);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (lpLocaleName == LOCALE_NAME_USER_DEFAULT &&
|
||||
LCType == LOCALE_SISO639LANGNAME &&
|
||||
lpLCData != NULL &&
|
||||
cchData >= 3) {
|
||||
log_misc("hooks::lang",
|
||||
"GetLocaleInfoEx_hook hit (LOCALE_NAME_USER_DEFAULT / LOCALE_SISO639LANGNAME), return `ja`");
|
||||
wcscpy(lpLCData, L"ja");
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (lpLocaleName == LOCALE_NAME_USER_DEFAULT &&
|
||||
LCType == LOCALE_SISO3166CTRYNAME &&
|
||||
lpLCData != NULL &&
|
||||
cchData >= 3) {
|
||||
log_misc("hooks::lang",
|
||||
"GetLocaleInfoEx_hook hit (LOCALE_NAME_USER_DEFAULT / LOCALE_SISO3166CTRYNAME), return `JP`");
|
||||
wcscpy(lpLCData, L"JP");
|
||||
return 3;
|
||||
}
|
||||
|
||||
return GetLocaleInfoEx_orig(lpLocaleName, LCType, lpLCData, cchData);
|
||||
}
|
||||
|
||||
#ifdef SPICE64
|
||||
|
||||
static BOOL WINAPI IsDBCSLeadByte_hook (
|
||||
BYTE TestChar
|
||||
)
|
||||
{
|
||||
return IsDBCSLeadByteEx(CODEPAGE_SHIFT_JIS, TestChar);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void hooks::lang::early_init() {
|
||||
log_info("hooks::lang", "early initialization");
|
||||
|
||||
@@ -138,6 +194,27 @@ void hooks::lang::early_init() {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (avs::game::is_model("XIF")) {
|
||||
log_info("hooks::lang", "hooking GetLocaleInfoEx");
|
||||
detour::trampoline_try(
|
||||
"kernel32.dll",
|
||||
"GetLocaleInfoEx",
|
||||
GetLocaleInfoEx_hook,
|
||||
&GetLocaleInfoEx_orig);
|
||||
}
|
||||
|
||||
#ifdef SPICE64
|
||||
// for TDJ subscreen search keyboard
|
||||
if (avs::game::is_model("LDJ") && games::iidx::TDJ_MODE) {
|
||||
log_info("hooks::lang", "hooking IsDBCSLeadByte");
|
||||
detour::trampoline_try(
|
||||
"kernel32.dll",
|
||||
"IsDBCSLeadByte",
|
||||
IsDBCSLeadByte_hook,
|
||||
&IsDBCSLeadByte_orig);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void hooks::lang::init() {
|
||||
|
||||
@@ -141,13 +141,17 @@ static ULONG WINAPI GetAdaptersInfo_hook(PIP_ADAPTER_INFO pAdapterInfo, PULONG p
|
||||
|
||||
static int WINAPI bind_hook(SOCKET s, const struct sockaddr *name, int namelen) {
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma ide diagnostic ignored "OCDFAInspection"
|
||||
#endif
|
||||
|
||||
// cast to sockaddr_in
|
||||
struct sockaddr_in *in_name = (struct sockaddr_in *) name;
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
// override bind to allow all hosts
|
||||
in_name->sin_addr.s_addr = inet_addr("0.0.0.0");
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <assert.h>
|
||||
#include <shlwapi.h>
|
||||
#include <cfg/configurator.h>
|
||||
|
||||
@@ -20,7 +21,9 @@
|
||||
#include "cfg/screen_resize.h"
|
||||
#include "easrv/easrv.h"
|
||||
#include "external/cardio/cardio_runner.h"
|
||||
#ifndef NO_SCARD
|
||||
#include "external/scard/scard.h"
|
||||
#endif
|
||||
#include "games/game.h"
|
||||
#include "games/io.h"
|
||||
#include "games/bbc/bbc.h"
|
||||
@@ -61,6 +64,7 @@
|
||||
#include "games/ccj/trackball.h"
|
||||
#include "games/qks/qks.h"
|
||||
#include "games/mfg/mfg.h"
|
||||
#include "games/pc/pc.h"
|
||||
#include "games/museca/museca.h"
|
||||
#include "hooks/avshook.h"
|
||||
#include "hooks/audio/audio.h"
|
||||
@@ -96,6 +100,7 @@
|
||||
#include "touch/touch.h"
|
||||
#include "util/cpuutils.h"
|
||||
#include "util/crypt.h"
|
||||
#include "util/deferlog.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
@@ -150,6 +155,9 @@ static bool check_dll(const std::string &model) {
|
||||
|
||||
void update_msvcrt_args(int argc, char *argv[]);
|
||||
|
||||
void dump_button_bindings(std::vector<Button> *buttons);
|
||||
void dump_analog_bindings();
|
||||
|
||||
int main_implementation(int argc, char *argv[]) {
|
||||
|
||||
// remember argv, argv
|
||||
@@ -219,6 +227,7 @@ int main_implementation(int argc, char *argv[]) {
|
||||
bool attach_ccj = false;
|
||||
bool attach_qks = false;
|
||||
bool attach_mfg = false;
|
||||
bool attach_pc = false;
|
||||
bool attach_museca = false;
|
||||
bool show_cursor_if_no_touch = false;
|
||||
|
||||
@@ -257,7 +266,8 @@ int main_implementation(int argc, char *argv[]) {
|
||||
|
||||
// detect model used to load option overrides
|
||||
auto options_version = launcher::detect_gameversion(
|
||||
LAUNCHER_OPTIONS->at(launcher::Options::PathToEa3Config).value
|
||||
LAUNCHER_OPTIONS->at(launcher::Options::PathToEa3Config).value,
|
||||
LAUNCHER_OPTIONS->at(launcher::Options::PathToBootstrap).value
|
||||
);
|
||||
if (!options_version.model.empty() && options_version.model.size() < 4) {
|
||||
if (options_version.dest.size() == 1) {
|
||||
@@ -291,25 +301,13 @@ int main_implementation(int argc, char *argv[]) {
|
||||
cfg_run = true;
|
||||
}
|
||||
|
||||
// -ea, but ignored if -url is set
|
||||
if (options[launcher::Options::EAmusementEmulation].value_bool() &&
|
||||
options[launcher::Options::ServiceURL].is_active() &&
|
||||
!cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_fatal(
|
||||
"launcher",
|
||||
"BAD EAMUSE SETTINGS ERROR\n\n\n"
|
||||
"-------------------------------------------------------------------\n"
|
||||
"WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING\n"
|
||||
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
|
||||
"A service URL is set **AND** E-Amusement emulation is enabled.\n"
|
||||
"Either remove the service URL, or disable E-Amusement emulation.\n"
|
||||
"Otherwise you may experience problems logging in.\n"
|
||||
"-------------------------------------------------------------------\n\n\n"
|
||||
);
|
||||
}
|
||||
if (options[launcher::Options::EAmusementEmulation].value_bool()) {
|
||||
!options[launcher::Options::ServiceURL].is_active()) {
|
||||
avs::ea3::URL_SLASH = 1;
|
||||
easrv_port = 8080;
|
||||
}
|
||||
|
||||
if (options[launcher::Options::SmartEAmusement].value_bool()) {
|
||||
easrv_smart = true;
|
||||
}
|
||||
@@ -360,6 +358,9 @@ int main_implementation(int argc, char *argv[]) {
|
||||
if (options[launcher::Options::VerboseAVSLogging].value_bool()) {
|
||||
hooks::avs::config::LOG = true;
|
||||
}
|
||||
if (options[launcher::Options::AllowEA3Verbose].value_bool()) {
|
||||
avs::ea3::EA3_DEBUG_VERBOSE = true;
|
||||
}
|
||||
if (options[launcher::Options::spice2x_AutoOrientation].is_active()) {
|
||||
GRAPHICS_ADJUST_ORIENTATION =
|
||||
(graphics_orientation)options[launcher::Options::spice2x_AutoOrientation].value_uint32();
|
||||
@@ -430,9 +431,6 @@ int main_implementation(int argc, char *argv[]) {
|
||||
if (options[launcher::Options::LoadSoundVoltexModule].value_bool()) {
|
||||
attach_sdvx = true;
|
||||
}
|
||||
if (options[launcher::Options::SDVXDisableCameras].value_bool()) {
|
||||
games::sdvx::DISABLECAMS = true;
|
||||
}
|
||||
if (options[launcher::Options::SDVXNativeTouch].value_bool()) {
|
||||
games::sdvx::NATIVETOUCH = true;
|
||||
}
|
||||
@@ -465,14 +463,26 @@ int main_implementation(int argc, char *argv[]) {
|
||||
if (options[launcher::Options::IIDXCameraOrderFlip].value_bool()) {
|
||||
games::iidx::FLIP_CAMS = true;
|
||||
}
|
||||
|
||||
// IIDX CONNECT_CAMERA logic here for cases where user is running without -iidx module
|
||||
// for now, we assume that user may be running on a cab
|
||||
// (games::iidx::DISABLE_CAMS starts out as false unless user overrides)
|
||||
// we will check again in IIDX module with a different default
|
||||
assert(!games::iidx::DISABLE_CAMS.has_value());
|
||||
if (options[launcher::Options::IIDXDisableCameras].value_bool()) {
|
||||
games::iidx::DISABLE_CAMS = true;
|
||||
}
|
||||
if (options[launcher::Options::IIDXCabCamAccess].is_active() &&
|
||||
options[launcher::Options::IIDXCabCamAccess].value_text() == "off") {
|
||||
games::iidx::DISABLE_CAMS = true;
|
||||
}
|
||||
if (options[launcher::Options::IIDXCamHook].value_bool()) {
|
||||
games::iidx::TDJ_CAMERA = true;
|
||||
// Disable legacy behaviour to avoid conflict
|
||||
games::iidx::DISABLE_CAMS = true;
|
||||
}
|
||||
// CONNECT_CAMERA env var will be set once logging is enabled
|
||||
|
||||
if (options[launcher::Options::IIDXCamHookOverride].is_active()) {
|
||||
games::iidx::TDJ_CAMERA_OVERRIDE = options[launcher::Options::IIDXCamHookOverride].value_text();
|
||||
}
|
||||
@@ -623,12 +633,18 @@ int main_implementation(int argc, char *argv[]) {
|
||||
if (options[launcher::Options::LoadMFGModule].value_bool()) {
|
||||
attach_mfg = true;
|
||||
}
|
||||
if (options[launcher::Options::LoadPCModule].value_bool()) {
|
||||
attach_pc = true;
|
||||
}
|
||||
if (options[launcher::Options::LoadMusecaModule].value_bool()) {
|
||||
attach_museca = true;
|
||||
}
|
||||
if (options[launcher::Options::DDR43Mode].value_bool()) {
|
||||
games::ddr::SDMODE = true;
|
||||
}
|
||||
if (options[launcher::Options::DDRSkipCodecRegisteration].value_bool()) {
|
||||
games::ddr::NO_CODEC_REGISTRATION = true;
|
||||
}
|
||||
if (options[launcher::Options::LoadSteelChronicleModule].value_bool()) {
|
||||
attach_sc = true;
|
||||
}
|
||||
@@ -804,6 +820,7 @@ int main_implementation(int argc, char *argv[]) {
|
||||
for (auto &sextet : options[launcher::Options::SextetStreamPort].values_text()) {
|
||||
sextet_devices.emplace_back(sextet);
|
||||
}
|
||||
#ifndef NO_SCARD
|
||||
if (options[launcher::Options::HIDSmartCard].value_bool()) {
|
||||
WINSCARD_CONFIG.cardinfo_callback = eamuse_scard_callback;
|
||||
scard_threadstart();
|
||||
@@ -823,6 +840,7 @@ int main_implementation(int argc, char *argv[]) {
|
||||
WINSCARD_CONFIG.add_padding_to_felica = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (options[launcher::Options::CardIOHIDReaderOrderFlip].value_bool()) {
|
||||
CARDIO_RUNNER_FLIP = true;
|
||||
}
|
||||
@@ -926,6 +944,11 @@ int main_implementation(int argc, char *argv[]) {
|
||||
}
|
||||
if (options[launcher::Options::DisableAudioHooks].value_bool()) {
|
||||
hooks::audio::ENABLED = false;
|
||||
deferredlogs::defer_error_messages({
|
||||
"audio hooks are forcibly disabled by user (-audiohookdisable)",
|
||||
" having hooks disabled means some required audio fixes are not being applied",
|
||||
" if you have audio-related crashes, you should try again after clearing this option"
|
||||
});
|
||||
}
|
||||
if (options[launcher::Options::spice2x_DisableVolumeHook].value_bool()) {
|
||||
hooks::audio::VOLUME_HOOK_ENABLED = false;
|
||||
@@ -1098,6 +1121,15 @@ int main_implementation(int argc, char *argv[]) {
|
||||
if (options[launcher::Options::MFGNoIO].is_active()) {
|
||||
games::mfg::MFG_NO_IO = options[launcher::Options::MFGNoIO].value_bool();
|
||||
}
|
||||
if (options[launcher::Options::PCArgs].is_active()) {
|
||||
games::pc::PC_INJECT_ARGS = options[launcher::Options::PCArgs].value_text();
|
||||
}
|
||||
if (options[launcher::Options::PCNoIO].is_active()) {
|
||||
games::pc::PC_NO_IO = options[launcher::Options::PCNoIO].value_bool();
|
||||
}
|
||||
if (options[launcher::Options::PCKnobMode].value_bool()) {
|
||||
games::pc::PC_KNOB_MODE = true;
|
||||
}
|
||||
if (options[launcher::Options::spice2x_EnableSMXStage].value_bool()) {
|
||||
rawinput::ENABLE_SMX_STAGE = true;
|
||||
}
|
||||
@@ -1179,15 +1211,28 @@ int main_implementation(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
// log
|
||||
#ifdef SPICE64
|
||||
log_info("launcher", "SpiceTools Bootstrap (x64) (spice2x)");
|
||||
#elif SPICE32_LARGE_ADDRESS_AWARE
|
||||
log_info("launcher", "SpiceTools Bootstrap (x32) (Large Address Aware) (spice2x)");
|
||||
#ifndef SPICE_LINUX
|
||||
#ifdef SPICE64
|
||||
log_info("launcher", "SpiceTools Bootstrap (x64) (spice2x)");
|
||||
#elif SPICE32_LARGE_ADDRESS_AWARE
|
||||
log_info("launcher", "SpiceTools Bootstrap (x32 - Large Address Aware) (spice2x)");
|
||||
#else
|
||||
log_info("launcher", "SpiceTools Bootstrap (x32) (spice2x)");
|
||||
#endif
|
||||
#else
|
||||
log_info("launcher", "SpiceTools Bootstrap (x32) (spice2x)");
|
||||
#ifdef SPICE64
|
||||
log_info("launcher", "SpiceTools Bootstrap (x64) (spice2x) for Linux");
|
||||
#else
|
||||
log_info("launcher", "SpiceTools Bootstrap (x32) (spice2x) for Linux");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
log_info("launcher", "{}", VERSION_STRING);
|
||||
|
||||
// note: distribution of modified version of this software without providing source is GPLv3 license violation.
|
||||
log_info("launcher", "spice2x is free & open source; if you paid money for it, you got scammed");
|
||||
log_info("launcher", "visit https://spice2x.github.io to download the latest version for free");
|
||||
|
||||
// log command line arguments
|
||||
std::ostringstream arguments;
|
||||
for (auto &root_option : options) {
|
||||
@@ -1231,18 +1276,19 @@ int main_implementation(int argc, char *argv[]) {
|
||||
for (const auto &option : options) {
|
||||
if (option.conflicting && option.get_definition().type != OptionType::Bool) {
|
||||
conflicts += 1;
|
||||
const auto& value = option.get_definition().sensitive ? "*****" : option.value;
|
||||
if (launcher::USE_CMD_OVERRIDE) {
|
||||
log_warning(
|
||||
"launcher",
|
||||
"multiple values for -{}, command line args take precedence: {}",
|
||||
option.get_definition().name,
|
||||
option.value);
|
||||
value);
|
||||
} else {
|
||||
log_warning(
|
||||
"launcher",
|
||||
"multiple values for -{}, spicecfg values take precedence: {}",
|
||||
option.get_definition().name,
|
||||
option.value);
|
||||
value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1258,6 +1304,19 @@ int main_implementation(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (options[launcher::Options::GameExecutable].is_active() && !cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_warning(
|
||||
"launcher",
|
||||
"WARNING - user specified -exec option\n\n\n"
|
||||
"!!! !!!\n"
|
||||
"!!! Using -exec option disables all game-specific hooks! !!!\n"
|
||||
"!!! Unless you know exactly what you are doing, clear -exec !!!\n"
|
||||
"!!! and try again. Using -exec by itself will result in weird !!!\n"
|
||||
"!!! errors and loss of functionality. !!!\n"
|
||||
"!!! !!!\n"
|
||||
);
|
||||
}
|
||||
|
||||
if (options[launcher::Options::FullscreenResolution].is_active()) {
|
||||
std::pair<uint32_t, uint32_t> result;
|
||||
if (parse_width_height(options[launcher::Options::FullscreenResolution].value_text(), result)) {
|
||||
@@ -1269,7 +1328,7 @@ int main_implementation(int argc, char *argv[]) {
|
||||
|
||||
// SDVXFullscreenLandscape disabled due to it messing with in-game camera angle
|
||||
// FullscreenOrientationFlip continues to live on, however.
|
||||
if (options[launcher::Options::SDVXFullscreenLandscape].value_bool()) {
|
||||
if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_fatal("launcher", "-sdvxlandscape removed due to a camera bug in SDVX!");
|
||||
}
|
||||
// if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !GRAPHICS_WINDOWED) {
|
||||
@@ -1284,8 +1343,15 @@ int main_implementation(int argc, char *argv[]) {
|
||||
GRAPHICS_FS_ORIENTATION_SWAP = true;
|
||||
}
|
||||
|
||||
if (games::iidx::DISABLE_CAMS.has_value() &&
|
||||
games::iidx::DISABLE_CAMS.value() &&
|
||||
!cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_misc("launcher", "CONNECT_CAMERA env var set to 0");
|
||||
SetEnvironmentVariable("CONNECT_CAMERA", "0");
|
||||
}
|
||||
|
||||
// deleted options
|
||||
if (options[launcher::Options::OpenKFControl].value_bool()) {
|
||||
if (options[launcher::Options::OpenKFControl].value_bool() && !cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_fatal("launcher", "KFControl has been removed from spice2x; please use an older version.");
|
||||
}
|
||||
if (options[launcher::Options::VREnable].value_bool()) {
|
||||
@@ -1342,7 +1408,11 @@ int main_implementation(int argc, char *argv[]) {
|
||||
log_info("launcher", "loading early hook DLL {}", hook);
|
||||
HMODULE module;
|
||||
if (!(module = libutils::try_library(hook))) {
|
||||
log_warning("launcher", "failed to load early hook {}", hook);
|
||||
log_warning("launcher", "failed to load early hook {}: {}", hook, get_last_error_string());
|
||||
deferredlogs::defer_error_messages({
|
||||
fmt::format("failed to load early hook {}:", hook),
|
||||
fmt::format(" {}", get_last_error_string())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1609,6 +1679,14 @@ int main_implementation(int argc, char *argv[]) {
|
||||
attach_io = true;
|
||||
attach_mfc = true;
|
||||
break;
|
||||
|
||||
// Mahjong Fight Club - 2025?
|
||||
// they removed allinone.dll and moved i/o into system.dll
|
||||
} else if (check_dll("system.dll") && fileutils::file_exists("data/mfc.ini")) {
|
||||
avs::game::DLL_NAME = "system.dll";
|
||||
attach_io = true;
|
||||
attach_mfc = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// FutureTomTom
|
||||
@@ -1719,6 +1797,16 @@ int main_implementation(int argc, char *argv[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Polaris Chord
|
||||
if (check_dll("kamunity.dll") && fileutils::dir_exists("game/svm_Data")) {
|
||||
avs::game::DLL_NAME = "kamunity.dll";
|
||||
attach_io = true;
|
||||
attach_pc = true;
|
||||
launcher::signal::USE_VEH_WORKAROUND = true;
|
||||
show_cursor_if_no_touch = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Busou Shinki: Armored Princess Battle Conductor
|
||||
if (check_dll("kamunity.dll") && fileutils::file_exists("game/bsac_app.exe")) {
|
||||
avs::game::DLL_NAME = "kamunity.dll";
|
||||
@@ -1743,7 +1831,16 @@ int main_implementation(int argc, char *argv[]) {
|
||||
// usage error
|
||||
if (!cfg::CONFIGURATOR_STANDALONE
|
||||
&& (!CHECK_DLL_IGNORE_ARCH)) {
|
||||
log_fatal("launcher", "module auto detection failed.");
|
||||
log_warning(
|
||||
"launcher",
|
||||
"module auto detection failed!\n\n"
|
||||
"This usually means one of the following:\n\n"
|
||||
" 1. You put spice executables in the wrong place, or\n"
|
||||
" 2. XML files in prop directory are malformed or missing, or\n"
|
||||
" 3. You passed in invalid options (usually, path overrides like -exec)\n\n"
|
||||
);
|
||||
|
||||
log_fatal("launcher", "module auto detection failed, see log.txt for troubleshooting");
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1871,6 +1968,10 @@ int main_implementation(int argc, char *argv[]) {
|
||||
avs::core::set_default_heap_size("kamunity.dll");
|
||||
games.push_back(new games::mfg::MFGGame());
|
||||
}
|
||||
if (attach_pc) {
|
||||
avs::core::HEAP_SIZE = 536870912; // 512MB
|
||||
games.push_back(new games::pc::PCGame());
|
||||
}
|
||||
|
||||
// apply user heap size, if defined
|
||||
if (user_heap_size > 0) {
|
||||
@@ -1915,6 +2016,8 @@ int main_implementation(int argc, char *argv[]) {
|
||||
libutils::warn_if_dll_exists("nvcuda.dll");
|
||||
libutils::warn_if_dll_exists("nvcuvid.dll");
|
||||
libutils::warn_if_dll_exists("nvEncodeAPI64.dll");
|
||||
libutils::warn_if_dll_exists("msvcr100.dll");
|
||||
libutils::warn_if_dll_exists("dsound.dll");
|
||||
|
||||
// complain loudly & early about dll load ordering issue
|
||||
libutils::check_duplicate_dlls();
|
||||
@@ -1936,6 +2039,21 @@ int main_implementation(int argc, char *argv[]) {
|
||||
// print devices
|
||||
RI_MGR->devices_print();
|
||||
|
||||
auto buttons = games::get_buttons(eamuse_get_game());
|
||||
log_misc("rawinput", "Button mappings:");
|
||||
dump_button_bindings(buttons);
|
||||
|
||||
log_misc("rawinput", "Keypad button mappings:");
|
||||
auto keypads = games::get_buttons_keypads(eamuse_get_game());
|
||||
dump_button_bindings(keypads);
|
||||
|
||||
log_misc("rawinput", "Overlay button mappings:");
|
||||
auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game());
|
||||
dump_button_bindings(overlay_buttons);
|
||||
|
||||
log_misc("rawinput", "Analog mappings:");
|
||||
dump_analog_bindings();
|
||||
|
||||
// for certain games, show cursor if no touch is available (must be called after RI_MGR is available)
|
||||
if (show_cursor_if_no_touch && !is_touch_available("launcher::main_implementation")) {
|
||||
GRAPHICS_SHOW_CURSOR = true;
|
||||
@@ -2077,15 +2195,6 @@ int main_implementation(int argc, char *argv[]) {
|
||||
networkhook_init();
|
||||
}
|
||||
|
||||
// layeredfs
|
||||
if (fileutils::dir_exists("data_mods") &&
|
||||
!fileutils::file_exists("ifs_hook.dll") &&
|
||||
!fileutils::file_exists(MODULE_PATH / "ifs_hook.dll")) {
|
||||
log_warning("launcher", "data_mods directory was found, but ifs_hook.dll is not present; your mods will not load");
|
||||
log_warning("launcher", "to fix this, download ifs_layeredfs and add it as a DLL hook (-k)");
|
||||
log_warning("launcher", "https://github.com/mon/ifs_layeredfs");
|
||||
}
|
||||
|
||||
update_msvcrt_args(argc, argv);
|
||||
|
||||
// load hooks
|
||||
@@ -2093,12 +2202,28 @@ int main_implementation(int argc, char *argv[]) {
|
||||
log_info("launcher", "loading hook DLL {}", hook);
|
||||
HMODULE module;
|
||||
if (!(module = libutils::try_library(hook))) {
|
||||
log_warning("launcher", "failed to load hook {}", hook);
|
||||
log_warning("launcher", "failed to load hook {}: {}", hook, get_last_error_string());
|
||||
deferredlogs::defer_error_messages({
|
||||
fmt::format("failed to load hook {}:", hook),
|
||||
fmt::format(" {}", get_last_error_string())
|
||||
});
|
||||
} else {
|
||||
bt5api_hook(module);
|
||||
}
|
||||
}
|
||||
|
||||
// layeredfs
|
||||
if (fileutils::dir_exists("data_mods") &&
|
||||
GetModuleHandleA("ifs_hook.dll") == nullptr) {
|
||||
log_warning("launcher", "data_mods directory found, but ifs_hook.dll is not present; mods will not load");
|
||||
deferredlogs::defer_error_messages({
|
||||
"data_mods directory was found, but ifs_hook.dll is not present",
|
||||
" your mods will not load",
|
||||
" to fix this, download ifs_layeredfs and add it as a DLL hook (-k)",
|
||||
" https://github.com/mon/ifs_layeredfs"
|
||||
});
|
||||
}
|
||||
|
||||
// apply patches
|
||||
{
|
||||
overlay::windows::PatchManager patch_manager(nullptr, true);
|
||||
@@ -2231,8 +2356,10 @@ int main_implementation(int argc, char *argv[]) {
|
||||
// debug hook
|
||||
debughook::detach();
|
||||
|
||||
#ifndef NO_SCARD
|
||||
// scard
|
||||
scard_fini();
|
||||
#endif
|
||||
|
||||
// stop reader thread in case it was running
|
||||
stop_reader_thread();
|
||||
@@ -2321,6 +2448,65 @@ void update_msvcrt_args(int argc, char *argv[]) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void dump_button_bindings(std::vector<Button> *buttons) {
|
||||
if (!buttons) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto button = buttons->begin(); button != buttons->end(); ++button) {
|
||||
if (!button->isSet()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (button->isNaive()) {
|
||||
log_misc(
|
||||
"rawinput", " [{}] dev=Naive, vkey={}",
|
||||
button->getName(),
|
||||
button->getVKey()
|
||||
);
|
||||
} else {
|
||||
log_misc(
|
||||
"rawinput", " [{}] dev={}, vkey={}, analogtype={}",
|
||||
button->getName(),
|
||||
button->getDeviceIdentifier(),
|
||||
button->getVKey(),
|
||||
button->getAnalogType()
|
||||
);
|
||||
}
|
||||
|
||||
for (auto& alt : button->getAlternatives()) {
|
||||
if (alt.getVKey() == INVALID_VKEY) {
|
||||
continue;
|
||||
}
|
||||
log_misc(
|
||||
"rawinput", " [{}] (alt) dev={}, vkey={}, analogtype={}",
|
||||
button->getName(),
|
||||
alt.isNaive() ? "Naive" : alt.getDeviceIdentifier(),
|
||||
alt.getVKey(),
|
||||
alt.getAnalogType()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dump_analog_bindings() {
|
||||
auto analogs = games::get_analogs(eamuse_get_game());
|
||||
if (!analogs) {
|
||||
return;
|
||||
}
|
||||
for (auto& analog : *analogs) {
|
||||
if (!analog.isSet()) {
|
||||
continue;
|
||||
}
|
||||
log_misc(
|
||||
"rawinput", " [{}] dev={}, index={}",
|
||||
analog.getName(),
|
||||
analog.getDeviceIdentifier(),
|
||||
analog.getIndex()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef SPICETOOLS_SPICECFG_STANDALONE
|
||||
int main(int argc, char *argv[]) {
|
||||
return main_implementation(argc, argv);
|
||||
|
||||
@@ -6,6 +6,12 @@
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#ifdef NO_SCARD
|
||||
#define WITH_SCARD 0
|
||||
#else
|
||||
#define WITH_SCARD 1
|
||||
#endif
|
||||
|
||||
static const std::vector<std::string> CATEGORY_ORDER_API = {
|
||||
"SpiceCompanion and API",
|
||||
"API (Serial)",
|
||||
@@ -17,7 +23,6 @@ static const std::vector<std::string> CATEGORY_ORDER_BASIC = {
|
||||
"Game Options",
|
||||
"Common",
|
||||
"Network",
|
||||
"Overlay",
|
||||
"Graphics (Common)",
|
||||
"Graphics (Full Screen)",
|
||||
"Graphics (Windowed)",
|
||||
@@ -26,18 +31,21 @@ static const std::vector<std::string> CATEGORY_ORDER_BASIC = {
|
||||
|
||||
static const std::vector<std::string> CATEGORY_ORDER_ADVANCED = {
|
||||
"Game Options (Advanced)",
|
||||
"Overlay",
|
||||
"Network (Advanced)",
|
||||
"Performance",
|
||||
"Miscellaneous",
|
||||
"Paths",
|
||||
"Touch Parameters",
|
||||
"I/O Options",
|
||||
"NFC Card Readers",
|
||||
};
|
||||
static const std::vector<std::string> CATEGORY_ORDER_DEV = {
|
||||
"Network Adapters",
|
||||
"Path Overrides",
|
||||
"Network (Development)",
|
||||
"Audio (Hacks)",
|
||||
"I/O Modules",
|
||||
"Development",
|
||||
"Debug Log",
|
||||
};
|
||||
static const std::vector<std::string> CATEGORY_ORDER_NONE = {
|
||||
""
|
||||
@@ -51,12 +59,14 @@ bool launcher::USE_CMD_OVERRIDE = false;
|
||||
*/
|
||||
static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
{
|
||||
.title = "Game Executable",
|
||||
.title = "Override Game Executable & Disable Hooks",
|
||||
.name = "exec",
|
||||
.desc = "Path to the game DLL file",
|
||||
.desc = "Path to the game DLL file.\n\n"
|
||||
"This option is typically not needed; leave it empty to allow spice to auto-detect your game.\n\n"
|
||||
"WARNING: this option also disables all game-specific hooks and turns off most I/O modules!",
|
||||
.type = OptionType::Text,
|
||||
.setting_name = "*.dll",
|
||||
.category = "Paths",
|
||||
.category = "Path Overrides",
|
||||
},
|
||||
{
|
||||
.title = "Open Configurator",
|
||||
@@ -268,7 +278,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.name = "nolegacy",
|
||||
.desc = "Disables legacy key activation in-game.",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Miscellaneous",
|
||||
.category = "I/O Options",
|
||||
},
|
||||
{
|
||||
.title = "Discord Rich Presence",
|
||||
@@ -310,7 +320,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.desc = "This is NOT the EA service URL; use -url for that. "
|
||||
"Force the use of an adapter with the specified network. Must also provide -subnet",
|
||||
.type = OptionType::Text,
|
||||
.category = "Network Adapters",
|
||||
.category = "Network (Development)",
|
||||
.sensitive = true,
|
||||
},
|
||||
{
|
||||
@@ -318,21 +328,21 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.name = "subnet",
|
||||
.desc = "Force the use of an adapter with the specified subnet. Must also provide -network",
|
||||
.type = OptionType::Text,
|
||||
.category = "Network Adapters",
|
||||
.category = "Network (Development)",
|
||||
},
|
||||
{
|
||||
.title = "Disable Network Fixes",
|
||||
.name = "netfixdisable",
|
||||
.desc = "Force disables network fixes",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Network Adapters",
|
||||
.category = "Network (Development)",
|
||||
},
|
||||
{
|
||||
.title = "HTTP/1.1",
|
||||
.name = "http11",
|
||||
.desc = "Sets EA3 http11 value",
|
||||
.type = OptionType::Enum,
|
||||
.category = "Network (Advanced)",
|
||||
.category = "Network (Development)",
|
||||
.elements = {{"0", "Off"}, {"1", "On"}},
|
||||
},
|
||||
{
|
||||
@@ -340,7 +350,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.name = "ssldisable",
|
||||
.desc = "Prevents the SSL protocol from being registered",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Network (Advanced)",
|
||||
.category = "Network (Development)",
|
||||
},
|
||||
{
|
||||
.title = "URL Slash",
|
||||
@@ -355,7 +365,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.name = "r",
|
||||
.desc = "Set custom SOFTID override",
|
||||
.type = OptionType::Text,
|
||||
.category = "Network (Advanced)",
|
||||
.category = "Development",
|
||||
.sensitive = true,
|
||||
},
|
||||
{
|
||||
@@ -446,13 +456,30 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.category = "Game Options (Advanced)",
|
||||
},
|
||||
{
|
||||
.title = "IIDX Disable Cameras",
|
||||
// IIDXDisableCameras
|
||||
.title = "IIDX Disable Cameras (DEPRECATED - no longer needed)",
|
||||
.name = "iidxdisablecams",
|
||||
.desc = "Disables cameras",
|
||||
.type = OptionType::Bool,
|
||||
.hidden = true,
|
||||
.game_name = "Beatmania IIDX",
|
||||
.category = "Game Options",
|
||||
},
|
||||
{
|
||||
// IIDXCabCamAccess
|
||||
.title = "IIDX Use Official AC Cams",
|
||||
.name = "iidxcabcams",
|
||||
.desc = "For IIDX25+, allow direct access to cameras from real arcade cabinets. "
|
||||
"Only turn this on if you have OFFICIAL arcade cameras connected to the correct USB ports. Default: auto",
|
||||
.type = OptionType::Enum,
|
||||
.game_name = "Beatmania IIDX",
|
||||
.category = "Game Options (Advanced)",
|
||||
.elements = {
|
||||
{"auto", ""},
|
||||
{"off", ""},
|
||||
{"on", ""}
|
||||
},
|
||||
},
|
||||
{
|
||||
// IIDXCamHook
|
||||
.title = "IIDX Cam Hook",
|
||||
@@ -487,10 +514,10 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.display_name = "iidxcamhookoffset",
|
||||
.aliases = "iidxcamhookoffset",
|
||||
.desc = "Override DLL offsets for camera hook; "
|
||||
"format: 4 hex values separated by commas "
|
||||
"(offset_hook_a, offset_hook_b, offset_textures, offset_d3d_device)",
|
||||
"format: 5 hex values separated by commas "
|
||||
"(offset_hook_a, offset_textures, offset_camera_manager, offset_d3d_device, offset_afp_texture)",
|
||||
.type = OptionType::Text,
|
||||
.setting_name = "0x5817a0,0x5ea3b0,0x6fffbd8,0xe0",
|
||||
.setting_name = "0x5817a0,0x6fffbd8,0xbbae40,0xe0,0x30",
|
||||
.game_name = "Beatmania IIDX",
|
||||
.category = "Game Options (Advanced)",
|
||||
},
|
||||
@@ -550,10 +577,11 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.category = "Game Options",
|
||||
},
|
||||
{
|
||||
.title = "IIDX BIO2 Firmware",
|
||||
.title = "IIDX BIO2 Firmware Update",
|
||||
.name = "iidxbio2fw",
|
||||
.desc = "Enables BIO2 firmware updates",
|
||||
.desc = "Enables BIO2 firmware updates. WARNING - can cause semi-permanent change to your I/O board",
|
||||
.type = OptionType::Bool,
|
||||
.hidden = true,
|
||||
.game_name = "Beatmania IIDX",
|
||||
.category = "Game Options (Advanced)",
|
||||
},
|
||||
@@ -717,10 +745,13 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.category = "Game Options (Advanced)",
|
||||
},
|
||||
{
|
||||
.title = "SDVX Disable Cameras",
|
||||
.title = "SDVX Disable Cameras (DEPRECATED - no longer needed)",
|
||||
.name = "sdvxdisablecams",
|
||||
.desc = "Disables cameras",
|
||||
.desc = "This option does nothing.\n\n"
|
||||
"This option was used in the past to fix cameras in SDVX5 but this is no longer "
|
||||
"needed as camera check will always be skipped with a built-in patch.",
|
||||
.type = OptionType::Bool,
|
||||
.hidden = true,
|
||||
.game_name = "Sound Voltex",
|
||||
.category = "Game Options",
|
||||
},
|
||||
@@ -806,6 +837,15 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.game_name = "Dance Dance Revolution",
|
||||
.category = "Game Options",
|
||||
},
|
||||
{
|
||||
// DDRSkipCodecRegisteration
|
||||
.title = "DDR Skip Codec Registration",
|
||||
.name = "ddrnocodec",
|
||||
.desc = "Prevent automatic registration of codecs in the com folder",
|
||||
.type = OptionType::Bool,
|
||||
.game_name = "Dance Dance Revolution",
|
||||
.category = "Game Options (Advanced)",
|
||||
},
|
||||
{
|
||||
.title = "Force Load Pop'n Music Module",
|
||||
.name = "pnm",
|
||||
@@ -1033,7 +1073,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
},
|
||||
{
|
||||
// LoadCCJModule
|
||||
.title = "Force Load Chase Chase Jokers",
|
||||
.title = "Force Load Chase Chase Jokers Module",
|
||||
.name = "ccj",
|
||||
.desc = "manually enable Chase Chase Jokers module",
|
||||
.type = OptionType::Bool,
|
||||
@@ -1042,7 +1082,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
},
|
||||
{
|
||||
// LoadQKSModule
|
||||
.title = "Force Load QuizKnock STADIUM",
|
||||
.title = "Force Load QuizKnock STADIUM Module",
|
||||
.name = "qks",
|
||||
.desc = "manually enable QuizKnock STADIUM module",
|
||||
.type = OptionType::Bool,
|
||||
@@ -1051,16 +1091,25 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
},
|
||||
{
|
||||
// LoadMFGModule
|
||||
.title = "Force Load Mahjong Fight Girl",
|
||||
.title = "Force Load Mahjong Fight Girl Module",
|
||||
.name = "mfg",
|
||||
.desc = "manually enable Mahjong Fight Girl module",
|
||||
.type = OptionType::Bool,
|
||||
.game_name = "Mahjong Fight Girl",
|
||||
.category = "Game Options (Advanced)",
|
||||
},
|
||||
{
|
||||
// LoadPCModule
|
||||
.title = "Force Load Polaris Chord Module",
|
||||
.name = "pc",
|
||||
.desc = "manually enable Polaris Chord module",
|
||||
.type = OptionType::Bool,
|
||||
.game_name = "Polaris Chord",
|
||||
.category = "Game Options (Advanced)",
|
||||
},
|
||||
{
|
||||
// LoadMusecaModule
|
||||
.title = "Force Load Museca",
|
||||
.title = "Force Load Museca Module",
|
||||
.name = "museca",
|
||||
.desc = "manually enable Museca module",
|
||||
.type = OptionType::Bool,
|
||||
@@ -1068,88 +1117,88 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.category = "Game Options (Advanced)",
|
||||
},
|
||||
{
|
||||
.title = "Modules Folder Path",
|
||||
.title = "Modules Folder Override",
|
||||
.name = "modules",
|
||||
.desc = "Sets a custom path to the modules folder",
|
||||
.type = OptionType::Text,
|
||||
.category = "Paths",
|
||||
.category = "Path Overrides",
|
||||
},
|
||||
{
|
||||
.title = "Screenshot Folder Path",
|
||||
.title = "Screenshot Folder Override",
|
||||
.name = "screenshotpath",
|
||||
.desc = "Sets a custom path to the screenshots folder",
|
||||
.type = OptionType::Text,
|
||||
.category = "Paths",
|
||||
.category = "Path Overrides",
|
||||
},
|
||||
{
|
||||
.title = "Configuration Path",
|
||||
.title = "Configuration Path Override",
|
||||
.name = "cfgpath",
|
||||
.desc = "Sets a custom file path for config file. Must be passed via the command line. "
|
||||
"If left empty, %appdata%\\spicetools.xml will be used",
|
||||
.type = OptionType::Text,
|
||||
.setting_name = "(default)",
|
||||
.category = "Paths",
|
||||
.category = "Path Overrides",
|
||||
.disabled = true,
|
||||
},
|
||||
{
|
||||
// ScreenResizeConfigPath
|
||||
.title = "Screen Resize Config Path",
|
||||
.title = "Screen Resize Config Path Override",
|
||||
.name = "resizecfgpath",
|
||||
.desc = "Sets a custom file path for screen resize config file. "
|
||||
"If left empty, %appdata%\\spice2x\\spicetools_screen_resize.json will be used",
|
||||
.type = OptionType::Text,
|
||||
.category = "Paths",
|
||||
.category = "Path Overrides",
|
||||
},
|
||||
{
|
||||
// PatchManagerConfigPath
|
||||
.title = "Patch Manager Config Path",
|
||||
.title = "Patch Manager Config Path Override",
|
||||
.name = "patchcfgpath",
|
||||
.desc = "Sets a custom file path for patch manager config file. Can be used to manage 'profiles' for auto-patches. "
|
||||
"If left empty, %appdata%\\spice2x\\spicetools_patch_manager.json will be used",
|
||||
.type = OptionType::Text,
|
||||
.category = "Paths",
|
||||
.category = "Path Overrides",
|
||||
},
|
||||
{
|
||||
.title = "Intel SDE Folder Path",
|
||||
.title = "Intel SDE",
|
||||
.name = "sde",
|
||||
.desc = "Path to Intel SDE kit path for automatic attaching",
|
||||
.type = OptionType::Text,
|
||||
.category = "Paths",
|
||||
.category = "Development",
|
||||
},
|
||||
{
|
||||
.title = "Path to ea3-config.xml",
|
||||
.title = "ea3-config.xml Override",
|
||||
.name = "e",
|
||||
.desc = "Sets a custom path to ea3-config.xml",
|
||||
.type = OptionType::Text,
|
||||
.category = "Paths",
|
||||
.category = "Path Overrides",
|
||||
},
|
||||
{
|
||||
.title = "Path to app-config.xml",
|
||||
.title = "app-config.xml Override",
|
||||
.name = "a",
|
||||
.desc = "Sets a custom path to app-config.xml",
|
||||
.type = OptionType::Text,
|
||||
.category = "Paths",
|
||||
.category = "Path Overrides",
|
||||
},
|
||||
{
|
||||
.title = "Path to avs-config.xml",
|
||||
.title = "avs-config.xml Override",
|
||||
.name = "v",
|
||||
.desc = "Sets a custom path to avs-config.xml",
|
||||
.type = OptionType::Text,
|
||||
.category = "Paths",
|
||||
.category = "Path Overrides",
|
||||
},
|
||||
{
|
||||
.title = "Path to bootstrap.xml",
|
||||
.title = "bootstrap.xml Override",
|
||||
.name = "b",
|
||||
.desc = "Sets a custom path to bootstrap.xml",
|
||||
.type = OptionType::Text,
|
||||
.category = "Paths",
|
||||
.category = "Path Overrides",
|
||||
},
|
||||
{
|
||||
.title = "Path to log.txt",
|
||||
.title = "log.txt Override",
|
||||
.name = "y",
|
||||
.desc = "Sets a custom path to log.txt",
|
||||
.type = OptionType::Text,
|
||||
.category = "Paths",
|
||||
.category = "Path Overrides",
|
||||
},
|
||||
{
|
||||
.title = "API TCP Port",
|
||||
@@ -1359,21 +1408,27 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.name = "scard",
|
||||
.desc = "Detects and uses HID smart card readers for card input. Developed for ACR122U reader",
|
||||
.type = OptionType::Bool,
|
||||
.hidden = !WITH_SCARD,
|
||||
.category = "NFC Card Readers",
|
||||
.disabled = !WITH_SCARD,
|
||||
},
|
||||
{
|
||||
.title = "HID SmartCard Order Flip",
|
||||
.name = "scardflip",
|
||||
.desc = "Flips the order of detection for P1/P2",
|
||||
.type = OptionType::Bool,
|
||||
.hidden = !WITH_SCARD,
|
||||
.category = "NFC Card Readers",
|
||||
.disabled = !WITH_SCARD,
|
||||
},
|
||||
{
|
||||
.title = "HID SmartCard Order Toggle",
|
||||
.name = "scardtoggle",
|
||||
.desc = "Toggles reader between P1/P2 using the NumLock key state",
|
||||
.type = OptionType::Bool,
|
||||
.hidden = !WITH_SCARD,
|
||||
.category = "NFC Card Readers",
|
||||
.disabled = !WITH_SCARD,
|
||||
},
|
||||
{
|
||||
// HIDSmartCardIdConvert
|
||||
@@ -1387,12 +1442,14 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
"For example, abcd56780123 is converted to e00401abcd567801 on card in. This results in different card numbers so "
|
||||
"be careful when connecting to servers!",
|
||||
.type = OptionType::Enum,
|
||||
.hidden = !WITH_SCARD,
|
||||
.category = "NFC Card Readers",
|
||||
.elements = {
|
||||
{"legacy", "Use cards as-is"},
|
||||
{"fix", "Fix bad cards only"},
|
||||
{"all", "Force all cards"},
|
||||
},
|
||||
.disabled = !WITH_SCARD,
|
||||
},
|
||||
{
|
||||
// SextetStreamPort
|
||||
@@ -1400,7 +1457,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.name = "sextet",
|
||||
.desc = "Use a SextetStream device on a given COM port",
|
||||
.type = OptionType::Text,
|
||||
.category = "Miscellaneous",
|
||||
.category = "I/O Options",
|
||||
},
|
||||
{
|
||||
.title = "Enable BemaniTools 5 API",
|
||||
@@ -1501,7 +1558,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
"you use a different backend instead of exclusive WASAPI).\n\n"
|
||||
"Check this if you want games to natively access your audio device",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Audio",
|
||||
.category = "Audio (Hacks)",
|
||||
},
|
||||
{
|
||||
// spice2x_DisableVolumeHook
|
||||
@@ -1513,16 +1570,19 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
"Default: off (prevent games from changing audio volume by hooking IAudioEndpointVolume).\n\n"
|
||||
"Check this if you want games to freely change your volume",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Audio",
|
||||
.category = "Audio (Hacks)",
|
||||
},
|
||||
{
|
||||
.title = "Spice Audio Hook Backend",
|
||||
.name = "audiobackend",
|
||||
.desc = "Selects the audio backend to use when spice audio hook is enabled, overriding exclusive WASAPI. "
|
||||
" Does nothing for games that do not output to exclusive WASAPI",
|
||||
"Does nothing for games that do not output to exclusive WASAPI",
|
||||
.type = OptionType::Enum,
|
||||
.category = "Audio",
|
||||
.elements = {{"asio", "ASIO"}, {"waveout", "waveOut"}},
|
||||
.elements = {
|
||||
{"asio", "ASIO"},
|
||||
{"waveout", "broken, do not use"}
|
||||
},
|
||||
},
|
||||
{
|
||||
.title = "Spice Audio Hook ASIO Driver ID",
|
||||
@@ -1538,7 +1598,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
"This is automatically enabled when required and not normally needed",
|
||||
.type = OptionType::Bool,
|
||||
.hidden = true,
|
||||
.category = "Audio",
|
||||
.category = "Audio (Hacks)",
|
||||
},
|
||||
{
|
||||
// DelayBy5Seconds
|
||||
@@ -1591,11 +1651,11 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.elements = {{"0", "90 (CW)"}, {"1", "270 (CCW)"}},
|
||||
},
|
||||
{
|
||||
.title = "Log Level",
|
||||
.title = "AVS Log Level",
|
||||
.name = "loglevel",
|
||||
.desc = "Set the level of detail that gets written to the log",
|
||||
.desc = "Set the level of detail for AVS log messages written to the log. Does not affect logging from spice",
|
||||
.type = OptionType::Enum,
|
||||
.category = "Performance",
|
||||
.category = "Debug Log",
|
||||
.elements = {{"fatal", ""}, {"warning", ""}, {"info", ""}, {"misc", ""}, {"all", ""}, {"disable", ""}},
|
||||
},
|
||||
{
|
||||
@@ -1603,14 +1663,14 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.name = "automap",
|
||||
.desc = "Enable automap in patch configuration",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Development",
|
||||
.category = "Network (Development)",
|
||||
},
|
||||
{
|
||||
.title = "EA Netdump",
|
||||
.name = "netdump",
|
||||
.desc = "Enable automap in network dumping configuration",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Development",
|
||||
.category = "Network (Development)",
|
||||
},
|
||||
{
|
||||
.title = "Discord RPC AppID Override",
|
||||
@@ -1624,35 +1684,45 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.name = "logblock",
|
||||
.desc = "Slower but safer logging used for debugging",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Development",
|
||||
.category = "Debug Log",
|
||||
},
|
||||
{
|
||||
.title = "Debug CreateFile",
|
||||
.name = "createfiledebug",
|
||||
.desc = "Outputs CreateFile debug prints",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Development",
|
||||
.category = "Debug Log",
|
||||
},
|
||||
{
|
||||
.title = "Verbose Graphics Logging",
|
||||
.name = "graphicsverbose",
|
||||
.desc = "Enable the verbose logging of graphics hook code",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Development",
|
||||
.category = "Debug Log",
|
||||
},
|
||||
{
|
||||
.title = "Verbose AVS Logging",
|
||||
.name = "avsverbose",
|
||||
.desc = "Enable the verbose logging of AVS filesystem functions",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Development",
|
||||
.category = "Debug Log",
|
||||
},
|
||||
{
|
||||
// AllowEA3Verbose
|
||||
.title = "Allow EA3 Verbose Debug Logging",
|
||||
.name = "ea3verbose",
|
||||
.desc = "This option is only useful for network developers; leave this OFF.\n\n"
|
||||
"By default, ea3/debug/verbose node is removed by spice to prevent exposing PCBID in the logs. "
|
||||
"When this is enabled, ea3/debug/verbose node in the XML will be kept and exposed to the game",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Debug Log",
|
||||
},
|
||||
{
|
||||
.title = "Disable Colored Output",
|
||||
.name = "nocolor",
|
||||
.desc = "Disable terminal colors for log outputs to console",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Development",
|
||||
.category = "Debug Log",
|
||||
},
|
||||
{
|
||||
.title = "Disable ACP Hook",
|
||||
@@ -1687,7 +1757,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.name = "pebprint",
|
||||
.desc = "Prints PEB on startup to console",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Development",
|
||||
.category = "Debug Log",
|
||||
},
|
||||
{
|
||||
// DumpSystemInfo
|
||||
@@ -1695,7 +1765,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.name = "sysdump",
|
||||
.desc = "Print system information to the log on startup. Default: basic",
|
||||
.type = OptionType::Enum,
|
||||
.category = "Development",
|
||||
.category = "Debug Log",
|
||||
.elements = {
|
||||
{"none", "Nothing"},
|
||||
{"basic", "OS, CPU, SMBIOS, GPU"},
|
||||
@@ -1780,6 +1850,35 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.game_name = "Mahjong Fight Girl",
|
||||
.category = "Game Options (Advanced)"
|
||||
},
|
||||
{
|
||||
// PCArgs
|
||||
.title = "PC Arguments Override",
|
||||
.name = "pcargs",
|
||||
.desc = "Command line arguments passed to the game.",
|
||||
.type = OptionType::Text,
|
||||
.setting_name = "",
|
||||
.game_name = "Polaris Chord",
|
||||
.category = "Game Options (Advanced)",
|
||||
},
|
||||
{
|
||||
// PCNoIO
|
||||
.title = "PC Disable IO Emulation",
|
||||
.name = "pcnoio",
|
||||
.desc = "Disables BI2X hooks for PC",
|
||||
.type = OptionType::Bool,
|
||||
.setting_name = "",
|
||||
.game_name = "Polaris Chord",
|
||||
.category = "Game Options (Advanced)"
|
||||
},
|
||||
{
|
||||
// PCKnobMode
|
||||
.title = "PC Fader Knobs Mode",
|
||||
.name = "pcknobs",
|
||||
.desc = "Allows SDVX knobs and IIDX turntables to be bound as Polaris Chord faders",
|
||||
.type = OptionType::Bool,
|
||||
.game_name = "Polaris Chord",
|
||||
.category = "Game Options",
|
||||
},
|
||||
{
|
||||
// spice2x_LightsOverallBrightness
|
||||
.title = "Lights Brightness Adjustment",
|
||||
@@ -1925,7 +2024,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.desc = "For certain buggy ASIO drivers, force unload of ASIO driver when audio stream stops. "
|
||||
"Used for working around ASIO drivers that lock up after force quitting games",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Audio",
|
||||
.category = "Audio (Hacks)",
|
||||
},
|
||||
{
|
||||
// spice2x_IIDXNoESpec
|
||||
@@ -2321,7 +2420,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.type = OptionType::Bool,
|
||||
.category = "Development",
|
||||
.disabled = true,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
const std::vector<std::string> &launcher::get_categories(Options::OptionsCategory category) {
|
||||
@@ -2402,7 +2501,7 @@ std::unique_ptr<std::vector<Option>> launcher::parse_options(int argc, char *arg
|
||||
} else {
|
||||
// validate it is an integer
|
||||
try {
|
||||
std::stoull(argv[i], nullptr, 16);
|
||||
auto _ = std::stoull(argv[i], nullptr, 16);
|
||||
} catch (const std::exception &ex) {
|
||||
log_fatal("options", "parameter for -{} is not a hex number: {}", alias, argv[i]);
|
||||
}
|
||||
@@ -2540,8 +2639,19 @@ std::vector<Option> launcher::merge_options(
|
||||
return merged;
|
||||
}
|
||||
|
||||
std::string launcher::detect_bootstrap_release_code() {
|
||||
std::string bootstrap_path = "prop/bootstrap.xml";
|
||||
std::string launcher::detect_bootstrap_release_code(const std::string& bootstrap_user) {
|
||||
std::string bootstrap_path;
|
||||
|
||||
if (!bootstrap_user.empty()) {
|
||||
bootstrap_path = bootstrap_user;
|
||||
} else if (fileutils::file_exists("prop/bootstrap.xml")) {
|
||||
bootstrap_path = "prop/bootstrap.xml";
|
||||
}
|
||||
|
||||
if (bootstrap_path.empty()) {
|
||||
log_warning("options", "unable to detect bootstrap.xml file");
|
||||
return "";
|
||||
}
|
||||
|
||||
// load XML
|
||||
tinyxml2::XMLDocument bootstrap;
|
||||
@@ -2564,7 +2674,7 @@ std::string launcher::detect_bootstrap_release_code() {
|
||||
return "";
|
||||
}
|
||||
|
||||
static launcher::GameVersion detect_gameversion_ident() {
|
||||
static launcher::GameVersion detect_gameversion_ident(const std::string& bootstrap_user) {
|
||||
|
||||
// detect ea3-ident path
|
||||
std::string ident_path;
|
||||
@@ -2610,7 +2720,7 @@ static launcher::GameVersion detect_gameversion_ident() {
|
||||
auto node_ext = node_soft->FirstChildElement("ext");
|
||||
if (node_ext) {
|
||||
version.ext = node_ext->GetText();
|
||||
auto bootstrap_ext = launcher::detect_bootstrap_release_code();
|
||||
auto bootstrap_ext = launcher::detect_bootstrap_release_code(bootstrap_user);
|
||||
if (version.ext.size() != 10 && bootstrap_ext.size() == 10) {
|
||||
version.ext = bootstrap_ext;
|
||||
} else if (bootstrap_ext.size() == 10) {
|
||||
@@ -2646,8 +2756,7 @@ static launcher::GameVersion detect_gameversion_ident() {
|
||||
return launcher::GameVersion();
|
||||
}
|
||||
|
||||
launcher::GameVersion launcher::detect_gameversion(const std::string &ea3_user) {
|
||||
|
||||
launcher::GameVersion launcher::detect_gameversion(const std::string &ea3_user, const std::string& bootstrap_user) {
|
||||
// detect ea3-config path
|
||||
std::string ea3_path;
|
||||
if (!ea3_user.empty()) {
|
||||
@@ -2661,7 +2770,7 @@ launcher::GameVersion launcher::detect_gameversion(const std::string &ea3_user)
|
||||
}
|
||||
if (ea3_path.empty()) {
|
||||
log_warning("options", "unable to detect EA3 configuration file");
|
||||
return detect_gameversion_ident();
|
||||
return detect_gameversion_ident(bootstrap_user);
|
||||
}
|
||||
|
||||
// load XML
|
||||
@@ -2701,7 +2810,7 @@ launcher::GameVersion launcher::detect_gameversion(const std::string &ea3_user)
|
||||
if (ea3_config.Parse(xml_str.c_str()) != tinyxml2::XML_SUCCESS) {
|
||||
// if we still failed, give up
|
||||
log_warning("options", "unable to parse fixed xml");
|
||||
return detect_gameversion_ident();
|
||||
return detect_gameversion_ident(bootstrap_user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2732,7 +2841,7 @@ launcher::GameVersion launcher::detect_gameversion(const std::string &ea3_user)
|
||||
auto node_ext = node_soft->FirstChildElement("ext");
|
||||
if (node_ext) {
|
||||
version.ext = node_ext->GetText();
|
||||
auto bootstrap_ext = launcher::detect_bootstrap_release_code();
|
||||
auto bootstrap_ext = launcher::detect_bootstrap_release_code(bootstrap_user);
|
||||
if (version.ext.size() != 10 && bootstrap_ext.size() == 10) {
|
||||
version.ext = bootstrap_ext;
|
||||
} else if (bootstrap_ext.size() == 10) {
|
||||
@@ -2765,5 +2874,5 @@ launcher::GameVersion launcher::detect_gameversion(const std::string &ea3_user)
|
||||
|
||||
// error
|
||||
log_warning("options", "unable to find /ea3/soft/model in {}", ea3_path);
|
||||
return detect_gameversion_ident();
|
||||
return detect_gameversion_ident(bootstrap_user);
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ namespace launcher {
|
||||
LoadIIDXModule,
|
||||
IIDXCameraOrderFlip,
|
||||
IIDXDisableCameras,
|
||||
IIDXCabCamAccess,
|
||||
IIDXCamHook,
|
||||
IIDXCamHookRatio,
|
||||
IIDXCamHookOverride,
|
||||
@@ -86,6 +87,7 @@ namespace launcher {
|
||||
spice2x_SDVXSubRedraw,
|
||||
LoadDDRModule,
|
||||
DDR43Mode,
|
||||
DDRSkipCodecRegisteration,
|
||||
LoadPopnMusicModule,
|
||||
PopnMusicForceHDMode,
|
||||
PopnMusicForceSDMode,
|
||||
@@ -117,6 +119,7 @@ namespace launcher {
|
||||
LoadCCJModule,
|
||||
LoadQKSModule,
|
||||
LoadMFGModule,
|
||||
LoadPCModule,
|
||||
LoadMusecaModule,
|
||||
PathToModules,
|
||||
ScreenshotFolder,
|
||||
@@ -186,6 +189,7 @@ namespace launcher {
|
||||
DebugCreateFile,
|
||||
VerboseGraphicsLogging,
|
||||
VerboseAVSLogging,
|
||||
AllowEA3Verbose,
|
||||
DisableColoredOutput,
|
||||
DisableACPHook,
|
||||
DisableSignalHandling,
|
||||
@@ -201,6 +205,9 @@ namespace launcher {
|
||||
MFGArgs,
|
||||
MFGCabType,
|
||||
MFGNoIO,
|
||||
PCArgs,
|
||||
PCNoIO,
|
||||
PCKnobMode,
|
||||
spice2x_LightsOverallBrightness,
|
||||
spice2x_WindowBorder,
|
||||
spice2x_WindowSize,
|
||||
@@ -273,6 +280,6 @@ namespace launcher {
|
||||
std::string ext;
|
||||
};
|
||||
|
||||
std::string detect_bootstrap_release_code();
|
||||
GameVersion detect_gameversion(const std::string& ea3_user);
|
||||
std::string detect_bootstrap_release_code(const std::string& bootstrap_user);
|
||||
GameVersion detect_gameversion(const std::string& ea3_user, const std::string& bootstrap_user);
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ namespace richpresence {
|
||||
{"Busou Shinki: Armored Princess Battle Conductor", "1226739666741366916"},
|
||||
{"Chase Chase Jokers", "1226739863915593770"},
|
||||
{"QuizKnock STADIUM", "1226739930328334478"},
|
||||
{"Mahjong Fight Girl", "1417371188342030468"},
|
||||
{"Polaris Chord", "1417371592224407552"},
|
||||
};
|
||||
|
||||
// state
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "easrv/easrv.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "hooks/audio/audio.h"
|
||||
#include "util/deferlog.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
#include "launcher.h"
|
||||
@@ -21,6 +22,9 @@ namespace launcher {
|
||||
// therefore, subsystems need to be guarded against multiple unload attempts
|
||||
log_info("launcher", "stopping subsystems");
|
||||
|
||||
// before shutting down logger, dump any deferred log messages
|
||||
deferredlogs::dump_to_logger();
|
||||
|
||||
// flush/stop logger
|
||||
logger::stop();
|
||||
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
#include "signal.h"
|
||||
|
||||
#include <future>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
|
||||
#include "acio/acio.h"
|
||||
#include "external/stackwalker/stackwalker.h"
|
||||
#include "hooks/libraryhook.h"
|
||||
#include "launcher/shutdown.h"
|
||||
#include "util/deferlog.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
@@ -97,6 +99,34 @@ static LONG WINAPI TopLevelExceptionFilter(struct _EXCEPTION_POINTERS *Exception
|
||||
// print signal
|
||||
log_warning("signal", "exception raised: {}", exception_code(ExceptionRecord));
|
||||
|
||||
switch (ExceptionRecord->ExceptionCode) {
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||
deferredlogs::defer_error_messages({
|
||||
"illegal instruction exception:",
|
||||
" either your CPU is too old (e.g., does not support SSE4.2 or AVX2 ",
|
||||
" but perhaps the game requires it); or, a bad patch was applied."
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// check ACIO init failures
|
||||
if (acio::IO_INIT_IN_PROGRESS) {
|
||||
deferredlogs::defer_error_messages({
|
||||
"exception raised during ACIO init, this usually happens when ",
|
||||
" a third party application interferes with hooks",
|
||||
" please check for the following, disable them, and try launching the game again:",
|
||||
" * RivaTuner Statistics Server (RTSS)",
|
||||
" * MSI Afterburner",
|
||||
" * kernel mode anti-cheat"
|
||||
});
|
||||
}
|
||||
|
||||
// dump deferred logs BEFORE stack trace since some errors cause stack trace logic to hang
|
||||
// (e.g., ACIO init hang due to RTSS)
|
||||
deferredlogs::dump_to_logger(true);
|
||||
|
||||
// walk the exception chain
|
||||
struct _EXCEPTION_RECORD *record_cause = ExceptionRecord->ExceptionRecord;
|
||||
while (record_cause != nullptr) {
|
||||
@@ -145,6 +175,9 @@ static LONG WINAPI TopLevelExceptionFilter(struct _EXCEPTION_POINTERS *Exception
|
||||
log_warning("signal", "minidump creation function not available, skipping");
|
||||
}
|
||||
|
||||
// this will stall all UI threads for this process
|
||||
show_popup_for_crash();
|
||||
|
||||
log_fatal("signal", "end");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
namespace launcher::signal {
|
||||
|
||||
// settings
|
||||
extern bool DISABLE;
|
||||
extern bool USE_VEH_WORKAROUND;
|
||||
|
||||
//void print_stacktrace();
|
||||
void attach();
|
||||
void init();
|
||||
}
|
||||
|
||||
@@ -659,6 +659,8 @@ void eamuse_autodetect_game() {
|
||||
eamuse_set_game("QuizKnock STADIUM");
|
||||
else if (avs::game::is_model("VFG"))
|
||||
eamuse_set_game("Mahjong Fight Girl");
|
||||
else if (avs::game::is_model("XIF"))
|
||||
eamuse_set_game("Polaris Chord");
|
||||
else {
|
||||
log_warning("eamuse", "unknown game model: {}", avs::game::MODEL);
|
||||
eamuse_set_game("unknown");
|
||||
|
||||
@@ -162,7 +162,7 @@ static void __cdecl cardunit_update() {
|
||||
bool kb_insert_press = false;
|
||||
|
||||
// eamio keypress
|
||||
kb_insert_press |= eamuse_get_keypad_state(unit) & (1 << EAM_IO_INSERT);
|
||||
kb_insert_press |= static_cast<bool>(eamuse_get_keypad_state(unit) & (1 << EAM_IO_INSERT));
|
||||
|
||||
// update card inserts
|
||||
if (eamuse_card_insert_consume((int) EXTDEV_CARDUNIT_COUNT, unit) ||
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace wintouchemu {
|
||||
bool FORCE = false;
|
||||
bool INJECT_MOUSE_AS_WM_TOUCH = false;
|
||||
bool LOG_FPS = false;
|
||||
bool ADD_TOUCH_FLAG_PRIMARY = false;
|
||||
|
||||
static inline bool is_emu_enabled() {
|
||||
return FORCE || !is_touch_available("wintouchemu::is_emu_enabled") || GRAPHICS_SHOW_CURSOR;
|
||||
@@ -187,16 +188,28 @@ namespace wintouchemu {
|
||||
switch (touch_event->type) {
|
||||
case TOUCH_DOWN:
|
||||
if (valid) {
|
||||
if (ADD_TOUCH_FLAG_PRIMARY) {
|
||||
touch_input->dwFlags |= TOUCHEVENTF_PRIMARY;
|
||||
}
|
||||
|
||||
touch_input->dwFlags |= TOUCHEVENTF_DOWN;
|
||||
}
|
||||
break;
|
||||
case TOUCH_MOVE:
|
||||
if (valid) {
|
||||
if (ADD_TOUCH_FLAG_PRIMARY) {
|
||||
touch_input->dwFlags |= TOUCHEVENTF_PRIMARY;
|
||||
}
|
||||
|
||||
touch_input->dwFlags |= TOUCHEVENTF_MOVE;
|
||||
}
|
||||
break;
|
||||
case TOUCH_UP:
|
||||
// don't check valid so that this touch ID can be released
|
||||
if (ADD_TOUCH_FLAG_PRIMARY) {
|
||||
touch_input->dwFlags |= TOUCHEVENTF_PRIMARY;
|
||||
}
|
||||
|
||||
touch_input->dwFlags |= TOUCHEVENTF_UP;
|
||||
break;
|
||||
}
|
||||
@@ -238,16 +251,28 @@ namespace wintouchemu {
|
||||
switch (mouse_state.touch_event) {
|
||||
case TOUCHEVENTF_DOWN:
|
||||
if (valid) {
|
||||
if (ADD_TOUCH_FLAG_PRIMARY) {
|
||||
touch_input->dwFlags |= TOUCHEVENTF_PRIMARY;
|
||||
}
|
||||
|
||||
touch_input->dwFlags |= TOUCHEVENTF_DOWN;
|
||||
}
|
||||
break;
|
||||
case TOUCHEVENTF_MOVE:
|
||||
if (valid) {
|
||||
if (ADD_TOUCH_FLAG_PRIMARY) {
|
||||
touch_input->dwFlags |= TOUCHEVENTF_PRIMARY;
|
||||
}
|
||||
|
||||
touch_input->dwFlags |= TOUCHEVENTF_MOVE;
|
||||
}
|
||||
break;
|
||||
case TOUCHEVENTF_UP:
|
||||
// don't check valid so that this touch ID can be released
|
||||
if (ADD_TOUCH_FLAG_PRIMARY) {
|
||||
touch_input->dwFlags |= TOUCHEVENTF_PRIMARY;
|
||||
}
|
||||
|
||||
touch_input->dwFlags |= TOUCHEVENTF_UP;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace wintouchemu {
|
||||
extern bool FORCE;
|
||||
extern bool INJECT_MOUSE_AS_WM_TOUCH;
|
||||
extern bool LOG_FPS;
|
||||
extern bool ADD_TOUCH_FLAG_PRIMARY;
|
||||
|
||||
void hook(const char *window_title, HMODULE module = nullptr, int delay_in_s=0);
|
||||
void hook_title_ends(
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "touch/touch.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
|
||||
#if !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) || \
|
||||
!defined(IMGUI_DISABLE_DEFAULT_ALLOCATORS) || \
|
||||
@@ -175,10 +176,22 @@ static void ImGui_ImplSpice_UpdateMousePos() {
|
||||
static_cast<int>(pos.y / io.DisplaySize.y * window_size.y));
|
||||
}
|
||||
|
||||
const auto active_window = ::GetForegroundWindow();
|
||||
|
||||
// if the main focus is a windowed subscreen, put the imgui cursor in a place that won't
|
||||
// trigger any overlay, don't process anything else
|
||||
const auto is_windowed_subscreen =
|
||||
(GRAPHICS_IIDX_WSUB && active_window == TDJ_SUBSCREEN_WINDOW) ||
|
||||
(active_window == SDVX_SUBSCREEN_WINDOW);
|
||||
if (is_windowed_subscreen) {
|
||||
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
|
||||
return;
|
||||
}
|
||||
|
||||
// set mouse position
|
||||
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
|
||||
POINT pos;
|
||||
if (HWND active_window = ::GetForegroundWindow()) {
|
||||
if (active_window) {
|
||||
if (active_window == g_hWnd
|
||||
|| ::IsChild(active_window, g_hWnd)
|
||||
|| ::IsChild(g_hWnd, active_window)
|
||||
@@ -208,7 +221,7 @@ static void ImGui_ImplSpice_UpdateMousePos() {
|
||||
static size_t delay_touch = 0;
|
||||
static size_t delay_touch_target = 2;
|
||||
static DWORD last_touch_id = ~0u;
|
||||
if (!touch_points.empty()) {
|
||||
if (!touch_points.empty() && !is_windowed_subscreen) {
|
||||
|
||||
// use the first touch point
|
||||
auto &tp = touch_points[0];
|
||||
|
||||
@@ -174,18 +174,18 @@ void overlay::SpiceOverlay::init() {
|
||||
// https://github.com/ocornut/imgui/issues/707#issuecomment-760220280
|
||||
// r, g, b, a
|
||||
ImVec4* colors = ImGui::GetStyle().Colors;
|
||||
// colors[ImGuiCol_Text] = ImVec4(0.75f, 0.75f, 0.75f, 1.00f);
|
||||
// colors[ImGuiCol_TextDisabled] = ImVec4(0.35f, 0.35f, 0.35f, 1.00f);
|
||||
|
||||
// colors[ImGuiCol_WindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.94f);
|
||||
// colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.f, 0.f, 0.94f);
|
||||
// note: also see bg_alpha value in each overlay window
|
||||
colors[ImGuiCol_WindowBg] = ImVec4(0.06f, 0.06f, 0.06f, 1.0f);
|
||||
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 1.0f);
|
||||
|
||||
colors[ImGuiCol_Border] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f);
|
||||
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||
colors[ImGuiCol_FrameBg] = ImVec4(0.37f, 0.14f, 0.00f, 0.54f);
|
||||
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.37f, 0.14f, 0.14f, 0.67f);
|
||||
colors[ImGuiCol_FrameBgActive] = ImVec4(0.39f, 0.20f, 0.20f, 0.67f);
|
||||
|
||||
colors[ImGuiCol_FrameBg] = ImVec4(0.18f, 0.18f, 0.18f, 0.54f);
|
||||
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.18f, 0.18f, 0.18f, 0.80f);
|
||||
colors[ImGuiCol_FrameBgActive] = ImVec4(0.24f, 0.24f, 0.24f, 0.80f);
|
||||
|
||||
colors[ImGuiCol_TitleBg] = ImVec4(0.04f, 0.04f, 0.04f, 1.00f);
|
||||
colors[ImGuiCol_TitleBgActive] = ImVec4(0.48f, 0.16f, 0.16f, 1.00f);
|
||||
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.48f, 0.16f, 0.16f, 1.00f);
|
||||
@@ -194,16 +194,16 @@ void overlay::SpiceOverlay::init() {
|
||||
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
|
||||
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
|
||||
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
|
||||
colors[ImGuiCol_CheckMark] = ImVec4(0.56f, 0.10f, 0.10f, 1.00f);
|
||||
colors[ImGuiCol_CheckMark] = ImVec4(0.89f, 0.00f, 0.19f, 1.00f);
|
||||
colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 0.19f, 0.19f, 0.40f);
|
||||
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.89f, 0.00f, 0.19f, 1.00f);
|
||||
|
||||
colors[ImGuiCol_Button] = ImVec4(1.00f, 0.19f, 0.19f, 0.40f);
|
||||
colors[ImGuiCol_ButtonHovered] = ImVec4(0.80f, 0.17f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_ButtonActive] = ImVec4(0.89f, 0.00f, 0.19f, 1.00f);
|
||||
colors[ImGuiCol_ButtonActive] = ImVec4(0.80f, 0.17f, 0.00f, 0.90f);
|
||||
|
||||
colors[ImGuiCol_Header] = ImVec4(0.33f, 0.35f, 0.36f, 0.53f);
|
||||
colors[ImGuiCol_HeaderHovered] = ImVec4(0.76f, 0.28f, 0.44f, 0.67f);
|
||||
colors[ImGuiCol_HeaderHovered] = ImVec4(0.44f, 0.44f, 0.44f, 0.67f);
|
||||
colors[ImGuiCol_HeaderActive] = ImVec4(0.47f, 0.47f, 0.47f, 0.67f);
|
||||
colors[ImGuiCol_Separator] = ImVec4(0.32f, 0.32f, 0.32f, 1.00f);
|
||||
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.32f, 0.32f, 0.32f, 1.00f);
|
||||
@@ -228,7 +228,7 @@ void overlay::SpiceOverlay::init() {
|
||||
colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);
|
||||
colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);
|
||||
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.04f);
|
||||
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.02f);
|
||||
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
|
||||
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
|
||||
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
|
||||
@@ -270,13 +270,14 @@ void overlay::SpiceOverlay::init() {
|
||||
ImFontConfig config {};
|
||||
config.MergeMode = true;
|
||||
|
||||
log_misc("overlay", "loading fonts, failures are not fatal");
|
||||
add_font("simsun.ttc", &config, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
|
||||
add_font("arial.ttc", &config, io.Fonts->GetGlyphRangesCyrillic());
|
||||
add_font("arial.ttf", &config, io.Fonts->GetGlyphRangesCyrillic());
|
||||
add_font("meiryu.ttc", &config, io.Fonts->GetGlyphRangesJapanese());
|
||||
add_font("meiryo.ttc", &config, io.Fonts->GetGlyphRangesJapanese());
|
||||
add_font("gulim.ttc", &config, io.Fonts->GetGlyphRangesKorean());
|
||||
add_font("cordia.ttc", &config, io.Fonts->GetGlyphRangesThai());
|
||||
add_font("arial.ttc", &config, io.Fonts->GetGlyphRangesVietnamese());
|
||||
add_font("cordia.ttf", &config, io.Fonts->GetGlyphRangesThai());
|
||||
add_font("arial.ttf", &config, io.Fonts->GetGlyphRangesVietnamese());
|
||||
|
||||
// add special font
|
||||
if (avs::game::is_model("LDJ")) {
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace overlay {
|
||||
ImVec2 init_size = ImVec2(0, 0);
|
||||
ImVec2 size_min = ImVec2(0, 0);
|
||||
ImVec2 size_max = ImVec2(-1, -1);
|
||||
float bg_alpha = 0.8f;
|
||||
float bg_alpha = 0.96f;
|
||||
|
||||
Window(SpiceOverlay *overlay);
|
||||
};
|
||||
|
||||
@@ -556,9 +556,16 @@ namespace overlay::windows {
|
||||
}
|
||||
|
||||
// disclaimer
|
||||
// note: distribution of modified version of this software without providing source is GPLv3 license violation.
|
||||
ImGui::TextColored(
|
||||
ImVec4(1, 0.5f, 0.5f, 1.f),
|
||||
"Do NOT stream or upload game data anywhere public! Support arcades when you can. Thanks.");
|
||||
"spice2x is free & open source; if you paid money for it, you got scammed.");
|
||||
if (cfg::CONFIGURATOR_STANDALONE) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::TextLink(PROJECT_URL)) {
|
||||
launch_shell(PROJECT_URL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Config::build_buttons(const std::string &name, std::vector<Button> *buttons, int min, int max) {
|
||||
@@ -1720,6 +1727,13 @@ namespace overlay::windows {
|
||||
std::vector<int> analogs_midi_indices;
|
||||
if (this->analogs_devices_selected >= 0) {
|
||||
auto device = this->analogs_devices.at(this->analogs_devices_selected);
|
||||
|
||||
// add a tooltip to devices selector
|
||||
if (!device->name.empty()) {
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker(device->name.c_str());
|
||||
}
|
||||
|
||||
switch (device->type) {
|
||||
case rawinput::MOUSE: {
|
||||
|
||||
@@ -2029,6 +2043,11 @@ namespace overlay::windows {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Reset")) {
|
||||
analog.resetValues();
|
||||
}
|
||||
|
||||
// clean up
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
@@ -2633,17 +2652,16 @@ namespace overlay::windows {
|
||||
|
||||
// check hidden option
|
||||
if (!this->options_show_hidden && option.value.empty()) {
|
||||
|
||||
// skip hidden entries
|
||||
if (definition.hidden) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for game exclusivity
|
||||
if (!definition.game_name.empty()) {
|
||||
if (definition.game_name != this->games_selected_name) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// check for game exclusivity
|
||||
if (!definition.game_name.empty()) {
|
||||
if (definition.game_name != this->games_selected_name) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2822,7 +2840,10 @@ namespace overlay::windows {
|
||||
std::string current_item = option.value_text();
|
||||
for (auto &element : definition.elements) {
|
||||
if (element.first == current_item) {
|
||||
current_item += fmt::format(" ({})", element.second);
|
||||
if (!element.second.empty()) {
|
||||
current_item += fmt::format(" ({})", element.second);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (current_item.empty()) {
|
||||
@@ -2830,11 +2851,12 @@ namespace overlay::windows {
|
||||
}
|
||||
if (ImGui::BeginCombo("##combo", current_item.c_str(), 0)) {
|
||||
for (auto &element : definition.elements) {
|
||||
bool selected = current_item == element.first;
|
||||
std::string label = element.first;
|
||||
if (!element.second.empty()) {
|
||||
label += fmt::format(" ({})", element.second);
|
||||
}
|
||||
|
||||
bool selected = current_item == label;
|
||||
if (ImGui::Selectable(label.c_str(), selected)) {
|
||||
this->options_dirty = true;
|
||||
option.value = element.first;
|
||||
@@ -2905,26 +2927,28 @@ namespace overlay::windows {
|
||||
}
|
||||
|
||||
void Config::build_about() {
|
||||
#ifndef SPICE_LINUX
|
||||
ImGui::TextUnformatted(std::string(
|
||||
"spice2x (a fork of SpiceTools)\r\n"
|
||||
"=========================\r\n" +
|
||||
to_string(VERSION_STRING)).c_str());
|
||||
#else
|
||||
ImGui::TextUnformatted(std::string(
|
||||
"spice2x (a fork of SpiceTools) for Linux\r\n"
|
||||
"=========================\r\n" +
|
||||
to_string(VERSION_STRING)).c_str());
|
||||
#endif
|
||||
|
||||
ImGui::TextUnformatted("");
|
||||
|
||||
if (ImGui::Button(PROJECT_URL)) {
|
||||
if (ImGui::TextLink(PROJECT_URL)) {
|
||||
launch_shell(PROJECT_URL);
|
||||
}
|
||||
|
||||
|
||||
ImGui::TextUnformatted("");
|
||||
ImGui::TextUnformatted(std::string(
|
||||
resutil::load_file_string_crlf(IDR_README) +
|
||||
"\r\n" +
|
||||
"Changelog (Highlights):\r\n" +
|
||||
resutil::load_file_string_crlf(IDR_CHANGELOG)).c_str());
|
||||
}
|
||||
|
||||
void Config::build_licenses() {
|
||||
ImGui::TextUnformatted(resutil::load_file_string_crlf(IDR_README).c_str());
|
||||
ImGui::TextUnformatted("");
|
||||
ImGui::TextUnformatted("");
|
||||
ImGui::TextUnformatted("Licenses:");
|
||||
ImGui::TextUnformatted(resutil::load_file_string_crlf(IDR_LICENSES).c_str());
|
||||
}
|
||||
|
||||
@@ -2977,7 +3001,7 @@ namespace overlay::windows {
|
||||
|
||||
void Config::build_menu(int *game_selected) {
|
||||
bool about_popup = false;
|
||||
bool licenses_popup = false;
|
||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.14f, 0.14f, 0.14f, 1.0f));
|
||||
if (ImGui::BeginMenuBar()) {
|
||||
|
||||
// [spice2x]
|
||||
@@ -2987,9 +3011,6 @@ namespace overlay::windows {
|
||||
if (ImGui::MenuItem("spice2x.github.io")) {
|
||||
launch_shell(PROJECT_URL);
|
||||
}
|
||||
if (ImGui::MenuItem("Licenses")) {
|
||||
licenses_popup = true;
|
||||
}
|
||||
if (ImGui::MenuItem("About")) {
|
||||
about_popup = true;
|
||||
}
|
||||
@@ -3024,9 +3045,13 @@ namespace overlay::windows {
|
||||
}
|
||||
|
||||
// game selector
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.34f, 0.14f, 0.14f, 0.54f));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.34f, 0.14f, 0.14f, 0.54f));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.34f, 0.14f, 0.14f, 0.64f));
|
||||
ImGui::PushItemWidth(MIN(700, MAX(100, ImGui::GetWindowSize().x - 400)));
|
||||
ImGui::Combo("##game_selector", game_selected, games_names.data(), (int)games_list.size());
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
ImGui::BeginDisabled();
|
||||
if (!avs::game::is_model("000")) {
|
||||
@@ -3037,13 +3062,12 @@ namespace overlay::windows {
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor(); // ImGuiCol_PopupBg
|
||||
|
||||
// workaround for popups triggered by menu, see https://github.com/ocornut/imgui/issues/331
|
||||
if (about_popup) {
|
||||
ImGui::OpenPopup("About##topbarpopup");
|
||||
}
|
||||
if (licenses_popup) {
|
||||
ImGui::OpenPopup("Licenses##topbarpopup");
|
||||
}
|
||||
|
||||
// draw popups
|
||||
{
|
||||
@@ -3062,13 +3086,6 @@ namespace overlay::windows {
|
||||
this->build_about();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::SetNextWindowSize(popup_size, ImGuiCond_Appearing);
|
||||
ImGui::SetNextWindowPos(popup_pos, ImGuiCond_Appearing);
|
||||
bool unused_open3 = true;
|
||||
if (ImGui::BeginPopupModal("Licenses##topbarpopup", &unused_open3)) {
|
||||
this->build_licenses();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,6 @@ namespace overlay::windows {
|
||||
void build_options(
|
||||
std::vector<Option> *options, const std::string &category, const std::string *filter=nullptr);
|
||||
void build_about();
|
||||
void build_licenses();
|
||||
void build_launcher();
|
||||
void launch_shell(LPCSTR app, LPCSTR file=nullptr);
|
||||
|
||||
|
||||
@@ -70,7 +70,6 @@ namespace overlay::windows {
|
||||
raw_input_view();
|
||||
touch_view();
|
||||
lcd_view();
|
||||
about_view();
|
||||
ddr_timing_view();
|
||||
iidx_effectors_view();
|
||||
}
|
||||
@@ -788,27 +787,6 @@ namespace overlay::windows {
|
||||
}
|
||||
}
|
||||
|
||||
void Control::about_view() {
|
||||
if (ImGui::CollapsingHeader("About")) {
|
||||
if (ImGui::TreeNode("Changelog")) {
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginChild("changelog", ImVec2(400, 400))) {
|
||||
ImGui::TextUnformatted(resutil::load_file_string(IDR_CHANGELOG).c_str());
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
if (ImGui::TreeNode("Licenses")) {
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginChild("changelog", ImVec2(400, 400), false,
|
||||
ImGuiWindowFlags_HorizontalScrollbar
|
||||
| ImGuiWindowFlags_AlwaysHorizontalScrollbar)) {
|
||||
ImGui::TextUnformatted(resutil::load_file_string(IDR_LICENSES).c_str());
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Control::ddr_timing_view() {
|
||||
if (avs::game::is_model("MDX") && ImGui::CollapsingHeader("DDR Timing")) {
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace overlay::windows {
|
||||
| ImGuiWindowFlags_NoNavFocus
|
||||
| ImGuiWindowFlags_NoNavInputs
|
||||
| ImGuiWindowFlags_NoDocking;
|
||||
this->bg_alpha = 0.4f;
|
||||
this->bg_alpha = 0.5f;
|
||||
this->start_time = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/io.h"
|
||||
#include "cfg/screen_resize.h"
|
||||
#include "hooks/graphics/backends/d3d9/d3d9_backend.h"
|
||||
@@ -88,13 +89,32 @@ namespace overlay::windows {
|
||||
void GenericSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {}
|
||||
|
||||
void GenericSubScreen::build_content() {
|
||||
this->flags |= ImGuiWindowFlags_NoBackground;
|
||||
|
||||
if (this->disabled_message.has_value()) {
|
||||
this->flags &= ~ImGuiWindowFlags_NoBackground;
|
||||
ImGui::TextColored(YELLOW, "%s", this->disabled_message.value().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
this->draw_texture();
|
||||
|
||||
if (!this->texture) {
|
||||
this->flags &= ~ImGuiWindowFlags_NoBackground;
|
||||
ImGui::TextColored(YELLOW, "Failed to acquire surface texture for subscreen.");
|
||||
if (avs::game::is_model("LDJ")) {
|
||||
ImGui::TextColored(
|
||||
YELLOW,
|
||||
"Ensure that you are using the correct DLL type, \n"
|
||||
"or patch the DLL to properly enable TDJ mode.");
|
||||
} else if (avs::game::is_model("KFC")) {
|
||||
ImGui::TextColored(
|
||||
YELLOW,
|
||||
"Ensure that you did not accidentally enable a patch \n"
|
||||
"that turns off subscreen rendering.");
|
||||
}
|
||||
}
|
||||
|
||||
#if OVERLAYDBG
|
||||
if (this->status_message.has_value()) {
|
||||
log_warning("sub::overlay", "{}", this->status_message.value().c_str());
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace overlay::windows {
|
||||
if (GRAPHICS_IIDX_WSUB) {
|
||||
this->disabled_message =
|
||||
"Close this overlay and use the second window.\n"
|
||||
"Or, turn on -iidxnosub to use the overlay instead.";
|
||||
"If you don't see the window, double check your DLL type and apply TDJ I/O patches as needed.";
|
||||
this->draws_window = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace overlay::windows {
|
||||
{"arkmmd.dll", {"gamemmd.dll"}},
|
||||
{"arkklp.dll", {"lpac.dll"}},
|
||||
{"arknck.dll", {"weac.dll"}},
|
||||
{"gdxg.dll", {"game.dll"}}
|
||||
{"gdxg.dll", {"game.dll", "libshare-pj.dll", "boot.dll"}}
|
||||
};
|
||||
|
||||
static size_t url_recent_idx = -1;
|
||||
@@ -230,7 +230,13 @@ namespace overlay::windows {
|
||||
auto const register_fn = reinterpret_cast<decltype(&LdrRegisterDllNotification)>
|
||||
(GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "LdrRegisterDllNotification"));
|
||||
|
||||
auto callback = [] CALLBACK (ULONG reason, PCLDR_DLL_NOTIFICATION_DATA data, PVOID ctx) {
|
||||
auto callback =
|
||||
#ifdef _MSC_VER
|
||||
static_cast<PLDR_DLL_NOTIFICATION_FUNCTION>([]
|
||||
#else
|
||||
[] CALLBACK
|
||||
#endif
|
||||
(ULONG reason, PCLDR_DLL_NOTIFICATION_DATA data, PVOID ctx) {
|
||||
if (reason == LDR_DLL_NOTIFICATION_REASON_LOADED) {
|
||||
auto const dll = strtolower(std::filesystem::path({
|
||||
data->Loaded.FullDllName->Buffer,
|
||||
@@ -241,7 +247,11 @@ namespace overlay::windows {
|
||||
static_cast<PatchManager*>(ctx)->reload_local_patches(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (register_fn && NT_SUCCESS(register_fn(0, callback, this, &ldr_notify_cookie))) {
|
||||
log_info("patchmanager", "registered for DLL load notifications");
|
||||
@@ -278,6 +288,12 @@ namespace overlay::windows {
|
||||
this->reload_local_patches();
|
||||
}
|
||||
|
||||
if (avs::game::DLL_NAME == "kamunity.dll") {
|
||||
ImGui::TextColored(ImVec4(1.f, 0.f, 0.f, 1.f), "Patches are not supported for Unity-based games.");
|
||||
ImGui::TextColored(ImVec4(1.f, 0.f, 0.f, 1.f), "Instead, look for downloads of pre-patched DLLs.");
|
||||
return;
|
||||
}
|
||||
|
||||
// game code info
|
||||
std::string identifiers;
|
||||
identifiers += avs::game::get_identifier() + "\n\n";
|
||||
|
||||
@@ -55,6 +55,7 @@ namespace overlay::windows {
|
||||
scene.offset_y = 0;
|
||||
scene.scale_x = 1.f;
|
||||
scene.scale_y = 1.f;
|
||||
scene.duplicate = cfg::ScreenDuplicateMode::None;
|
||||
}
|
||||
|
||||
cfg::SCREENRESIZE->enable_window_resize = false;
|
||||
@@ -149,6 +150,17 @@ namespace overlay::windows {
|
||||
ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value.");
|
||||
}
|
||||
|
||||
static const char* dupe_items[] = { "None", "Copy Left", "Copy Right" };
|
||||
ImGui::Combo(
|
||||
"Duplicate",
|
||||
reinterpret_cast<int *>(&scene.duplicate),
|
||||
dupe_items,
|
||||
ARRAYSIZE(dupe_items));
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker(
|
||||
"Show an identical copy of the image on the left or right, allowing you to achieve "
|
||||
"wrap-around effect when an offset is set.");
|
||||
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
|
||||
@@ -347,40 +347,38 @@ void rawinput::RawInputManager::devices_scan_rawinput(RAWINPUTDEVICELIST *device
|
||||
if (hid_handle != INVALID_HANDLE_VALUE) {
|
||||
|
||||
// check manufacturer string
|
||||
std::wstring man_ws;
|
||||
std::string man_ws;
|
||||
wchar_t man_str_buffer[256] {};
|
||||
if (HidD_GetManufacturerString(hid_handle, man_str_buffer, sizeof(man_str_buffer))) {
|
||||
man_ws = std::wstring(man_str_buffer);
|
||||
man_ws = wchar_to_u8(man_str_buffer);
|
||||
}
|
||||
|
||||
// get product string
|
||||
std::wstring prod_ws;
|
||||
std::string prod_ws;
|
||||
wchar_t prod_str_buffer[256] {};
|
||||
if (HidD_GetProductString(hid_handle, prod_str_buffer, sizeof(prod_str_buffer))) {
|
||||
prod_ws = std::wstring(prod_str_buffer);
|
||||
prod_ws = wchar_to_u8(prod_str_buffer);
|
||||
}
|
||||
|
||||
// For Bluetooth LE HID devices (e.g., newer Xbox controllers) HidD_GetProductString
|
||||
// and HidD_GetManufacturerString will return blank strings.
|
||||
// https://docs.microsoft.com/en-us/answers/questions/401236/hidd-getproductstring-with-ble-hid-device.html
|
||||
if (man_ws.empty() && prod_ws.empty()) {
|
||||
prod_ws = s2ws(device_description);
|
||||
prod_ws = device_description;
|
||||
}
|
||||
|
||||
// build desc string
|
||||
std::wstring desc_ws;
|
||||
std::string desc_ws;
|
||||
if (string_begins_with(prod_ws, man_ws)) {
|
||||
desc_ws = prod_ws;
|
||||
} else {
|
||||
desc_ws = man_ws;
|
||||
if (!man_ws.empty() && !prod_ws.empty()) {
|
||||
desc_ws += L" ";
|
||||
desc_ws += " ";
|
||||
}
|
||||
desc_ws += prod_ws;
|
||||
}
|
||||
|
||||
// convert to normal string
|
||||
new_device.desc = ws2s(desc_ws);
|
||||
new_device.desc = desc_ws;
|
||||
|
||||
// get attributes
|
||||
if (!HidD_GetAttributes(hid_handle, &hid_attributes)) {
|
||||
@@ -1148,7 +1146,7 @@ std::string rawinput::RawInputManager::rawinput_get_device_description(const raw
|
||||
|
||||
// get property
|
||||
DWORD desc_size = 0;
|
||||
if (SetupDiGetDeviceRegistryProperty(
|
||||
if (SetupDiGetDeviceRegistryPropertyW(
|
||||
devinfo, &devinfo_data, SPDRP_DEVICEDESC, nullptr, nullptr, 0, &desc_size))
|
||||
{
|
||||
continue;
|
||||
@@ -1160,14 +1158,14 @@ std::string rawinput::RawInputManager::rawinput_get_device_description(const raw
|
||||
// allocate buffer
|
||||
auto desc_data = std::make_unique<BYTE[]>(desc_size);
|
||||
|
||||
if (!SetupDiGetDeviceRegistryProperty(
|
||||
if (!SetupDiGetDeviceRegistryPropertyW(
|
||||
devinfo, &devinfo_data, SPDRP_DEVICEDESC, nullptr, desc_data.get(), desc_size, nullptr))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// kthxbye
|
||||
device_description = std::string(reinterpret_cast<char *>(desc_data.get()));
|
||||
device_description = wchar_to_u8(reinterpret_cast<PWCHAR>(desc_data.get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,6 @@ namespace rawinput {
|
||||
|
||||
HotplugManager *hotplug;
|
||||
std::vector<Device> devices;
|
||||
HWND input_hwnd = nullptr;
|
||||
WNDCLASSEX input_hwnd_class {};
|
||||
std::thread *input_thread = nullptr;
|
||||
std::thread *flush_thread = nullptr;
|
||||
@@ -91,6 +90,8 @@ namespace rawinput {
|
||||
|
||||
public:
|
||||
|
||||
HWND input_hwnd = nullptr;
|
||||
|
||||
RawInputManager();
|
||||
~RawInputManager();
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ developers when you can. Sources should be included, have fun!
|
||||
|
||||
spice2x fork maintainers:
|
||||
* sp2xdev team
|
||||
* the lab
|
||||
|
||||
SpiceTools original contributors:
|
||||
* cube, felix, dinsfire, nolm, s2, crazyredmachine (jotaro44), mon, nibs
|
||||
|
||||
@@ -240,8 +240,11 @@ static LRESULT CALLBACK SpiceTouchWndProc(HWND hWnd, UINT msg, WPARAM wParam, LP
|
||||
rawinput::touch::display_update();
|
||||
}
|
||||
|
||||
const auto is_windowed_sub =
|
||||
(GRAPHICS_IIDX_WSUB && hWnd == TDJ_SUBSCREEN_WINDOW) || (hWnd == SDVX_SUBSCREEN_WINDOW);
|
||||
|
||||
if (msg == WM_CLOSE) {
|
||||
if ((GRAPHICS_IIDX_WSUB && hWnd == TDJ_SUBSCREEN_WINDOW) || (hWnd == SDVX_SUBSCREEN_WINDOW)) {
|
||||
if (is_windowed_sub) {
|
||||
log_misc("touch", "ignore WM_CLOSE for subscreen window");
|
||||
return false;
|
||||
}
|
||||
@@ -461,11 +464,14 @@ static LRESULT CALLBACK SpiceTouchWndProc(HWND hWnd, UINT msg, WPARAM wParam, LP
|
||||
};
|
||||
|
||||
// check if imgui is handling this mouse event
|
||||
if (overlay::OVERLAY != nullptr && overlay::OVERLAY->get_active() && ImGui::GetIO().WantCaptureMouse) {
|
||||
if (is_windowed_sub) {
|
||||
// do nothing, don't let imgui hijack clicks on the sub window
|
||||
result.action = ACTION_PASS;
|
||||
|
||||
} else if (overlay::OVERLAY != nullptr && overlay::OVERLAY->get_active() && ImGui::GetIO().WantCaptureMouse) {
|
||||
result.action = ACTION_RETURN_DEFAULT;
|
||||
|
||||
} else if (TOUCH_HANDLER != nullptr) {
|
||||
|
||||
// call touch handler
|
||||
TOUCH_HANDLER->handle_message(result, hWnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
#include "build/defs.h"
|
||||
#include "deferlog.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace deferredlogs {
|
||||
|
||||
const std::initializer_list<std::string> SUPERSTEP_SOUND_ERROR_MESSAGE = {
|
||||
"audio initialization error was previously detected during boot!",
|
||||
" this crash is most likely related to audio init failure",
|
||||
" * check if the default audio device has changed",
|
||||
" * fix your audio device settings (e.g., sample rate)",
|
||||
" * double check your spice audio options and patches"
|
||||
};
|
||||
|
||||
std::mutex deferred_errors_mutex;
|
||||
std::vector<std::vector<std::string>> deferred_errors;
|
||||
|
||||
void defer_error_messages(std::initializer_list<std::string> messages) {
|
||||
std::lock_guard<std::mutex> lock(deferred_errors_mutex);
|
||||
deferred_errors.emplace_back(messages);
|
||||
}
|
||||
|
||||
void dump_to_logger(bool is_crash) {
|
||||
static std::once_flag printed;
|
||||
std::call_once(printed, [is_crash]() {
|
||||
|
||||
// move to a local vector under lock first
|
||||
// this is to avoid holding a lock while emitting to the logger, which may deadlock
|
||||
// due to recursive calls (e.g., via log hooks used for failure detection which then
|
||||
// again calls into defer_error_messages), and std::mutex cannot be acquired recursively
|
||||
std::vector<std::vector<std::string>> errors;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(deferred_errors_mutex);
|
||||
if (deferred_errors.empty() && !is_crash) {
|
||||
return;
|
||||
}
|
||||
errors = std::move(deferred_errors);
|
||||
}
|
||||
|
||||
std::string msg;
|
||||
msg += "\n\n";
|
||||
msg += "/-------------------------- spice2x auto-troubleshooter -----------------------\\\n";
|
||||
msg += "\n";
|
||||
msg += " spice2x version: " + to_string(VERSION_STRING_CFG) + "\n";
|
||||
msg += "\n";
|
||||
|
||||
if (is_crash) {
|
||||
msg += " the game has crashed\n";
|
||||
msg += " * share this entire log file with someone for troubleshooting (log.txt)\n";
|
||||
msg += " * spice will also attempt to create a minidump (minidump.dmp)\n";
|
||||
msg += " minidump should only be shared with people you trust as it may contain\n";
|
||||
msg += " sensitive data (PCBID, card ID, etc)\n";
|
||||
msg += "\n";
|
||||
}
|
||||
|
||||
for (auto messages : errors) {
|
||||
for (auto message : messages) {
|
||||
msg += " " + message + "\n";
|
||||
}
|
||||
msg += "\n";
|
||||
}
|
||||
|
||||
msg += " unsure what to do next?\n";
|
||||
msg += " * update to the latest version:\n";
|
||||
msg += " https://github.com/spice2x/spice2x.github.io/releases/latest\n";
|
||||
msg += " * check the FAQ:\n";
|
||||
msg += " https://github.com/spice2x/spice2x.github.io/wiki/Known-issues\n";
|
||||
msg += "\n";
|
||||
msg += "\\------------------------- spice2x auto-troubleshooter ------------------------/\n";
|
||||
|
||||
log_warning("troubleshooter", "{}", msg);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <initializer_list>
|
||||
|
||||
namespace deferredlogs {
|
||||
|
||||
// some shared error messages
|
||||
extern const std::initializer_list<std::string> SUPERSTEP_SOUND_ERROR_MESSAGE;
|
||||
|
||||
void defer_error_messages(std::initializer_list<std::string> messages);
|
||||
void dump_to_logger(bool is_crash=false);
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
#include <set>
|
||||
#include "logging.h"
|
||||
#include "libutils.h"
|
||||
#include "scope_guard.h"
|
||||
#include "dependencies.h"
|
||||
|
||||
using loader_hint = std::tuple<std::string, std::string, std::string>;
|
||||
|
||||
namespace {
|
||||
// list of commonly missing dependencies and tips on where to get them
|
||||
std::vector<loader_hint> hints = {
|
||||
{
|
||||
"msvcr100.dll",
|
||||
"Visual Studio 2010 (VC++ 10.0) SP1 Redistributable",
|
||||
"Download and install from https://github.com/spice2x/spice2x.github.io/wiki/Visual-Cpp-Redistributable-Runtimes",
|
||||
},
|
||||
{
|
||||
"d3dx9_43.dll",
|
||||
"DirectX End-User Runtimes",
|
||||
"Download and install from https://www.microsoft.com/en-us/download/details.aspx?id=35"
|
||||
},
|
||||
{
|
||||
"nvEncodeAPI64.dll",
|
||||
"NVIDIA Graphics Driver",
|
||||
"For non-NVIDIA GPUs, copy the stub file from the spice2x release .zip.",
|
||||
},
|
||||
{
|
||||
"nvcuda.dll",
|
||||
"NVIDIA Graphics Driver",
|
||||
"For non-NVIDIA GPUs, copy the stub file from the spice2x release .zip.",
|
||||
},
|
||||
{
|
||||
"nvcuvid.dll",
|
||||
"NVIDIA Graphics Driver",
|
||||
"For non-NVIDIA GPUs, copy the stub file from the spice2x release .zip.",
|
||||
},
|
||||
{
|
||||
"cpusbxpkm.dll",
|
||||
"LovePlus Printer DLL",
|
||||
"Copy the stub file from the spice2x release .zip.",
|
||||
},
|
||||
};
|
||||
|
||||
std::set<std::wstring> failed = {};
|
||||
|
||||
auto read_imports(const std::filesystem::path& path) -> std::vector<std::filesystem::path> {
|
||||
auto result = std::vector<std::filesystem::path> {};
|
||||
auto const file = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
|
||||
if (file == INVALID_HANDLE_VALUE) {
|
||||
return result;
|
||||
}
|
||||
|
||||
auto const file_ = scope_guard { [file] { CloseHandle(file); } };
|
||||
auto const mapping = CreateFileMapping(file, nullptr, PAGE_READONLY, 0, 0, nullptr);
|
||||
|
||||
if (!mapping) {
|
||||
return result;
|
||||
}
|
||||
|
||||
auto const mapping_ = scope_guard { [mapping] { CloseHandle(mapping); } };
|
||||
auto const view = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
|
||||
|
||||
if (!view) {
|
||||
return result;
|
||||
}
|
||||
|
||||
auto const view_ = scope_guard { [view] { UnmapViewOfFile(view); } };
|
||||
auto const dos = static_cast<PIMAGE_DOS_HEADER>(view);
|
||||
|
||||
if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
|
||||
return result;
|
||||
}
|
||||
|
||||
auto const base = static_cast<std::uint8_t*>(view);
|
||||
auto const nt = reinterpret_cast<PIMAGE_NT_HEADERS>(base + dos->e_lfanew);
|
||||
|
||||
if (nt->Signature != IMAGE_NT_SIGNATURE) {
|
||||
return result;
|
||||
}
|
||||
|
||||
auto const imports = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||
|
||||
if (imports.VirtualAddress == 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
auto desc = reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(base + libutils::rva2offset(nt, imports.VirtualAddress));
|
||||
|
||||
while (desc->Name != 0) {
|
||||
auto const filename = reinterpret_cast<char*>(base + libutils::rva2offset(nt, desc->Name));
|
||||
auto entry_path = path.parent_path().append(filename);
|
||||
|
||||
// try to use library in the same directory if one exists
|
||||
// otherwise, assume system library and use filename only
|
||||
if (!std::filesystem::exists(entry_path)) {
|
||||
entry_path = filename;
|
||||
}
|
||||
|
||||
result.emplace_back(entry_path);
|
||||
desc++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace dependencies {
|
||||
auto walk(const std::filesystem::path& path, const std::string& prefix) -> bool {
|
||||
// try to load the library -- skip walking if it loads successfully
|
||||
if (auto const module = LoadLibraryW(path.c_str())) {
|
||||
FreeLibrary(module);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto const error = GetLastError();
|
||||
auto const filename = path.filename().string();
|
||||
|
||||
log_misc("dependencies", "{}{}", prefix + (prefix.empty() ? "": "|-- "), filename);
|
||||
|
||||
if (failed.contains(path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
failed.insert(path);
|
||||
|
||||
auto const dependencies = read_imports(path);
|
||||
auto const next_prefix = prefix + (prefix.empty() ? " ": "| ");
|
||||
|
||||
if (!dependencies.empty()) {
|
||||
for (auto const& item : dependencies) {
|
||||
walk(item, next_prefix);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto const& [dll, name, hint] : hints) {
|
||||
if (_stricmp(dll.c_str(), filename.c_str()) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
log_warning("dependencies", "{}|-- [!] {}", next_prefix, name.c_str());
|
||||
log_warning("dependencies", "{}|-- {}", next_prefix, hint.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
log_warning("dependencies", "{}|-- [!] The library could not be loaded. ({})", next_prefix, error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
namespace dependencies {
|
||||
auto walk(const std::filesystem::path& path, const std::string& prefix = "") -> bool;
|
||||
}
|
||||
@@ -106,8 +106,10 @@ bool detour::inline_restore(void *address, char *data) {
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma ide diagnostic ignored "OCDFAInspection"
|
||||
#endif
|
||||
|
||||
static void *pe_offset(void *ptr, size_t offset) {
|
||||
if (offset == 0) {
|
||||
@@ -282,7 +284,9 @@ void **detour::iat_find_proc(const char *iid_name, void *proc, HMODULE module) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
void *detour::iat_try(const char *function, void *new_func, HMODULE module, const char *iid_name) {
|
||||
|
||||
|
||||
@@ -14,11 +14,13 @@ namespace execexe {
|
||||
static decltype(&CreateFileA) execexe_CreateFileA = nullptr;
|
||||
static decltype(&CreateFileW) execexe_CreateFileW = nullptr;
|
||||
static decltype(&CloseHandle) execexe_CloseHandle = nullptr;
|
||||
static uint64_t (*execexe_PreLoadLibraries)(const char *) = nullptr;
|
||||
|
||||
static std::wstring plugins_dir;
|
||||
static acioemu::ACIOHandle *acio = nullptr;
|
||||
static std::wstring port_name;
|
||||
static bool port_opened = false;
|
||||
static std::function<void()> deferred_function = nullptr;
|
||||
|
||||
static HANDLE WINAPI execexe_CreateFileA_hook(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
|
||||
@@ -62,6 +64,24 @@ namespace execexe {
|
||||
return execexe_CloseHandle(hObject);
|
||||
}
|
||||
|
||||
static uint64_t execexe_PreLoadLibraries_hook(const char *libs) {
|
||||
static bool init = false;
|
||||
|
||||
uint64_t result = execexe_PreLoadLibraries(libs);
|
||||
if (init) {
|
||||
return result;
|
||||
}
|
||||
|
||||
init = true;
|
||||
log_info("execexe", "execexe_PreLoadLibraries hook hit");
|
||||
|
||||
if (deferred_function) {
|
||||
deferred_function();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
HMODULE init() {
|
||||
execexe_module = libutils::load_library("execexe.dll");
|
||||
execexe_LoadLibraryW = libutils::get_proc<decltype(&LoadLibraryW)>(execexe_module, MAKEINTRESOURCE(34));
|
||||
@@ -70,6 +90,7 @@ namespace execexe {
|
||||
execexe_CloseHandle = libutils::get_proc<decltype(&CloseHandle)>(execexe_module, MAKEINTRESOURCE(7));
|
||||
execexe_CreateFileA = libutils::get_proc<decltype(&CreateFileA)>(execexe_module, MAKEINTRESOURCE(9));
|
||||
execexe_CreateFileW = libutils::get_proc<decltype(&CreateFileW)>(execexe_module, MAKEINTRESOURCE(11));
|
||||
execexe_PreLoadLibraries = libutils::get_proc<decltype(execexe_PreLoadLibraries)>(execexe_module, MAKEINTRESOURCE(48));
|
||||
|
||||
auto module_path = libutils::module_file_name(nullptr);
|
||||
module_path = module_path.replace_extension("");
|
||||
@@ -79,6 +100,15 @@ namespace execexe {
|
||||
return execexe_module;
|
||||
}
|
||||
|
||||
void init_deferred(std::function<void()> init_func) {
|
||||
if (deferred_function) {
|
||||
log_fatal("execexe", "deferred init function is already set");
|
||||
}
|
||||
deferred_function = std::move(init_func);
|
||||
detour::trampoline("execexe.dll", MAKEINTRESOURCE(48),
|
||||
execexe_PreLoadLibraries_hook, &execexe_PreLoadLibraries);
|
||||
}
|
||||
|
||||
void init_port_hook(const std::wstring &portName, acioemu::ACIOHandle *acioHandle) {
|
||||
static bool init = false;
|
||||
if (init)
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <windows.h>
|
||||
#include "acioemu/handle.h"
|
||||
|
||||
namespace execexe {
|
||||
HMODULE init();
|
||||
void init_deferred(std::function<void()> init_func);
|
||||
void init_port_hook(const std::wstring &portName, acioemu::ACIOHandle *acioHandle);
|
||||
HMODULE load_library(const char *module_name, bool fatal = true);
|
||||
FARPROC get_proc(HMODULE module, const char *proc_name, bool fatal = true);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "utils.h"
|
||||
#include "peb.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/dependencies.h"
|
||||
|
||||
std::filesystem::path libutils::module_file_name(HMODULE module) {
|
||||
std::wstring buf;
|
||||
@@ -29,22 +30,21 @@ std::filesystem::path libutils::module_file_name(HMODULE module) {
|
||||
|
||||
static inline void load_library_fail(const std::string &file_name, bool fatal) {
|
||||
std::string info_str { fmt::format(
|
||||
"\n\nPlease check if {} exists and the permissions are fine.\n"
|
||||
"\n"
|
||||
"* If the problem still persists, try installing things on this list:\n"
|
||||
" https://github.com/spice2x/spice2x.github.io/wiki/DLL-Dependencies \n"
|
||||
"\n"
|
||||
"* Still have problems after installing from above and rebooting PC?\n"
|
||||
" Avoid manually specifying DLL path (-exec) and module directory (-modules); let spice2x auto-detect unless you have a good reason not to\n"
|
||||
" Ensure you do NOT have multiple copies of the game DLLs (e.g., in contents and in contents\\modules)\n"
|
||||
" Certain games require specific NVIDIA DLLs when running with AMD/Intel GPUs (hint: look inside stub directory for DLLs)\n"
|
||||
"\n"
|
||||
"* (For advanced users) if none of the above helps, find the missing dependency using:\n"
|
||||
" https://github.com/lucasg/Dependencies (recommended for most) \n"
|
||||
" http://www.dependencywalker.com/ (for old OS) \n"
|
||||
"DLL failed to load - this is a common error. Please carefully read ALL of the following steps for a fix:\n"
|
||||
" 1. Confirm if the file ({}) exists on the disk and check the file permissions.\n"
|
||||
" 2. Follow this link and install DLL prerequisites on this list:\n"
|
||||
" https://github.com/spice2x/spice2x.github.io/wiki/DLL-Dependencies \n"
|
||||
" 3. Still have problems after installing from above and rebooting PC?\n"
|
||||
" a. Avoid manually specifying DLL path (-exec) and module directory (-modules); let spice2x auto-detect unless you have a good reason not to\n"
|
||||
" b. Ensure you do NOT have multiple copies of the game DLLs (e.g., in contents and in contents\\modules)\n"
|
||||
" c. Certain games require specific NVIDIA DLLs when running with AMD/Intel GPUs (hint: look inside stub directory for DLLs)\n"
|
||||
" 4. (For advanced users) if none of the above helps, find the missing dependency using:\n"
|
||||
" a. https://github.com/lucasg/Dependencies (recommended for most) \n"
|
||||
" b. http://www.dependencywalker.com/ (for old OS) \n"
|
||||
, file_name) };
|
||||
if (fatal) {
|
||||
log_fatal("libutils", "{}", info_str);
|
||||
log_warning("libutils", "{}", info_str);
|
||||
log_fatal("libutils", "DLL failed to load: {}, see log.txt for troubleshooting", file_name);
|
||||
} else {
|
||||
log_warning("libutils", "{}", info_str);
|
||||
}
|
||||
@@ -67,6 +67,7 @@ HMODULE libutils::load_library(const std::filesystem::path &path, bool fatal) {
|
||||
|
||||
if (!module) {
|
||||
log_warning("libutils", "'{}' couldn't be loaded: {}", path.string(), get_last_error_string());
|
||||
dependencies::walk(path);
|
||||
load_library_fail(path.filename().string(), fatal);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
|
||||
#include "logging.h"
|
||||
#include "build/defs.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
|
||||
std::string_view log_get_datetime() {
|
||||
return log_get_datetime(time(nullptr));
|
||||
@@ -12,3 +18,66 @@ std::string_view log_get_datetime(std::time_t now) {
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void show_popup(const std::string text) {
|
||||
static std::once_flag shown;
|
||||
std::call_once(shown, [text]() {
|
||||
|
||||
// minimize all windows
|
||||
// only needed because in multi-monitor full screen games, MessageBox fails to show above the game
|
||||
for (auto &hwnd : GRAPHICS_WINDOWS) {
|
||||
ShowWindow(hwnd, SW_FORCEMINIMIZE);
|
||||
}
|
||||
|
||||
// schedule a worker to terminate the game in 30 seconds in case user can't dismiss UI
|
||||
std::atomic_bool cancel_popup = false;
|
||||
auto worker = std::async(std::launch::async, [&cancel_popup]{
|
||||
for (int ms = 300; ms > 0; ms--) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
if (cancel_popup) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
launcher::stop_subsystems();
|
||||
launcher::kill();
|
||||
std::terminate();
|
||||
});
|
||||
|
||||
// MessageBox will block until user presses OK
|
||||
const std::string title = "spice2x (" + to_string(VERSION_STRING_CFG) + ")";
|
||||
MessageBox(
|
||||
nullptr,
|
||||
text.c_str(),
|
||||
title.c_str(),
|
||||
MB_OK | MB_ICONERROR | MB_TASKMODAL | MB_SETFOREGROUND | MB_TOPMOST);
|
||||
|
||||
// cancel the worker since the user acknowledged the popup
|
||||
cancel_popup = true;
|
||||
});
|
||||
}
|
||||
|
||||
void show_popup_for_crash() {
|
||||
std::string text;
|
||||
text += "Game has crashed.\n\n";
|
||||
text += "Check log.txt and look for error messages near the end of file.\n\n";
|
||||
text += "Unsure what to do next?\n";
|
||||
text += " * update spice2x to the latest version\n";
|
||||
text += " * check the FAQ on spice2x github wiki\n";
|
||||
text += " * do NOT screenshot this, instead, share log.txt with someone and ask for help\n\n";
|
||||
text += "Press Enter, Esc, Alt+F4, or click OK to exit. Otherwise, game will close in 30 seconds.";
|
||||
show_popup(text);
|
||||
}
|
||||
|
||||
void show_popup_for_fatal_error(std::string message) {
|
||||
std::string text;
|
||||
text += "A fatal error was encountered. For details, check log.txt.\n\n";
|
||||
text += "----------------------------------------------------------\n";
|
||||
text += message;
|
||||
text += "----------------------------------------------------------\n\n";
|
||||
text += "Unsure what to do next?\n";
|
||||
text += " * update spice2x to the latest version\n";
|
||||
text += " * check the FAQ on spice2x github wiki\n";
|
||||
text += " * do NOT screenshot this, instead, share log.txt with someone and ask for help\n\n";
|
||||
text += "Press Enter, Esc, Alt+F4, or click OK to exit. Otherwise, game will close in 30 seconds.";
|
||||
show_popup(text);
|
||||
}
|
||||
@@ -71,20 +71,28 @@ struct fmt::formatter<fmt_hresult> {
|
||||
}
|
||||
};
|
||||
|
||||
void show_popup_for_crash();
|
||||
void show_popup_for_fatal_error(std::string message);
|
||||
|
||||
// misc log
|
||||
#define LOG_FORMAT(level, module, fmt_str, ...) fmt::format(FMT_COMPILE("{}" fmt_str "\n"), \
|
||||
fmt_log { std::time(nullptr), level, module }, ## __VA_ARGS__)
|
||||
|
||||
#define LOG_FORMAT_POPUP(module, fmt_str, ...) fmt::format(FMT_COMPILE("{}: " fmt_str "\n"), module, ## __VA_ARGS__)
|
||||
|
||||
#define log_misc(module, format_str, ...) logger::push( \
|
||||
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||
|
||||
#define log_info(module, format_str, ...) logger::push( \
|
||||
LOG_FORMAT("I", module, format_str, ## __VA_ARGS__), logger::Style::DEFAULT)
|
||||
|
||||
#define log_warning(module, format_str, ...) logger::push( \
|
||||
LOG_FORMAT("W", module, format_str, ## __VA_ARGS__), logger::Style::YELLOW)
|
||||
|
||||
#define log_fatal(module, format_str, ...) { \
|
||||
logger::push(LOG_FORMAT("F", module, format_str, ## __VA_ARGS__), logger::Style::RED); \
|
||||
logger::push(LOG_FORMAT("F", "spice", "encountered a fatal error, you can close the window or press ctrl + c"), logger::Style::RED); \
|
||||
show_popup_for_fatal_error(LOG_FORMAT_POPUP(module, format_str, ## __VA_ARGS__)); \
|
||||
launcher::stop_subsystems(); \
|
||||
Sleep(10000); \
|
||||
launcher::kill(); \
|
||||
std::terminate(); \
|
||||
} ((void) 0 )
|
||||
|
||||
@@ -77,7 +77,7 @@ std::string peb::entry_name(const LDR_DATA_TABLE_ENTRY* entry) {
|
||||
}
|
||||
|
||||
const PEB* peb::peb_get() {
|
||||
#ifdef SPICE64
|
||||
#ifdef _WIN64
|
||||
return reinterpret_cast<const PEB *>(__readgsqword(0x60));
|
||||
#else
|
||||
return reinterpret_cast<const PEB *>(__readfsdword(0x30));
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
class scope_guard {
|
||||
private:
|
||||
std::function<void()> f;
|
||||
public:
|
||||
explicit scope_guard(std::function<void()>&& f) : f(std::move(f)) {}
|
||||
~scope_guard() {
|
||||
f();
|
||||
}
|
||||
|
||||
scope_guard(const scope_guard&) = delete;
|
||||
scope_guard& operator=(const scope_guard&) = delete;
|
||||
|
||||
scope_guard(scope_guard&&) = delete;
|
||||
scope_guard& operator=(scope_guard&&) = delete;
|
||||
};
|
||||
@@ -283,7 +283,8 @@ static inline std::string get_last_error_string() {
|
||||
LPSTR messageBuffer = nullptr;
|
||||
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS |
|
||||
FORMAT_MESSAGE_MAX_WIDTH_MASK,
|
||||
nullptr,
|
||||
error,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
@@ -292,7 +293,12 @@ static inline std::string get_last_error_string() {
|
||||
nullptr);
|
||||
|
||||
// return as string
|
||||
std::string message(messageBuffer, size);
|
||||
std::string message;
|
||||
if (size == 0) {
|
||||
message = fmt::format("(Win32 error {})", error);
|
||||
} else {
|
||||
message = fmt::format("{}(Win32 error {})", messageBuffer, error);
|
||||
}
|
||||
LocalFree(messageBuffer);
|
||||
|
||||
return message;
|
||||
@@ -333,4 +339,14 @@ static inline bool parse_width_height(const std::string wh, std::pair<uint32_t,
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline std::string wchar_to_u8(const wchar_t* wstr) {
|
||||
int size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, nullptr, 0, nullptr, nullptr);
|
||||
if (size <= 1) {
|
||||
return "";
|
||||
}
|
||||
std::string out(size - 1, 0);
|
||||
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, out.data(), size - 1, nullptr, nullptr);
|
||||
return out;
|
||||
}
|
||||
Reference in New Issue
Block a user