Update to spice2x-25-04-25 (pre-apply)
> broken commit
This commit is contained in:
+151
-236
@@ -1,26 +1,20 @@
|
||||
// set version to Windows 7 to enable Media Foundation functions
|
||||
#ifdef _WIN32_WINNT
|
||||
#undef _WIN32_WINNT
|
||||
#endif
|
||||
#define _WIN32_WINNT 0x0601
|
||||
|
||||
// turn on C-style COM objects
|
||||
#define CINTERFACE
|
||||
|
||||
#include "iidx.h"
|
||||
|
||||
#include <mfobjects.h>
|
||||
#include <mfidl.h>
|
||||
|
||||
#include "acioemu/handle.h"
|
||||
#include "avs/core.h"
|
||||
#include "avs/game.h"
|
||||
#include "cfg/configurator.h"
|
||||
#include "games/io.h"
|
||||
|
||||
#include "games/iidx/legacy_camera.h"
|
||||
|
||||
#include "hooks/avshook.h"
|
||||
#include "hooks/cfgmgr32hook.h"
|
||||
#include "hooks/devicehook.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#ifdef SPICE64
|
||||
#include "hooks/graphics/nvenc_hook.h"
|
||||
#endif
|
||||
#include "hooks/setupapihook.h"
|
||||
#include "hooks/sleephook.h"
|
||||
#include "launcher/options.h"
|
||||
@@ -31,6 +25,7 @@
|
||||
#include "util/fileutils.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/memutils.h"
|
||||
#include "util/sigscan.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
#include "bi2a.h"
|
||||
@@ -38,9 +33,6 @@
|
||||
#include "ezusb.h"
|
||||
#include "io.h"
|
||||
|
||||
static VTBL_TYPE(IMFActivate, GetAllocatedString) GetAllocatedString_orig = nullptr;
|
||||
|
||||
static decltype(MFEnumDeviceSources) *MFEnumDeviceSources_orig = nullptr;
|
||||
static decltype(RegCloseKey) *RegCloseKey_orig = nullptr;
|
||||
static decltype(RegEnumKeyA) *RegEnumKeyA_orig = nullptr;
|
||||
static decltype(RegOpenKeyA) *RegOpenKeyA_orig = nullptr;
|
||||
@@ -57,12 +49,19 @@ namespace games::iidx {
|
||||
// settings
|
||||
bool FLIP_CAMS = false;
|
||||
bool DISABLE_CAMS = false;
|
||||
bool TDJ_CAMERA = false;
|
||||
bool TDJ_CAMERA_PREFER_16_9 = true;
|
||||
bool TDJ_MODE = false;
|
||||
bool FORCE_720P = false;
|
||||
bool DISABLE_ESPEC_IO = false;
|
||||
bool NATIVE_TOUCH = false;
|
||||
std::optional<std::string> SOUND_OUTPUT_DEVICE = std::nullopt;
|
||||
std::optional<std::string> SOUND_OUTPUT_DEVICE_IN_EFFECT = std::nullopt;
|
||||
std::optional<std::string> ASIO_DRIVER = std::nullopt;
|
||||
uint8_t DIGITAL_TT_SENS = 4;
|
||||
std::optional<std::string> SUBSCREEN_OVERLAY_SIZE = std::nullopt;
|
||||
std::optional<std::string> SCREEN_MODE = std::nullopt;
|
||||
std::optional<std::string> TDJ_CAMERA_OVERRIDE = std::nullopt;
|
||||
|
||||
// states
|
||||
static HKEY real_asio_reg_handle = nullptr;
|
||||
@@ -101,9 +100,25 @@ namespace games::iidx {
|
||||
*phkResult = DEVICE_ASIO_REG_HANDLE;
|
||||
|
||||
log_info("iidx::asio", "replacing '{}' with '{}'", lpSubKey, ASIO_DRIVER.value());
|
||||
|
||||
return RegOpenKeyExA_orig(real_asio_reg_handle, ASIO_DRIVER.value().c_str(), ulOptions, samDesired,
|
||||
const auto result = RegOpenKeyExA_orig(
|
||||
real_asio_reg_handle,
|
||||
ASIO_DRIVER.value().c_str(),
|
||||
ulOptions,
|
||||
samDesired,
|
||||
&real_asio_device_reg_handle);
|
||||
|
||||
if (result != ERROR_SUCCESS) {
|
||||
log_warning(
|
||||
"iidx::asio",
|
||||
"failed to open registry subkey '{}', error=0x{:x}",
|
||||
ASIO_DRIVER.value().c_str(), result);
|
||||
log_warning(
|
||||
"iidx::asio",
|
||||
"due to improper ASIO setting, game will likely fail to launch; double check -iidxasio and the registry",
|
||||
ASIO_DRIVER.value().c_str(), result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,20 +145,20 @@ namespace games::iidx {
|
||||
return RegEnumKeyA_orig(hKey, dwIndex, lpName, cchName);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dirty Workaround for IO Device
|
||||
* Game gets registry object via SetupDiOpenDevRegKey, then looks up "PortName" via RegQueryValueExA
|
||||
* We ignore the first and just cheat on the second.
|
||||
*/
|
||||
static LONG WINAPI RegQueryValueExA_hook(HKEY hKey, LPCTSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType,
|
||||
LPBYTE lpData, LPDWORD lpcbData)
|
||||
{
|
||||
if (lpValueName != nullptr && lpData != nullptr && lpcbData != nullptr) {
|
||||
|
||||
// ASIO hack
|
||||
if (hKey == DEVICE_ASIO_REG_HANDLE && ASIO_DRIVER.has_value()) {
|
||||
log_info("iidx::asio", "RegQueryValueExA({}, \"{}\")", fmt::ptr((void *) hKey), lpValueName);
|
||||
|
||||
if (_stricmp(lpValueName, "Description") == 0) {
|
||||
memcpy(lpData, ASIO_DRIVER.value().c_str(), ASIO_DRIVER.value().size() + 1);
|
||||
// newer iidx does a comparison against hardcoded string "XONAR SOUND CARD(64)" (same as sdvx)
|
||||
// so what's in the registry must be overridden with "XONAR SOUND CARD(64)"
|
||||
// otherwise you end up with this error: M:BMSoundLib: ASIODriver: No such driver
|
||||
memcpy(lpData, ORIGINAL_ASIO_DEVICE_NAME, strlen(ORIGINAL_ASIO_DEVICE_NAME) + 1);
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
} else {
|
||||
@@ -151,6 +166,11 @@ namespace games::iidx {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Dirty Workaround for IO Device
|
||||
* Game gets registry object via SetupDiOpenDevRegKey, then looks up "PortName" via RegQueryValueExA
|
||||
* We ignore the first and just cheat on the second.
|
||||
*/
|
||||
// check for port name lookup
|
||||
if (_stricmp(lpValueName, "PortName") == 0) {
|
||||
const char port[] = "COM2";
|
||||
@@ -172,195 +192,24 @@ namespace games::iidx {
|
||||
return RegCloseKey_orig(hKey);
|
||||
}
|
||||
|
||||
/*
|
||||
* Camera related stuff
|
||||
*/
|
||||
static std::wstring CAMERA0_ID;
|
||||
static std::wstring CAMERA1_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);
|
||||
|
||||
// log the cam name
|
||||
log_misc("iidx::cam", "obtained {}", ws2s(*ppwszValue));
|
||||
|
||||
// try first camera
|
||||
wchar_t *pwc = nullptr;
|
||||
if (CAMERA0_ID.length() == 23) {
|
||||
pwc = wcsstr(*ppwszValue, CAMERA0_ID.c_str());
|
||||
}
|
||||
|
||||
// try second camera if first wasn't found
|
||||
if (!pwc && CAMERA1_ID.length() == 23) {
|
||||
pwc = wcsstr(*ppwszValue, CAMERA1_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';
|
||||
|
||||
log_misc("iidx::cam", "replaced {}", ws2s(*ppwszValue));
|
||||
}
|
||||
|
||||
// return original result
|
||||
return result;
|
||||
}
|
||||
|
||||
static void hook_camera_vtable(IMFActivate *camera) {
|
||||
|
||||
// hook allocated string method for camera identification
|
||||
memutils::VProtectGuard camera_guard(camera->lpVtbl);
|
||||
camera->lpVtbl->GetAllocatedString = GetAllocatedString_hook;
|
||||
}
|
||||
|
||||
static void hook_camera(size_t no, std::wstring camera_id, std::string camera_instance) {
|
||||
|
||||
// logic based on camera no
|
||||
if (no == 0) {
|
||||
|
||||
// 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);
|
||||
|
||||
log_info("iidx::cam", "hooked camera 1 @ {}", ws2s(camera_id));
|
||||
}
|
||||
else if (no == 1) {
|
||||
|
||||
// don't hook if camera 1 is already hooked
|
||||
if (CAMERA1_ID.length() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// save the camera ID
|
||||
CAMERA1_ID = camera_id;
|
||||
|
||||
// cfgmgr hook
|
||||
CFGMGR32_HOOK_SETTING camera_setting;
|
||||
camera_setting.device_instance = 0xBEEFDEAD;
|
||||
camera_setting.parent_instance = ~camera_setting.device_instance;
|
||||
camera_setting.device_id = "USB\\VEN_1022&DEV_7914";
|
||||
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);
|
||||
|
||||
log_info("iidx::cam", "hooked camera 2 @ {}", ws2s(camera_id));
|
||||
}
|
||||
}
|
||||
|
||||
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 < 2; cam_num++) {
|
||||
|
||||
// flip
|
||||
size_t cam_num_flipped = cam_num;
|
||||
if (FLIP_CAMS) {
|
||||
cam_num_flipped = *pcSourceActivate - cam_num - 1;
|
||||
}
|
||||
|
||||
// get camera
|
||||
IMFActivate *camera = (*pppSourceActivate)[cam_num_flipped];
|
||||
|
||||
// save original method for later use
|
||||
if (GetAllocatedString_orig == nullptr) {
|
||||
GetAllocatedString_orig = camera->lpVtbl->GetAllocatedString;
|
||||
}
|
||||
|
||||
// hook allocated string method for camera identification
|
||||
hook_camera_vtable(camera);
|
||||
|
||||
// get camera link
|
||||
LPWSTR camera_link_lpwstr;
|
||||
UINT32 camera_link_length;
|
||||
if (SUCCEEDED(GetAllocatedString_orig(
|
||||
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);
|
||||
|
||||
log_info("iidx::cam", "found video capture device: {}", ws2s(camera_link_ws));
|
||||
|
||||
// only support cameras that start with \\?\usb
|
||||
if (!string_begins_with(camera_link_ws, L"\\\\?\\usb")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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(cam_hook_num, camera_id, camera_instance);
|
||||
|
||||
// increase camera hook number
|
||||
cam_hook_num++;
|
||||
|
||||
} else {
|
||||
log_warning("iidx::cam", "failed to open camera {}", cam_num_flipped);
|
||||
}
|
||||
}
|
||||
|
||||
// return result
|
||||
return result_orig;
|
||||
}
|
||||
|
||||
static bool log_hook(void *user, const std::string &data, logger::Style style, std::string &out) {
|
||||
if (data.empty() || data[0] != '[') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// get rid of the log spam
|
||||
if (data.find(" I:graphic: adapter mode ") != std::string::npos) {
|
||||
out.clear();
|
||||
return true;
|
||||
} else if (data.find(" W:afputils: CDirectX::SetRenderState ") != std::string::npos) {
|
||||
out.clear();
|
||||
return true;
|
||||
} else if (data.find("\" layer ID 0 is not layer ID.") != std::string::npos) {
|
||||
out.clear();
|
||||
return true;
|
||||
} else if (data.find(" W:touch: missing trigger:") != std::string::npos) {
|
||||
out.clear();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -394,7 +243,6 @@ namespace games::iidx {
|
||||
|
||||
void IIDXGame::attach() {
|
||||
Game::attach();
|
||||
|
||||
// IO boards
|
||||
auto options = games::get_options(eamuse_get_game());
|
||||
if (!options->at(launcher::Options::IIDXBIO2FW).value_bool()) {
|
||||
@@ -440,20 +288,24 @@ namespace games::iidx {
|
||||
if (aio != nullptr) {
|
||||
|
||||
// check TDJ mode
|
||||
TDJ_MODE |= fileutils::text_read("C:\\000rom.txt") == "TDJ-JA";
|
||||
TDJ_MODE |= fileutils::text_read("D:\\001rom.txt") == "TDJ";
|
||||
|
||||
// force TDJ mode
|
||||
if (TDJ_MODE) {
|
||||
|
||||
// ensure game starts in the desired mode
|
||||
hooks::avs::set_rom("/c_drv/000rom.txt", "TDJ-JA");
|
||||
hooks::avs::set_rom("/d_drv/001rom.txt", "TDJ");
|
||||
|
||||
// need to hook `avs2-core.dll` so AVS win32fs operations go through rom hook
|
||||
devicehook_init(avs::core::DLL_INSTANCE);
|
||||
|
||||
wintouchemu::FORCE = true;
|
||||
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
|
||||
wintouchemu::hook_title_ends("beatmania IIDX", "main", avs::game::DLL_INSTANCE);
|
||||
if (!NATIVE_TOUCH) {
|
||||
wintouchemu::FORCE = true;
|
||||
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")) {
|
||||
@@ -498,6 +350,8 @@ namespace games::iidx {
|
||||
}
|
||||
}
|
||||
|
||||
apply_audio_hacks();
|
||||
|
||||
// ASIO device hook
|
||||
RegCloseKey_orig = detour::iat_try(
|
||||
"RegCloseKey", RegCloseKey_hook, avs::game::DLL_INSTANCE);
|
||||
@@ -514,22 +368,7 @@ namespace games::iidx {
|
||||
|
||||
// check if cam hook should be enabled
|
||||
if (!DISABLE_CAMS) {
|
||||
|
||||
// camera media framework hook
|
||||
MFEnumDeviceSources_orig = detour::iat_try(
|
||||
"MFEnumDeviceSources", MFEnumDeviceSources_hook, avs::game::DLL_INSTANCE);
|
||||
|
||||
// camera settings
|
||||
SETUPAPI_SETTINGS settings3 {};
|
||||
settings3.class_guid[0] = 0x00000000;
|
||||
settings3.class_guid[1] = 0x00000000;
|
||||
settings3.class_guid[2] = 0x00000000;
|
||||
settings3.class_guid[3] = 0x00000000;
|
||||
const char property3[] = "USB Composite Device";
|
||||
memcpy(settings3.property_devicedesc, property3, sizeof(property3));
|
||||
settings3.property_address[0] = 1;
|
||||
settings3.property_address[1] = 7;
|
||||
setupapihook_add(settings3);
|
||||
init_legacy_camera_hook(FLIP_CAMS);
|
||||
}
|
||||
|
||||
// init cfgmgr32 hooks
|
||||
@@ -545,18 +384,38 @@ namespace games::iidx {
|
||||
SetEnvironmentVariable("CONNECT_CAMERA", "0");
|
||||
}
|
||||
|
||||
if (SCREEN_MODE.has_value()) {
|
||||
SetEnvironmentVariable("SCREEN_MODE", SCREEN_MODE.value().c_str());
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
#ifdef SPICE64
|
||||
this->detect_sound_output_device();
|
||||
|
||||
#endif
|
||||
|
||||
// check bad model name
|
||||
if (!cfg::CONFIGURATOR_STANDALONE && avs::game::is_model("TDJ")) {
|
||||
log_fatal(
|
||||
"iidx",
|
||||
"BAD MODEL NAME ERROR\n\n\n"
|
||||
"!!! model name set to TDJ, this is WRONG and will break your game !!!\n"
|
||||
"If you are trying to boot IIDX with Lightning Model mode, please do the following instead:\n"
|
||||
" Revert your changes to XML file so it says <model __type=\"str\">LDJ</model>\n"
|
||||
" In SpiceCfg, enable 'IIDX TDJ Mode' or provide -iidxtdj flag in command line\n"
|
||||
" Apply any applicable settings / patches / hex edits\n"
|
||||
"!!! !!!\n"
|
||||
"!!! If you are trying to boot IIDX with Lightning Model mode, !!!\n"
|
||||
"!!! please do the following instead: !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! Revert your changes to XML file so it says !!!\n"
|
||||
"!!! <model __type=\"str\">LDJ</model> !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! In SpiceCfg, enable 'IIDX TDJ Mode' or provide -iidxtdj flag !!!\n"
|
||||
"!!! in command line !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! Apply any applicable settings / patches / hex edits !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! model name set to TDJ, this is WRONG and will break your game !!!\n\n\n"
|
||||
);
|
||||
}
|
||||
@@ -577,6 +436,7 @@ namespace games::iidx {
|
||||
"using user-supplied \"{}\" for SOUND_OUTPUT_DEVICE",
|
||||
SOUND_OUTPUT_DEVICE.value());
|
||||
SetEnvironmentVariable("SOUND_OUTPUT_DEVICE", SOUND_OUTPUT_DEVICE.value().c_str());
|
||||
SOUND_OUTPUT_DEVICE_IN_EFFECT = SOUND_OUTPUT_DEVICE;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -607,12 +467,8 @@ namespace games::iidx {
|
||||
device = "asio";
|
||||
}
|
||||
log_info("iidx", "SOUND_OUTPUT_DEVICE set to {}", device);
|
||||
log_info(
|
||||
"iidx",
|
||||
"SOUND_OUTPUT_DEVICE only affects IIDX27+ and ignored by older games. "
|
||||
"If {} is not what you wanted, you can override it with -iidxsounddevice option",
|
||||
device);
|
||||
SetEnvironmentVariable("SOUND_OUTPUT_DEVICE", device);
|
||||
SOUND_OUTPUT_DEVICE_IN_EFFECT = device;
|
||||
}
|
||||
|
||||
uint32_t get_pad() {
|
||||
@@ -864,4 +720,63 @@ namespace games::iidx {
|
||||
bool is_tdj_fhd() {
|
||||
return TDJ_MODE && avs::game::is_ext(2022101900, MAXINT);
|
||||
}
|
||||
|
||||
void apply_audio_hacks() {
|
||||
|
||||
#ifdef SPICE64
|
||||
|
||||
const auto has_XONAR_SOUND_CARD = find_pattern(
|
||||
avs::game::DLL_INSTANCE,
|
||||
"584F4E415220534F554E442043415244", // XONAR SOUND CARD
|
||||
"XXXXXXXXXXXXXXXX",
|
||||
0,
|
||||
0);
|
||||
|
||||
const auto has_SOUND_OUTPUT_DEVICE = find_pattern(
|
||||
avs::game::DLL_INSTANCE,
|
||||
"534F554E445F4F55545055545F444556494345", // SOUND_OUTPUT_DEVICE
|
||||
"XXXXXXXXXXXXXXXXXXX",
|
||||
0,
|
||||
0);
|
||||
|
||||
// attempt to detect ASIO support
|
||||
// 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) {
|
||||
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") {
|
||||
intptr_t result = replace_pattern(
|
||||
avs::game::DLL_INSTANCE,
|
||||
"FF5008E8????ECFF83780803740D",
|
||||
"??????BB00000000EB169090????",
|
||||
0, 0);
|
||||
|
||||
if (result == 0) {
|
||||
log_warning(
|
||||
"iidx",
|
||||
"Failed to force WASAPI as audio engine using signature matching. "
|
||||
"Unless patches are applied, game will default to ASIO");
|
||||
} else {
|
||||
log_info(
|
||||
"iidx",
|
||||
"Successfully forced WASAPI as audio engine using signature matching @ 0x{:x}.",
|
||||
result);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user