Compare commits

...

14 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
19 changed files with 405 additions and 53 deletions
+13 -3
View File
@@ -8,6 +8,7 @@
#include "external/robin_hood.h" #include "external/robin_hood.h"
#include "launcher/logger.h" #include "launcher/logger.h"
#include "launcher/signal.h"
#include "util/detour.h" #include "util/detour.h"
#include "util/fileutils.h" #include "util/fileutils.h"
#include "util/libutils.h" #include "util/libutils.h"
@@ -1736,7 +1737,13 @@ namespace avs {
return; 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 &avs_path,
const std::string_view &src_path) const std::string_view &src_path)
{ {
@@ -1745,6 +1752,7 @@ namespace avs {
auto real_path = std::filesystem::absolute(src_path, err); auto real_path = std::filesystem::absolute(src_path, err);
if (err) { if (err) {
avs_dir_err(real_path);
log_warning("avs-core", "failed to resolve '{}' path: {}", avs_path, err.message()); log_warning("avs-core", "failed to resolve '{}' path: {}", avs_path, err.message());
return; return;
} }
@@ -1754,7 +1762,9 @@ namespace avs {
if (created) { if (created) {
log_info("avs-core", "created '{}' at '{}'", avs_path, real_path.string()); log_info("avs-core", "created '{}' at '{}'", avs_path, real_path.string());
} }
if (err) { if (err) {
avs_dir_err(real_path);
log_warning("avs-core", "failed to create '{}' folder at '{}': {}", log_warning("avs-core", "failed to create '{}' folder at '{}': {}",
avs_path, avs_path,
real_path.string(), real_path.string(),
@@ -1815,7 +1825,7 @@ namespace avs {
return; return;
} }
create_dir(avs_path, device_path); create_avs_dir(avs_path, device_path);
} }
static void create_avs_config_fs_table( static void create_avs_config_fs_table(
@@ -1870,7 +1880,7 @@ namespace avs {
continue; 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))); } 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 HTTP11 = -1;
int URL_SLASH = -1; int URL_SLASH = -1;
int PCB_TYPE = -1; int PCB_TYPE = -1;
bool EA3_DEBUG_VERBOSE = false;
// handle // handle
std::string VERSION_STR = "unknown"; std::string VERSION_STR = "unknown";
@@ -412,6 +413,11 @@ namespace avs {
log_fatal("avs-ea3", "no PCBID set (try using -p to specify PCBID)"); 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 // remember IDs
char pcbid_buffer[256] { 0 }; char pcbid_buffer[256] { 0 };
char accountid_buffer[256] { 0 }; char accountid_buffer[256] { 0 };
+1
View File
@@ -28,6 +28,7 @@ namespace avs {
extern int HTTP11; extern int HTTP11;
extern int URL_SLASH; extern int URL_SLASH;
extern int PCB_TYPE; extern int PCB_TYPE;
extern bool EA3_DEBUG_VERBOSE;
// handle // handle
extern std::string VERSION_STR; extern std::string VERSION_STR;
+13
View File
@@ -1,3 +1,16 @@
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] 09/20/2025 [spice2x]
Polaris Chord support for knobs/turntables/mouse as fader input Polaris Chord support for knobs/turntables/mouse as fader input
Misc fixes Misc fixes
+45 -1
View File
@@ -8,6 +8,7 @@
#include "games/iidx/legacy_camera.h" #include "games/iidx/legacy_camera.h"
#include "hooks/audio/audio.h"
#include "hooks/avshook.h" #include "hooks/avshook.h"
#include "hooks/cfgmgr32hook.h" #include "hooks/cfgmgr32hook.h"
#include "hooks/devicehook.h" #include "hooks/devicehook.h"
@@ -376,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 // ASIO device hook
RegCloseKey_orig = detour::iat_try( RegCloseKey_orig = detour::iat_try(
@@ -766,22 +771,26 @@ namespace games::iidx {
0); 0);
// attempt to detect ASIO support // attempt to detect ASIO support
// <=24 : 32-bit only
// 25-26: has neither (no patch needed - WASAPI Exclusive by default) // 25-26: has neither (no patch needed - WASAPI Exclusive by default)
// 27-30: has both (envvar will be respected, ASIO or WASAPI) // 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+: 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) { if (!has_SOUND_OUTPUT_DEVICE && !has_XONAR_SOUND_CARD) {
// iidx 25-26
log_info("iidx", "This game only uses WASAPI audio engine"); log_info("iidx", "This game only uses WASAPI audio engine");
return; return;
} }
if (has_SOUND_OUTPUT_DEVICE && has_XONAR_SOUND_CARD) { if (has_SOUND_OUTPUT_DEVICE && has_XONAR_SOUND_CARD) {
// iidx 27-30
log_info("iidx", "This game accepts SOUND_OUTPUT_DEVICE environment variable"); log_info("iidx", "This game accepts SOUND_OUTPUT_DEVICE environment variable");
return; return;
} }
log_info("iidx", "This game supports ASIO but does not accept SOUND_OUTPUT_DEVICE environment variable"); 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") { if (SOUND_OUTPUT_DEVICE_IN_EFFECT.has_value() && SOUND_OUTPUT_DEVICE_IN_EFFECT.value() == "wasapi") {
intptr_t result = replace_pattern( intptr_t result = replace_pattern(
avs::game::DLL_INSTANCE, avs::game::DLL_INSTANCE,
@@ -800,6 +809,41 @@ namespace games::iidx {
"Successfully forced WASAPI as audio engine using signature matching @ 0x{:x}.", "Successfully forced WASAPI as audio engine using signature matching @ 0x{:x}.",
result); 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 #endif
+38 -15
View File
@@ -85,12 +85,19 @@ namespace games::iidx {
&m_pwszSymbolicLink, &m_pwszSymbolicLink,
&m_cchSymbolicLink &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()); log_misc("iidx:camhook", "[{}] Symlink: {}", m_name, GetSymLink());
// Create the media source object. // Create the media source object.
hr = pActivate->ActivateObject(IID_PPV_ARGS(&m_pSource)); 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 // Retain reference to the camera
m_pSource->AddRef(); m_pSource->AddRef();
@@ -98,13 +105,22 @@ namespace games::iidx {
// Create an attribute store to hold initialization settings. // Create an attribute store to hold initialization settings.
hr = WrappedMFCreateAttributes(&pAttributes, 2); 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); 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); 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 // TODO: Color space conversion
// if (SUCCEEDED(hr)) { // if (SUCCEEDED(hr)) {
@@ -120,11 +136,18 @@ namespace games::iidx {
pAttributes, pAttributes,
&m_pSourceReader &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); log_misc("iidx:camhook", "[{}] Created source reader", m_name);
hr = InitTargetTexture(); 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 // Camera should be still usable even if camera control is not supported
InitCameraControl(); InitCameraControl();
@@ -134,7 +157,7 @@ namespace games::iidx {
m_initialized = true; m_initialized = true;
log_misc("iidx:camhook", "[{}] Initialized", m_name); log_misc("iidx:camhook", "[{}] Initialized", m_name);
} else { } 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); SafeRelease(&pAttributes);
LeaveCriticalSection(&m_critsec); LeaveCriticalSection(&m_critsec);
@@ -165,7 +188,7 @@ namespace games::iidx {
if (FAILED(hr)) { if (FAILED(hr)) {
if (hr != MF_E_NO_MORE_TYPES) { 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; break;
} }
@@ -584,7 +607,7 @@ namespace games::iidx {
* Return values: * Return values:
* S_OK: this is a "better" media type than the existing one * S_OK: this is a "better" media type than the existing one
* S_FALSE: valid media type, but not "better" * 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 IIDXLocalCamera::TryMediaType(IMFMediaType *pType, UINT32 *pBestWidth, double *pBestFrameRate) {
HRESULT hr = S_OK; HRESULT hr = S_OK;
@@ -595,13 +618,13 @@ namespace games::iidx {
hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype); hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype);
if (FAILED(hr)) { 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; return hr;
} }
hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height); hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height);
if (FAILED(hr)) { 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; return hr;
} }
@@ -619,7 +642,7 @@ namespace games::iidx {
(UINT32*)&frameRate.Denominator (UINT32*)&frameRate.Denominator
); );
if (FAILED(hr)) { 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; return hr;
} }
double frameRateValue = frameRate.Numerator / frameRate.Denominator; double frameRateValue = frameRate.Numerator / frameRate.Denominator;
@@ -696,7 +719,7 @@ namespace games::iidx {
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
log_misc("iidx:camhook", "[{}] Created texture", m_name); log_misc("iidx:camhook", "[{}] Created texture", m_name);
} else { } 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; return hr;
} }
@@ -785,7 +808,7 @@ namespace games::iidx {
FlushDrawCommands(); FlushDrawCommands();
if (FAILED(hr)) { if (FAILED(hr)) {
log_warning("iidx:camhook", "Error in DrawSample {}", hr); log_warning("iidx:camhook", "Error in DrawSample {:#x}", (ULONG)hr);
} }
SafeRelease(&pCameraSurf); SafeRelease(&pCameraSurf);
LeaveCriticalSection(&m_critsec); LeaveCriticalSection(&m_critsec);
+89 -2
View File
@@ -415,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, 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_Offset, uint8_t *i_pData,
uint32_t i_cntTapeLed, bool i_bReverse) { uint32_t i_cntTapeLed, bool i_bReverse) {
@@ -422,8 +435,82 @@ namespace games::pc {
return aioIob2Bi2xAC1_SetTapeLedDataPart_orig(i_pNodeCtl, i_TapeLedCh, i_Offset, i_pData, i_cntTapeLed, i_bReverse); return aioIob2Bi2xAC1_SetTapeLedDataPart_orig(i_pNodeCtl, i_TapeLedCh, i_Offset, i_pData, i_cntTapeLed, i_bReverse);
} }
// TODO implement tape led // log_info(
// there are >200 lights in total, adding each one separately probably isn't the best idea... // "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) { void __fastcall aioIob2Bi2x_SetTapeLedDataGroup(AIO_IOB2_BI2X_AC1* i_pNodeCtl, uint32_t i_bfGroup) {
+49 -1
View File
@@ -71,7 +71,55 @@ std::vector<Light> &games::pc::get_lights() {
&lights, &lights,
"IC Reader R", "IC Reader R",
"IC Reader G", "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_R,
IC_READER_G, IC_READER_G,
IC_READER_B, 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; } pc_lights_t;
} }
+3
View File
@@ -365,6 +365,9 @@ int main_implementation(int argc, char *argv[]) {
if (options[launcher::Options::VerboseAVSLogging].value_bool()) { if (options[launcher::Options::VerboseAVSLogging].value_bool()) {
hooks::avs::config::LOG = true; 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()) { if (options[launcher::Options::spice2x_AutoOrientation].is_active()) {
GRAPHICS_ADJUST_ORIENTATION = GRAPHICS_ADJUST_ORIENTATION =
(graphics_orientation)options[launcher::Options::spice2x_AutoOrientation].value_uint32(); (graphics_orientation)options[launcher::Options::spice2x_AutoOrientation].value_uint32();
+22 -8
View File
@@ -359,7 +359,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.name = "r", .name = "r",
.desc = "Set custom SOFTID override", .desc = "Set custom SOFTID override",
.type = OptionType::Text, .type = OptionType::Text,
.category = "Network (Development)", .category = "Development",
.sensitive = true, .sensitive = true,
}, },
{ {
@@ -554,10 +554,11 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options", .category = "Game Options",
}, },
{ {
.title = "IIDX BIO2 Firmware", .title = "IIDX BIO2 Firmware Update",
.name = "iidxbio2fw", .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, .type = OptionType::Bool,
.hidden = true,
.game_name = "Beatmania IIDX", .game_name = "Beatmania IIDX",
.category = "Game Options (Advanced)", .category = "Game Options (Advanced)",
}, },
@@ -1135,11 +1136,11 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Path Overrides", .category = "Path Overrides",
}, },
{ {
.title = "Intel SDE Folder", .title = "Intel SDE",
.name = "sde", .name = "sde",
.desc = "Path to Intel SDE kit path for automatic attaching", .desc = "Path to Intel SDE kit path for automatic attaching",
.type = OptionType::Text, .type = OptionType::Text,
.category = "Path Overrides", .category = "Development",
}, },
{ {
.title = "ea3-config.xml Override", .title = "ea3-config.xml Override",
@@ -1544,10 +1545,13 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.title = "Spice Audio Hook Backend", .title = "Spice Audio Hook Backend",
.name = "audiobackend", .name = "audiobackend",
.desc = "Selects the audio backend to use when spice audio hook is enabled, overriding exclusive WASAPI. " .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, .type = OptionType::Enum,
.category = "Audio", .category = "Audio",
.elements = {{"asio", "ASIO"}, {"waveout", "waveOut"}}, .elements = {
{"asio", "ASIO"},
{"waveout", "broken, do not use"}
},
}, },
{ {
.title = "Spice Audio Hook ASIO Driver ID", .title = "Spice Audio Hook ASIO Driver ID",
@@ -1672,6 +1676,16 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.type = OptionType::Bool, .type = OptionType::Bool,
.category = "Debug Log", .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", .title = "Disable Colored Output",
.name = "nocolor", .name = "nocolor",
@@ -2375,7 +2389,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.type = OptionType::Bool, .type = OptionType::Bool,
.category = "Development", .category = "Development",
.disabled = true, .disabled = true,
}, }
}; };
const std::vector<std::string> &launcher::get_categories(Options::OptionsCategory category) { const std::vector<std::string> &launcher::get_categories(Options::OptionsCategory category) {
+1
View File
@@ -188,6 +188,7 @@ namespace launcher {
DebugCreateFile, DebugCreateFile,
VerboseGraphicsLogging, VerboseGraphicsLogging,
VerboseAVSLogging, VerboseAVSLogging,
AllowEA3Verbose,
DisableColoredOutput, DisableColoredOutput,
DisableACPHook, DisableACPHook,
DisableSignalHandling, DisableSignalHandling,
+32 -1
View File
@@ -1,11 +1,12 @@
#include "signal.h" #include "signal.h"
#include <exception> #include <exception>
#include <string>
#include <windows.h> #include <windows.h>
#include <dbghelp.h> #include <dbghelp.h>
#include "avs/core.h"
#include "avs/game.h"
#include "acio/acio.h" #include "acio/acio.h"
#include "external/stackwalker/stackwalker.h" #include "external/stackwalker/stackwalker.h"
#include "hooks/libraryhook.h" #include "hooks/libraryhook.h"
@@ -32,6 +33,8 @@ namespace launcher::signal {
// states // states
bool SUPERSTEP_SOUND_ERROR = false; bool SUPERSTEP_SOUND_ERROR = false;
bool AVS_DIR_CREATION_FAILURE = false;
std::string AVS_SRC_PATH;
} }
#define V(variant) case variant: return #variant #define V(variant) case variant: return #variant
@@ -101,6 +104,22 @@ static LONG WINAPI TopLevelExceptionFilter(struct _EXCEPTION_POINTERS *Exception
// print signal // print signal
log_warning("signal", "exception raised: {}", exception_code(ExceptionRecord)); 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 // check ACIO init failures
if (acio::IO_INIT_IN_PROGRESS) { if (acio::IO_INIT_IN_PROGRESS) {
log_warning( log_warning(
@@ -121,6 +140,18 @@ static LONG WINAPI TopLevelExceptionFilter(struct _EXCEPTION_POINTERS *Exception
log_warning("signal", " fix your audio device, double check your audio options/patches, and try again"); 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 // walk the exception chain
struct _EXCEPTION_RECORD *record_cause = ExceptionRecord->ExceptionRecord; struct _EXCEPTION_RECORD *record_cause = ExceptionRecord->ExceptionRecord;
while (record_cause != nullptr) { while (record_cause != nullptr) {
+5
View File
@@ -1,11 +1,16 @@
#pragma once #pragma once
#include <string>
namespace launcher::signal { namespace launcher::signal {
// settings // settings
extern bool DISABLE; extern bool DISABLE;
extern bool USE_VEH_WORKAROUND; extern bool USE_VEH_WORKAROUND;
extern bool SUPERSTEP_SOUND_ERROR; 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 print_stacktrace();
void attach(); void attach();
+5 -6
View File
@@ -2633,17 +2633,16 @@ namespace overlay::windows {
// check hidden option // check hidden option
if (!this->options_show_hidden && option.value.empty()) { if (!this->options_show_hidden && option.value.empty()) {
// skip hidden entries // skip hidden entries
if (definition.hidden) { if (definition.hidden) {
continue; continue;
} }
}
// check for game exclusivity // check for game exclusivity
if (!definition.game_name.empty()) { if (!definition.game_name.empty()) {
if (definition.game_name != this->games_selected_name) { if (definition.game_name != this->games_selected_name) {
continue; continue;
}
} }
} }
@@ -4,6 +4,7 @@
#include <fmt/format.h> #include <fmt/format.h>
#include "avs/game.h"
#include "games/io.h" #include "games/io.h"
#include "cfg/screen_resize.h" #include "cfg/screen_resize.h"
#include "hooks/graphics/backends/d3d9/d3d9_backend.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::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {}
void GenericSubScreen::build_content() { void GenericSubScreen::build_content() {
this->flags |= ImGuiWindowFlags_NoBackground;
if (this->disabled_message.has_value()) { if (this->disabled_message.has_value()) {
this->flags &= ~ImGuiWindowFlags_NoBackground; this->flags &= ~ImGuiWindowFlags_NoBackground;
ImGui::TextColored(YELLOW, "%s", this->disabled_message.value().c_str()); ImGui::TextColored(YELLOW, "%s", this->disabled_message.value().c_str());
return; return;
} }
this->draw_texture(); 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 OVERLAYDBG
if (this->status_message.has_value()) { if (this->status_message.has_value()) {
log_warning("sub::overlay", "{}", this->status_message.value().c_str()); 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) { if (GRAPHICS_IIDX_WSUB) {
this->disabled_message = this->disabled_message =
"Close this overlay and use the second window.\n" "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; this->draws_window = false;
} }
@@ -80,7 +80,7 @@ namespace overlay::windows {
{"arkmmd.dll", {"gamemmd.dll"}}, {"arkmmd.dll", {"gamemmd.dll"}},
{"arkklp.dll", {"lpac.dll"}}, {"arkklp.dll", {"lpac.dll"}},
{"arknck.dll", {"weac.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; static size_t url_recent_idx = -1;
+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) { static inline void load_library_fail(const std::string &file_name, bool fatal) {
std::string info_str { fmt::format( std::string info_str { fmt::format(
"\n\nPlease check if {} exists and the permissions are fine.\n" "DLL failed to load - this is a common error. Please carefully read ALL of the following steps for a fix:\n"
"\n" " 1. Confirm if the file ({}) exists on the disk and check the file permissions.\n"
"* If the problem still persists, try installing things on this list:\n" " 2. Follow this link and install DLL prerequisites on this list:\n"
" https://github.com/spice2x/spice2x.github.io/wiki/DLL-Dependencies \n" " https://github.com/spice2x/spice2x.github.io/wiki/DLL-Dependencies \n"
"\n" " 3. Still have problems after installing from above and rebooting PC?\n"
"* 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"
" 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"
" 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"
" 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"
"\n" " a. https://github.com/lucasg/Dependencies (recommended for most) \n"
"* (For advanced users) if none of the above helps, find the missing dependency using:\n" " b. http://www.dependencywalker.com/ (for old OS) \n"
" https://github.com/lucasg/Dependencies (recommended for most) \n"
" http://www.dependencywalker.com/ (for old OS) \n"
, file_name) }; , file_name) };
if (fatal) { if (fatal) {
log_fatal("libutils", "{}", info_str); log_fatal("libutils", "{}", info_str);