Compare commits

..

26 Commits

Author SHA1 Message Date
sp2xdev 0fc3aa5818 update changelog 2025-09-30 13:07:49 -07:00
sp2xdev 276874f882 update options 2025-09-29 19:28:08 -07:00
sp2xdev 1fdd270480 hide bio2 firmware update option 2025-09-29 19:03:13 -07:00
bicarus-dev 07cb6048a0 Add explanation for common exception codes (#386)
## Link to GitHub Issue, if one exists
#345 

## Description of change
Add a little warning message that shows when well-known exception codes
are encountered & caught by signal hooks.

## Testing
Tested by injecting bad instructions into the DLL.
2025-09-28 23:49:08 -07:00
bicarus-dev 61d79f76b2 ea3: remove verbose debug logs by default (#385)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Recently distributed iidx data has this enabled in ea3-config.xml by
default, exposing people's PCBID in the logs.

Find the node in the XML and remove it on boot, unless user overrides it
with a new option.

## Testing
Tested using asphyxia.
2025-09-28 20:31:48 -07:00
sp2xdev 7335d07e30 update changelog 2025-09-27 18:38:57 -07:00
bicarus-dev 9f99b71112 avs: when nvram/raw folder creation fails, show a warning message when the game eventually crashes (#383)
## Link to GitHub Issue, if one exists
#345 

## Description of change
When the directory specified in the mounttable (in avs-config.xml) lead
to bad path, remember this and show a warning message block before the
game crashes.

## Testing
Tested with simulated bad mounttable set up.
2025-09-27 18:16:20 -07:00
sp2xdev 5c3921a99c fix up error messages 2025-09-27 14:31:29 -07:00
sp2xdev 9fc397b8bc update loadlibrary failure message, again 2025-09-27 02:16:28 -07:00
bicarus-dev ddc8061268 iidx: apply signature patch for ASIO compatibility fix in iidx32 (#381)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Apply this fix automatically when needed -
https://github.com/two-torial/sp2xpatcher/pull/33

## Testing
Tested iidx31 to see that the patch does not apply
Checked -audiohookdisable to see that the logic gets skipped
Tested 2025041500 and 2025082500.
2025-09-26 01:26:49 -07:00
sp2xdev c0698a3872 update changelog 2025-09-21 19:44:47 -07:00
kazakhstan-sila 1ed3521091 patcher: add gitadora boot.dll to extra DLL list (#379)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Allow importing patches for `boot.dll`

## Testing
Observed patch for the new DLL being attempted to be imported in the
logs.
2025-09-21 12:51:04 -07:00
bicarus-dev 0dda920448 iidx, sdvx: show error text when subscreen fails to render (#378)
## Link to GitHub Issue, if one exists
#345 

## Description of change
For IIDX / SDVX, when the subscreen surface failed to capture, display
an error message, suggesting the user to patch the game as needed.

Prior to this change, the subscreen overlay just showed an invisible
window if this happened.

## Testing
Tested IIDX windowed / full screen, and voltex full screen.
2025-09-21 00:20:41 -07:00
bicarus-dev 5f7580b69e pc: add polaris chord button lights (#377)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Add lights output for the buttons lamp array. Only implemented the top
row of lights and not all of them, this is good enough.

## Testing
Tested with a Nostroller and also spiceremote.py.
2025-09-20 16:37:39 -07:00
sp2xdev 3ef1b3ecaa update changelog 2025-09-20 04:31:11 -07:00
bicarus-dev 931811da14 iidx,sdvx: detect SuperstepSound init failures and log a message upon crash (#376)
## Link to GitHub Issue, if one exists
#345 

## Description of change
If we see SuperstepSound failure during boot, remember it, and when the
game eventually crashes, log a series of warning messages to surface why
the game likely failed to launch.

## Testing
Tested both iidx and sdvx with an audio device set to not allow
exclusive mode.
2025-09-20 04:17:40 -07:00
bicarus-dev 7624ded512 pc: add option to translate absolute analog values to fader input (#375)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Add a new option that allows absolute position analog input, such as
SDVX controller knobs and IIDX turntables, to be bound as input for
Polaris Chord faders.

In practice this means turning a knob counter-clockwise results in fader
being held left (relative to velocity), and vice versa.

## Testing
Tested using FauceTwo and a mouse.
2025-09-20 03:40:18 -07:00
sp2xdev a5252939dd update iidx audio patch signature 2025-09-20 03:40:04 -07:00
GEEKiDoS 808ac0c557 Fix msvc compiler warnings (#374)
To be noticed I edited thirdparty header for fmt library (#373), idk if
it is acceptable
2025-09-18 21:57:38 -07:00
sp2xdev 4e138b244e add dsound.dll to custom DLL detection 2025-09-18 21:49:43 -07:00
sp2xdev 3fcd1bdbc9 fix option strings for force load 2025-09-18 15:59:06 -07:00
sp2xdev f857e63d5d update changelog 2025-09-18 15:27:22 -07:00
GEEKiDoS b462c838f6 Fix broken UI in windowed mode for Polaris Chord (#372) 2025-09-18 09:44:23 -07:00
sp2xdev 4d658bf273 Intel SDE is not an override 2025-09-18 02:35:32 -07:00
sp2xdev 67721c416b update UI strings 2025-09-18 02:33:18 -07:00
bicarus-dev 0b5f7787be launcher: show a giant warning when the user specifies -exec option (#371)
## Link to GitHub Issue, if one exists
#345

## Description of change
If `-exec` is specified, log a giant warning.

Also, fix some compiler warnings in MFG.

## Testing
Confirmed message being logged in log.txt.
2025-09-17 17:03:57 -07:00
33 changed files with 668 additions and 98 deletions
+6 -1
View File
@@ -18,4 +18,9 @@ dist/*
external/cv2pdb/*
.ccache/*
.ccache/*
# Visual Studio
.vs
out
CMakeSettings.json
+19
View File
@@ -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)
+1 -1
View File
@@ -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)) {
+13 -3
View File
@@ -8,6 +8,7 @@
#include "external/robin_hood.h"
#include "launcher/logger.h"
#include "launcher/signal.h"
#include "util/detour.h"
#include "util/fileutils.h"
#include "util/libutils.h"
@@ -1736,7 +1737,13 @@ namespace avs {
return;
}
static void create_dir(
static void avs_dir_err(const std::filesystem::path &src_path)
{
launcher::signal::AVS_DIR_CREATION_FAILURE = TRUE;
launcher::signal::AVS_SRC_PATH = src_path.string();
}
static void create_avs_dir(
const std::string_view &avs_path,
const std::string_view &src_path)
{
@@ -1745,6 +1752,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 +1762,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 +1825,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 +1880,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)));
}
+6
View File
@@ -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";
@@ -412,6 +413,11 @@ namespace avs {
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 };
+1
View File
@@ -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;
+21
View File
@@ -1,3 +1,24 @@
09/30/2025 [spice2x]
Prevent verbose debug EA3 logging by default
Options UI tweaks
09/28/2025 [spice2x]
IIDX: auto apply ASIO compatibility fix for IIDX32+
Misc diagnosability updates
09/22/2025 [spice2x]
Add Polaris Chord lights
Add boot.dll to Gitadora patches target
Misc fixes
09/20/2025 [spice2x]
Polaris Chord support for knobs/turntables/mouse as fader input
Misc fixes
09/18/2025 [spice2x]
Polaris Chord windowed mode fixes
UI tweaks
09/17/2025 [spice2x]
Polaris Chord I/O fixes, language shim
+4
View File
@@ -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)>
+1 -1
View File
@@ -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;
+50 -2
View File
@@ -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"
@@ -27,6 +28,7 @@
#include "util/memutils.h"
#include "util/sigscan.h"
#include "util/utils.h"
#include "launcher/signal.h"
#include "external/robin_hood.h"
@@ -232,6 +234,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) {
launcher::signal::SUPERSTEP_SOUND_ERROR = TRUE;
return false;
} else {
return false;
}
@@ -372,7 +377,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(
@@ -762,26 +771,30 @@ 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)
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) {
// iidx 27-30
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");
// patch game to force wasapi
if (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);
@@ -796,6 +809,41 @@ namespace games::iidx {
"Successfully forced WASAPI as audio engine using signature matching @ 0x{:x}.",
result);
}
} else {
log_info("iidx", "Not applying force wasapi patch; game will use ASIO");
}
// 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
+38 -15
View File
@@ -85,12 +85,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 +105,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 +136,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 +157,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 +188,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 +607,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 +618,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 +642,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;
@@ -696,7 +719,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 +808,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);
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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)) {
+163 -4
View File
@@ -7,8 +7,68 @@
#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
@@ -270,7 +330,13 @@ namespace games::pc {
// FADER-L
float val = 0.f;
if (analogs[Analogs::FaderL].isSet()) {
val = (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderL]) - 0.5f) * 2;
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])) {
@@ -289,7 +355,13 @@ namespace games::pc {
// FADER-R
val = 0.f;
if (analogs[Analogs::FaderR].isSet()) {
val = (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderR]) - 0.5f) * 2;
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])) {
@@ -343,6 +415,19 @@ namespace games::pc {
}
}
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) {
@@ -350,8 +435,82 @@ namespace games::pc {
return aioIob2Bi2xAC1_SetTapeLedDataPart_orig(i_pNodeCtl, i_TapeLedCh, i_Offset, i_pData, i_cntTapeLed, i_bReverse);
}
// TODO implement tape led
// there are >200 lights in total, adding each one separately probably isn't the best idea...
// 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) {
+57 -2
View File
@@ -35,7 +35,14 @@ std::vector<Button> &games::pc::get_buttons() {
}
std::string games::pc::get_buttons_help() {
return "";
// 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() {
@@ -64,7 +71,55 @@ std::vector<Light> &games::pc::get_lights() {
&lights,
"IC Reader R",
"IC Reader G",
"IC Reader B"
"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"
);
}
+49
View File
@@ -41,6 +41,55 @@ namespace games::pc {
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;
}
+27 -1
View File
@@ -21,6 +21,8 @@ namespace games::pc {
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) {
@@ -38,6 +40,27 @@ namespace games::pc {
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();
@@ -63,7 +86,10 @@ namespace games::pc {
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);
+1
View File
@@ -5,6 +5,7 @@
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:
+5
View File
@@ -24,6 +24,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;
@@ -209,6 +210,10 @@ namespace games::sdvx {
out = "";
return true;
}
if (data.find("SuperstepSound: Audio device is not available") != std::string::npos) {
launcher::signal::SUPERSTEP_SOUND_ERROR = TRUE;
return false;
}
return false;
}
+4
View File
@@ -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");
+28 -1
View File
@@ -365,6 +365,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();
@@ -1112,6 +1115,9 @@ int main_implementation(int argc, char *argv[]) {
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;
}
@@ -1272,6 +1278,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)) {
@@ -1767,7 +1786,14 @@ 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_fatal(
"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"
);
}
break;
@@ -1944,6 +1970,7 @@ int main_implementation(int argc, char *argv[]) {
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();
+65 -38
View File
@@ -34,7 +34,7 @@ static const std::vector<std::string> CATEGORY_ORDER_ADVANCED = {
"NFC Card Readers",
};
static const std::vector<std::string> CATEGORY_ORDER_DEV = {
"Paths",
"Path Overrides",
"Network (Development)",
"Audio (Hacks)",
"I/O Modules",
@@ -53,12 +53,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",
@@ -357,7 +359,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.name = "r",
.desc = "Set custom SOFTID override",
.type = OptionType::Text,
.category = "Network (Development)",
.category = "Development",
.sensitive = true,
},
{
@@ -552,10 +554,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)",
},
@@ -1047,7 +1050,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,
@@ -1056,7 +1059,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,
@@ -1065,7 +1068,7 @@ 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,
@@ -1074,7 +1077,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
},
{
// LoadPCModule
.title = "Force Load Polaris Chord",
.title = "Force Load Polaris Chord Module",
.name = "pc",
.desc = "manually enable Polaris Chord module",
.type = OptionType::Bool,
@@ -1083,7 +1086,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
},
{
// LoadMusecaModule
.title = "Force Load Museca",
.title = "Force Load Museca Module",
.name = "museca",
.desc = "manually enable Museca module",
.type = OptionType::Bool,
@@ -1091,88 +1094,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",
@@ -1542,10 +1545,13 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.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",
@@ -1670,6 +1676,16 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.type = OptionType::Bool,
.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",
@@ -1804,6 +1820,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options (Advanced)"
},
{
// PCArgs
.title = "PC Arguments Override",
.name = "pcargs",
.desc = "Command line arguments passed to the game.",
@@ -1813,6 +1830,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options (Advanced)",
},
{
// PCNoIO
.title = "PC Disable IO Emulation",
.name = "pcnoio",
.desc = "Disables BI2X hooks for PC",
@@ -1821,6 +1839,15 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.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",
@@ -2362,7 +2389,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) {
@@ -2443,7 +2470,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]);
}
+2
View File
@@ -188,6 +188,7 @@ namespace launcher {
DebugCreateFile,
VerboseGraphicsLogging,
VerboseAVSLogging,
AllowEA3Verbose,
DisableColoredOutput,
DisableACPHook,
DisableSignalHandling,
@@ -205,6 +206,7 @@ namespace launcher {
MFGNoIO,
PCArgs,
PCNoIO,
PCKnobMode,
spice2x_LightsOverallBrightness,
spice2x_WindowBorder,
spice2x_WindowSize,
+42 -1
View File
@@ -1,11 +1,12 @@
#include "signal.h"
#include <exception>
#include <string>
#include <windows.h>
#include <dbghelp.h>
#include "avs/core.h"
#include "avs/game.h"
#include "acio/acio.h"
#include "external/stackwalker/stackwalker.h"
#include "hooks/libraryhook.h"
@@ -29,6 +30,11 @@ namespace launcher::signal {
// settings
bool DISABLE = false;
bool USE_VEH_WORKAROUND = false;
// states
bool SUPERSTEP_SOUND_ERROR = false;
bool AVS_DIR_CREATION_FAILURE = false;
std::string AVS_SRC_PATH;
}
#define V(variant) case variant: return #variant
@@ -98,6 +104,22 @@ static LONG WINAPI TopLevelExceptionFilter(struct _EXCEPTION_POINTERS *Exception
// print signal
log_warning("signal", "exception raised: {}", exception_code(ExceptionRecord));
std::string err;
switch (ExceptionRecord->ExceptionCode) {
case EXCEPTION_ILLEGAL_INSTRUCTION:
err = "Illegal instruction: 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;
}
if (!err.empty()) {
log_warning(
"signal",
"likely cause for your error based on the exception code:\n {}",
err.c_str());
}
// check ACIO init failures
if (acio::IO_INIT_IN_PROGRESS) {
log_warning(
@@ -111,6 +133,25 @@ static LONG WINAPI TopLevelExceptionFilter(struct _EXCEPTION_POINTERS *Exception
" RivaTuner Statistics Server (RTSS), MSI Afterburner, kernel mode anti-cheat");
}
if (launcher::signal::SUPERSTEP_SOUND_ERROR) {
log_warning("signal", "audio initialization error was previously detected during boot!");
log_warning("signal", " (W:SuperstepSound: Audio device is not available!!!)");
log_warning("signal", " this crash is most likely related to audio init failure");
log_warning("signal", " fix your audio device, double check your audio options/patches, and try again");
}
if (launcher::signal::AVS_DIR_CREATION_FAILURE) {
log_warning("signal",
"AVS filesystem initialization failure was previously detected during boot!");
log_warning("signal",
" ERROR: directory could not be created: {}",
launcher::signal::AVS_SRC_PATH.c_str());
log_warning("signal",
" this crash may have been caused by bad <mounttable> contents in {}",
avs::core::CFG_PATH.c_str());
log_warning("signal", " fix the XML file and try again");
}
// walk the exception chain
struct _EXCEPTION_RECORD *record_cause = ExceptionRecord->ExceptionRecord;
while (record_cause != nullptr) {
+6
View File
@@ -1,10 +1,16 @@
#pragma once
#include <string>
namespace launcher::signal {
// settings
extern bool DISABLE;
extern bool USE_VEH_WORKAROUND;
extern bool SUPERSTEP_SOUND_ERROR;
extern bool AVS_DIR_CREATION_FAILURE;
extern std::string AVS_XML_PATH;
extern std::string AVS_SRC_PATH;
//void print_stacktrace();
void attach();
+1 -1
View File
@@ -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) ||
+6 -7
View File
@@ -2633,17 +2633,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;
}
}
@@ -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());
+1 -1
View File
@@ -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;
}
+13 -3
View File
@@ -80,7 +80,7 @@ namespace overlay::windows {
{"arkmmd.dll", {"gamemmd.dll"}},
{"arkklp.dll", {"lpac.dll"}},
{"arknck.dll", {"weac.dll"}},
{"gdxg.dll", {"game.dll", "libshare-pj.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");
+4
View File
@@ -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) {
+11 -13
View File
@@ -29,19 +29,17 @@ 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);
+1 -1
View File
@@ -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));