mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-02 23:00:42 -07:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c767918729 | |||
| fbc8bc2502 | |||
| c38f42cb19 | |||
| 42d8bbc92f | |||
| a09905792d | |||
| f113eb4f52 | |||
| f726748ebe |
@@ -526,6 +526,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
misc/device.cpp
|
misc/device.cpp
|
||||||
misc/eamuse.cpp
|
misc/eamuse.cpp
|
||||||
misc/extdev.cpp
|
misc/extdev.cpp
|
||||||
|
misc/nativetouchhook.cpp
|
||||||
misc/sciunit.cpp
|
misc/sciunit.cpp
|
||||||
misc/sde.cpp
|
misc/sde.cpp
|
||||||
misc/wintouchemu.cpp
|
misc/wintouchemu.cpp
|
||||||
|
|||||||
+168
-16
@@ -9,6 +9,15 @@
|
|||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#define MDFX_DEBUG_VERBOSE 0
|
||||||
|
|
||||||
|
#if MDFX_DEBUG_VERBOSE
|
||||||
|
#define log_debug(module, format_str, ...) logger::push( \
|
||||||
|
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||||
|
#else
|
||||||
|
#define log_debug(module, format_str, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
// constants
|
// constants
|
||||||
const size_t STATUS_BUFFER_SIZE = 32;
|
const size_t STATUS_BUFFER_SIZE = 32;
|
||||||
const size_t STATUS_BUFFER_NUM_ENTRIES = 16;
|
const size_t STATUS_BUFFER_NUM_ENTRIES = 16;
|
||||||
@@ -30,6 +39,21 @@ static bool IS_MDXF_ACTIVE = false;
|
|||||||
static const uint8_t BACKFILL_INTERVAL_MS = 4;
|
static const uint8_t BACKFILL_INTERVAL_MS = 4;
|
||||||
static const uint8_t BACKFILL_PADDING_MS = 2;
|
static const uint8_t BACKFILL_PADDING_MS = 2;
|
||||||
|
|
||||||
|
// These are used to determine if a thread needs to be spun up to keep pad state ring buffers populated with enough recent polls
|
||||||
|
static uint64_t START_TIME = 0;
|
||||||
|
static int CALL_COUNT = 0;
|
||||||
|
static const int THRESHOLD_REFRESH_RATE = 120;
|
||||||
|
|
||||||
|
static std::atomic<bool> IS_REFRESH_RATE_MEASUREMENT_STARTED{false};
|
||||||
|
static std::atomic<bool> IS_REFRESH_RATE_DETERMINED{false};
|
||||||
|
static std::atomic<bool> IS_THREAD_NEEDED{false};
|
||||||
|
static std::atomic<bool> MDXF_THREAD_RUNNING{false};
|
||||||
|
|
||||||
|
static std::thread MDXF_THREAD;
|
||||||
|
|
||||||
|
static constexpr int THREAD_REFRESH_RATE_HZ = 125;
|
||||||
|
static constexpr auto THREAD_PERIOD = std::chrono::milliseconds(1000 / THREAD_REFRESH_RATE_HZ);
|
||||||
|
|
||||||
// buffers
|
// buffers
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
static struct {
|
static struct {
|
||||||
@@ -40,9 +64,20 @@ static struct {
|
|||||||
|
|
||||||
static bool STATUS_BUFFER_FREEZE = false;
|
static bool STATUS_BUFFER_FREEZE = false;
|
||||||
|
|
||||||
|
// Decides which method to use for populating ring buffer entries for "padding".
|
||||||
|
// Overwritten in spicecfg using P4IO Buffer Algorithm option.
|
||||||
|
// THREAD_MODE: Spins a thread running at THREAD_REFRESH_RATE_HZ which periodically fills the ring
|
||||||
|
// buffer with auxiliary entries. Falls back on BACKFILL_MODE
|
||||||
|
// BACKFILL_MODE: On every update cycle, fill the ring buffer with entries for the last known state
|
||||||
|
// BACKFILL_INTERVAL_MS apart from each other from the time of the last entry to the
|
||||||
|
// current time before adding the entry for the current state.
|
||||||
|
// AUTO_MODE: thread mode if <120Hz, backfill if >=120Hz
|
||||||
|
acio::MDXFBufferFillMode acio::MDXF_BUFFER_FILL_MODE = acio::MDXFBufferFillMode::AUTO_MODE;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
ARKMDXP4_POLL = 0,
|
ARKMDXP4_POLL = 0,
|
||||||
EXTERNAL_POLL = 1
|
INTERNAL_POLL = 1,
|
||||||
|
EXTERNAL_POLL = 2
|
||||||
} MDXFPollSource;
|
} MDXFPollSource;
|
||||||
|
|
||||||
typedef uint64_t (__cdecl *ARK_GET_TICK_TIME64_T)();
|
typedef uint64_t (__cdecl *ARK_GET_TICK_TIME64_T)();
|
||||||
@@ -61,6 +96,106 @@ static uint64_t arkGetTickTime64() {
|
|||||||
return getTickTime64 ? getTickTime64() : timeGetTime();
|
return getTickTime64 ? getTickTime64() : timeGetTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used to keep the ring buffer populated with steady updates. 60Hz interval is too slow
|
||||||
|
static void mdxf_thread_start() {
|
||||||
|
bool expected = false;
|
||||||
|
if (!MDXF_THREAD_RUNNING.compare_exchange_strong(expected, true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info("mdxf", "starting poll thread");
|
||||||
|
|
||||||
|
MDXF_THREAD = std::thread([] {
|
||||||
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
|
||||||
|
|
||||||
|
while (MDXF_THREAD_RUNNING.load(std::memory_order_acquire)) {
|
||||||
|
mdxf_poll(false);
|
||||||
|
std::this_thread::sleep_for(THREAD_PERIOD);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mdxf_thread_stop() {
|
||||||
|
if (!MDXF_THREAD_RUNNING.exchange(false)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MDXF_THREAD.joinable()) {
|
||||||
|
MDXF_THREAD.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snaps measured refresh rate to best fit
|
||||||
|
static int snap_refresh_rate(int measured_hz) {
|
||||||
|
static constexpr std::array<int, 6> rates = {
|
||||||
|
60, 120, 144, 165, 180, 240
|
||||||
|
};
|
||||||
|
|
||||||
|
int best = rates[0];
|
||||||
|
int best_err = std::fabs(measured_hz - best);
|
||||||
|
|
||||||
|
for (int r : rates) {
|
||||||
|
int err = std::fabs(measured_hz - r);
|
||||||
|
if (err < best_err) {
|
||||||
|
best = r;
|
||||||
|
best_err = err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increments the number of times the update function was called,
|
||||||
|
// then calculates the current refresh rate of the game
|
||||||
|
// (20 seconds after the game starts, until 25 seconds)
|
||||||
|
static void count_calls_from_game() {
|
||||||
|
if (IS_REFRESH_RATE_DETERMINED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint64_t current_time = arkGetTickTime64();
|
||||||
|
|
||||||
|
if (!IS_REFRESH_RATE_MEASUREMENT_STARTED) {
|
||||||
|
if (START_TIME == 0) {
|
||||||
|
START_TIME = current_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
// boot screen takes about 10 seconds, so let's wait for double that
|
||||||
|
if ((current_time - START_TIME) < 20000) {
|
||||||
|
// too early, do nothing
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// 20s has passed for the first time, start measuring on next call
|
||||||
|
IS_REFRESH_RATE_MEASUREMENT_STARTED = true;
|
||||||
|
START_TIME = current_time;
|
||||||
|
log_debug("mdxf", "measurement begin");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint64_t elapsed_time = current_time - START_TIME;
|
||||||
|
CALL_COUNT++;
|
||||||
|
if (elapsed_time >= 5000) {
|
||||||
|
double measured_hz = static_cast<double>(CALL_COUNT) * 1000.0 / static_cast<double>(elapsed_time);
|
||||||
|
|
||||||
|
// Account for the main loop calling this twice per iteration
|
||||||
|
measured_hz *= 0.5;
|
||||||
|
const int snapped_hz = snap_refresh_rate(static_cast<int>(measured_hz));
|
||||||
|
|
||||||
|
IS_REFRESH_RATE_DETERMINED = true;
|
||||||
|
IS_THREAD_NEEDED = (snapped_hz < THRESHOLD_REFRESH_RATE);
|
||||||
|
log_info(
|
||||||
|
"mdxf",
|
||||||
|
"detected: {} Hz, best fit: {} Hz",
|
||||||
|
static_cast<int>(measured_hz),
|
||||||
|
snapped_hz);
|
||||||
|
|
||||||
|
if (IS_THREAD_NEEDED) {
|
||||||
|
mdxf_thread_start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Implementations
|
* Implementations
|
||||||
*/
|
*/
|
||||||
@@ -92,22 +227,20 @@ static uint64_t __cdecl ac_io_mdxf_get_control_status_buffer(int node, void *out
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(*mutex);
|
std::lock_guard<std::mutex> lock(*mutex);
|
||||||
|
|
||||||
|
const uint8_t start_index = (head_in == 0xFF) ? *head : head_in;
|
||||||
|
|
||||||
if (head_in != 0xFF) {
|
// Compute ring index: walk backwards from start_index as index increases
|
||||||
*head = head_in;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute ring index: walk backwards from head as index increases
|
|
||||||
// Assumes ring buffer size is a power of two
|
// Assumes ring buffer size is a power of two
|
||||||
const size_t mask = size - 1;
|
const size_t mask = size - 1;
|
||||||
const size_t offset = static_cast<size_t>(index) & mask;
|
const size_t offset = static_cast<size_t>(index) & mask;
|
||||||
const size_t i = (static_cast<size_t>(*head) - offset + size) & mask;
|
const size_t i = (static_cast<size_t>(start_index) - offset + size) & mask;
|
||||||
|
|
||||||
// Copy the chosen entry
|
// Copy the chosen entry
|
||||||
memcpy(out, buffer[i], STATUS_BUFFER_SIZE);
|
memcpy(out, buffer[i], STATUS_BUFFER_SIZE);
|
||||||
|
|
||||||
// Return the head value actually used
|
// Return the start value actually used
|
||||||
return static_cast<uint64_t>(*head);
|
return static_cast<uint64_t>(start_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
return error_ret;
|
return error_ret;
|
||||||
@@ -162,8 +295,18 @@ static bool __cdecl ac_io_mdxf_update_control_status_buffer_impl(int node, MDXFP
|
|||||||
// Dance Dance Revolution
|
// Dance Dance Revolution
|
||||||
if (avs::game::is_model("MDX")) {
|
if (avs::game::is_model("MDX")) {
|
||||||
// Marks this module as actively being used, allowing this function to be called from other sources
|
// Marks this module as actively being used, allowing this function to be called from other sources
|
||||||
if (!IS_MDXF_ACTIVE && source == ARKMDXP4_POLL) {
|
if (source == ARKMDXP4_POLL) {
|
||||||
IS_MDXF_ACTIVE = true;
|
if (!IS_MDXF_ACTIVE) {
|
||||||
|
log_debug("mdxf", "initializing mdxf I/O support");
|
||||||
|
IS_MDXF_ACTIVE = true;
|
||||||
|
if (acio::MDXF_BUFFER_FILL_MODE == acio::MDXFBufferFillMode::THREAD_MODE) {
|
||||||
|
IS_THREAD_NEEDED = true;
|
||||||
|
mdxf_thread_start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (acio::MDXF_BUFFER_FILL_MODE == acio::MDXFBufferFillMode::AUTO_MODE) {
|
||||||
|
count_calls_from_game();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t (*buffer)[STATUS_BUFFER_SIZE];
|
uint8_t (*buffer)[STATUS_BUFFER_SIZE];
|
||||||
@@ -226,6 +369,8 @@ static bool __cdecl ac_io_mdxf_update_control_status_buffer_impl(int node, MDXFP
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint16_t current_state;
|
uint16_t current_state;
|
||||||
|
|
||||||
|
// Only call getState() if called externally when actual input events happen, otherwise use previous known state
|
||||||
if (source == EXTERNAL_POLL) {
|
if (source == EXTERNAL_POLL) {
|
||||||
// get buttons
|
// get buttons
|
||||||
auto &buttons = games::ddr::get_buttons();
|
auto &buttons = games::ddr::get_buttons();
|
||||||
@@ -276,8 +421,8 @@ static bool __cdecl ac_io_mdxf_update_control_status_buffer_impl(int node, MDXFP
|
|||||||
start_time = min_time;
|
start_time = min_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only write one entry if called externally
|
// Don't backfill entries if called externally or if a separate thread is being used to fill auxiliary entries
|
||||||
if (source == EXTERNAL_POLL) {
|
if (source == EXTERNAL_POLL || IS_THREAD_NEEDED) {
|
||||||
start_time = stop_time - 1;
|
start_time = stop_time - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,11 +468,12 @@ static bool __cdecl ac_io_mdxf_update_control_status_buffer(int node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Used for triggering updates of the controller states from outside arkmdxp4.dll main refresh loop (i.e. within rawinput.cpp on controller events)
|
// Used for triggering updates of the controller states from outside arkmdxp4.dll main refresh loop (i.e. within rawinput.cpp on controller events)
|
||||||
void mdxf_poll() {
|
void mdxf_poll(bool isExternal) {
|
||||||
if (IS_MDXF_ACTIVE) {
|
if (IS_MDXF_ACTIVE) {
|
||||||
|
const MDXFPollSource source = isExternal ? EXTERNAL_POLL : INTERNAL_POLL;
|
||||||
const uint64_t call_time_ms = arkGetTickTime64();
|
const uint64_t call_time_ms = arkGetTickTime64();
|
||||||
ac_io_mdxf_update_control_status_buffer_impl(17, EXTERNAL_POLL, call_time_ms);
|
ac_io_mdxf_update_control_status_buffer_impl(17, source, call_time_ms);
|
||||||
ac_io_mdxf_update_control_status_buffer_impl(18, EXTERNAL_POLL, call_time_ms);
|
ac_io_mdxf_update_control_status_buffer_impl(18, source, call_time_ms);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,4 +494,10 @@ void acio::MDXFModule::attach() {
|
|||||||
ACIO_MODULE_HOOK(ac_io_mdxf_get_control_status_buffer);
|
ACIO_MODULE_HOOK(ac_io_mdxf_get_control_status_buffer);
|
||||||
ACIO_MODULE_HOOK(ac_io_mdxf_set_output_level);
|
ACIO_MODULE_HOOK(ac_io_mdxf_set_output_level);
|
||||||
ACIO_MODULE_HOOK(ac_io_mdxf_update_control_status_buffer);
|
ACIO_MODULE_HOOK(ac_io_mdxf_update_control_status_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
acio::MDXFModule::~MDXFModule() {
|
||||||
|
if (IS_THREAD_NEEDED) {
|
||||||
|
mdxf_thread_stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,10 +4,24 @@
|
|||||||
|
|
||||||
namespace acio {
|
namespace acio {
|
||||||
|
|
||||||
|
enum class MDXFBufferFillMode {
|
||||||
|
// backfill mode, but if <120Hz, enable poll thread
|
||||||
|
AUTO_MODE,
|
||||||
|
|
||||||
|
// forces poll thread
|
||||||
|
THREAD_MODE,
|
||||||
|
|
||||||
|
// forces backfill mode (no poll thread)
|
||||||
|
BACKFILL_MODE
|
||||||
|
};
|
||||||
|
|
||||||
|
extern MDXFBufferFillMode MDXF_BUFFER_FILL_MODE;
|
||||||
|
|
||||||
class MDXFModule : public ACIOModule {
|
class MDXFModule : public ACIOModule {
|
||||||
public:
|
public:
|
||||||
MDXFModule(HMODULE module, HookMode hookMode);
|
MDXFModule(HMODULE module, HookMode hookMode);
|
||||||
|
|
||||||
virtual void attach() override;
|
virtual void attach() override;
|
||||||
|
~MDXFModule() override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Called from rawinput thread whenever inputs have just been updated
|
// Called from rawinput thread whenever inputs have just been updated
|
||||||
void mdxf_poll();
|
void mdxf_poll(bool isExternal);
|
||||||
@@ -84,7 +84,7 @@ static bool __cdecl ac_io_panb_start_auto_input() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t panb_get_button_velocity(Button button, Button button_soft, Button button_medium, Button button_hard) {
|
static uint8_t panb_get_button_velocity(Button& button, Button& button_soft, Button& button_medium, Button& button_hard) {
|
||||||
const auto velocity = Buttons::getVelocity(RI_MGR, button);
|
const auto velocity = Buttons::getVelocity(RI_MGR, button);
|
||||||
const auto velocity_soft = Buttons::getVelocity(RI_MGR, button_soft);
|
const auto velocity_soft = Buttons::getVelocity(RI_MGR, button_soft);
|
||||||
const auto velocity_medium = Buttons::getVelocity(RI_MGR, button_medium);
|
const auto velocity_medium = Buttons::getVelocity(RI_MGR, button_medium);
|
||||||
|
|||||||
@@ -95,12 +95,19 @@ namespace acio2emu {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log_fatal("acio2emu", "cannot set step: unknown value: {}", s);
|
log_fatal(
|
||||||
|
"acio2emu",
|
||||||
|
"cannot set step: unknown value: {}",
|
||||||
|
static_cast<uint32_t>(s));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
log_fatal("acio2emu", "illegal transition detected: {} -> {}", step_, s);
|
log_fatal(
|
||||||
|
"acio2emu",
|
||||||
|
"illegal transition detected: {} -> {}",
|
||||||
|
static_cast<uint32_t>(step_),
|
||||||
|
static_cast<uint32_t>(s));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
step_ = s;
|
step_ = s;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "hooks/sleephook.h"
|
#include "hooks/sleephook.h"
|
||||||
#include "launcher/options.h"
|
#include "launcher/options.h"
|
||||||
#include "touch/touch.h"
|
#include "touch/touch.h"
|
||||||
|
#include "misc/nativetouchhook.h"
|
||||||
#include "misc/wintouchemu.h"
|
#include "misc/wintouchemu.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
#include "util/detour.h"
|
#include "util/detour.h"
|
||||||
@@ -332,7 +333,9 @@ namespace games::iidx {
|
|||||||
// need to hook `avs2-core.dll` so AVS win32fs operations go through rom hook
|
// need to hook `avs2-core.dll` so AVS win32fs operations go through rom hook
|
||||||
devicehook_init(avs::core::DLL_INSTANCE);
|
devicehook_init(avs::core::DLL_INSTANCE);
|
||||||
|
|
||||||
if (!NATIVE_TOUCH) {
|
if (NATIVE_TOUCH) {
|
||||||
|
nativetouchhook::hook(avs::game::DLL_INSTANCE);
|
||||||
|
} else {
|
||||||
wintouchemu::FORCE = true;
|
wintouchemu::FORCE = true;
|
||||||
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
|
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
|
||||||
wintouchemu::hook_title_ends("beatmania IIDX", "main", avs::game::DLL_INSTANCE);
|
wintouchemu::hook_title_ends("beatmania IIDX", "main", avs::game::DLL_INSTANCE);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "api/modules/capture.h"
|
#include "api/modules/capture.h"
|
||||||
#include "acio/acio.h"
|
#include "acio/acio.h"
|
||||||
#include "acio/icca/icca.h"
|
#include "acio/icca/icca.h"
|
||||||
|
#include "acio/mdxf/mdxf.h"
|
||||||
#include "api/controller.h"
|
#include "api/controller.h"
|
||||||
#include "avs/automap.h"
|
#include "avs/automap.h"
|
||||||
#include "avs/core.h"
|
#include "avs/core.h"
|
||||||
@@ -1149,6 +1150,13 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
rawinput::RAWINPUT_REQUIRE_FOCUS = false;
|
rawinput::RAWINPUT_REQUIRE_FOCUS = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::DDRP4IOBufferMode].is_active()) {
|
||||||
|
if (options[launcher::Options::DDRP4IOBufferMode].value_text() == "thread") {
|
||||||
|
acio::MDXF_BUFFER_FILL_MODE = acio::MDXFBufferFillMode::THREAD_MODE;
|
||||||
|
} else if (options[launcher::Options::DDRP4IOBufferMode].value_text() == "backfill") {
|
||||||
|
acio::MDXF_BUFFER_FILL_MODE = acio::MDXFBufferFillMode::BACKFILL_MODE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (options[launcher::Options::MidiAlgoVer].is_active()) {
|
if (options[launcher::Options::MidiAlgoVer].is_active()) {
|
||||||
if (options[launcher::Options::MidiAlgoVer].value_text() == "legacy") {
|
if (options[launcher::Options::MidiAlgoVer].value_text() == "legacy") {
|
||||||
|
|||||||
@@ -2291,6 +2291,26 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.setting_name = "20",
|
.setting_name = "20",
|
||||||
.category = "I/O Options",
|
.category = "I/O Options",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// DDRP4IOBufferMode
|
||||||
|
.title = "DDR P4IO Buffer Algorithm",
|
||||||
|
.name = "ddrp4iobuffer",
|
||||||
|
.desc =
|
||||||
|
"Remember to restart after changing this value.\n\n"
|
||||||
|
"Sets the algorithm used to populate entries to the buffer of controller polls read by the game.\n\n"
|
||||||
|
"auto (default): thread mode if <120Hz, backfill if >=120Hz\n\n"
|
||||||
|
"thread: starts a thread to periodically insert polls into the buffer\n\n"
|
||||||
|
"backfill: fills the buffer on each frame with last known state info at short regular intervals up to the current time, then writes the current state.\n\n"
|
||||||
|
"Only has an effect when emulating P4IO (arkmdxp4.dll)",
|
||||||
|
.type = OptionType::Enum,
|
||||||
|
.game_name = "Dance Dance Revolution",
|
||||||
|
.category = "Game Options (Advanced)",
|
||||||
|
.elements = {
|
||||||
|
{"auto", ""},
|
||||||
|
{"thread", ""},
|
||||||
|
{"backfill", ""},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// InputRequiresFocus
|
// InputRequiresFocus
|
||||||
.title = "Input Requires Focus",
|
.title = "Input Requires Focus",
|
||||||
|
|||||||
@@ -241,6 +241,7 @@ namespace launcher {
|
|||||||
IIDXRecDisable,
|
IIDXRecDisable,
|
||||||
MidiAlgoVer,
|
MidiAlgoVer,
|
||||||
MidiNoteSustain,
|
MidiNoteSustain,
|
||||||
|
DDRP4IOBufferMode,
|
||||||
InputRequiresFocus,
|
InputRequiresFocus,
|
||||||
NostalgiaPoke,
|
NostalgiaPoke,
|
||||||
ForceBackBufferCount,
|
ForceBackBufferCount,
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
// enable touch functions - set version to windows 7
|
||||||
|
// mingw otherwise doesn't load touch stuff
|
||||||
|
#define _WIN32_WINNT 0x0601
|
||||||
|
|
||||||
|
#include "wintouchemu.h"
|
||||||
|
|
||||||
|
#include "util/detour.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
|
||||||
|
#define TOUCH_SIMULATE_FAT_FINGERS 0
|
||||||
|
#define TOUCH_DEBUG_VERBOSE 0
|
||||||
|
|
||||||
|
#if TOUCH_DEBUG_VERBOSE
|
||||||
|
#define log_debug(module, format_str, ...) logger::push( \
|
||||||
|
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
||||||
|
#else
|
||||||
|
#define log_debug(module, format_str, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace nativetouchhook {
|
||||||
|
|
||||||
|
static decltype(GetTouchInputInfo) *GetTouchInputInfo_orig = nullptr;
|
||||||
|
|
||||||
|
static void strip_contact_size(PTOUCHINPUT point) {
|
||||||
|
|
||||||
|
#if TOUCH_SIMULATE_FAT_FINGERS
|
||||||
|
point->dwMask |= 0x004;
|
||||||
|
point->cxContact = 80 * 100;
|
||||||
|
point->cyContact = 60 * 100;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// most monitors do not set TOUCHEVENTFMASK_CONTACTAREA, but for
|
||||||
|
// monitors that do set it, IIDX can get very confused (SDVX is not
|
||||||
|
// affected)
|
||||||
|
//
|
||||||
|
// while the test menu and the touch "glow" seem to work properly,
|
||||||
|
// interacting with subscreen menu items or entering PIN becomes
|
||||||
|
// very unpredictable
|
||||||
|
//
|
||||||
|
// to fix this, simply remove the contact area width and height
|
||||||
|
//
|
||||||
|
// note: test menu > I/O > touch test gives 5 numbers:
|
||||||
|
// n: x, y, w, h
|
||||||
|
// where
|
||||||
|
// n is the nth touch input since boot
|
||||||
|
// x, y are coordinates (center of finger)
|
||||||
|
// w, h are contact width and height
|
||||||
|
//
|
||||||
|
// when TOUCHEVENTFMASK_CONTACTAREA is not set, w/h will
|
||||||
|
// automatically be seen as 1x1, which works perfectly fine
|
||||||
|
|
||||||
|
log_debug(
|
||||||
|
"touch::native",
|
||||||
|
"[{}, {}] dwMask = 0x{:x}, cxContact = {}, cyContact = {}",
|
||||||
|
point->x / 100,
|
||||||
|
point->y / 100,
|
||||||
|
point->dwMask,
|
||||||
|
point->cxContact,
|
||||||
|
point->cyContact);
|
||||||
|
|
||||||
|
point->dwMask &= ~(0x004ul); // clear TOUCHEVENTFMASK_CONTACTAREA
|
||||||
|
point->cxContact = 0;
|
||||||
|
point->cyContact = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL WINAPI GetTouchInputInfoHook(
|
||||||
|
HTOUCHINPUT hTouchInput, UINT cInputs, PTOUCHINPUT pInputs, int cbSize) {
|
||||||
|
|
||||||
|
// call the original fist
|
||||||
|
const auto result = GetTouchInputInfo_orig(hTouchInput, cInputs, pInputs, cbSize);
|
||||||
|
if (result == 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < cInputs; i++) {
|
||||||
|
PTOUCHINPUT point = &pInputs[i];
|
||||||
|
strip_contact_size(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hook(HMODULE module) {
|
||||||
|
GetTouchInputInfo_orig = detour::iat_try("GetTouchInputInfo", GetTouchInputInfoHook, module);
|
||||||
|
if (GetTouchInputInfo_orig != nullptr) {
|
||||||
|
log_misc("touch::native", "GetTouchInputInfo hooked");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
namespace nativetouchhook {
|
||||||
|
void hook(HMODULE module);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1850,7 +1850,7 @@ LRESULT CALLBACK rawinput::RawInputManager::input_wnd_proc(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update controller state ring buffers (DDR/MDXF)
|
// update controller state ring buffers (DDR/MDXF)
|
||||||
mdxf_poll();
|
mdxf_poll(true);
|
||||||
|
|
||||||
// call the default window handler for cleanup
|
// call the default window handler for cleanup
|
||||||
DefWindowProc(hWnd, msg, wparam, lParam);
|
DefWindowProc(hWnd, msg, wparam, lParam);
|
||||||
@@ -2835,4 +2835,4 @@ void rawinput::RawInputManager::remove_callback_midi(void * data, const std::fun
|
|||||||
[data, callback](MidiCallback const &cb) {
|
[data, callback](MidiCallback const &cb) {
|
||||||
return cb.data == data && cb.f.target<void>() == callback.target<void>();
|
return cb.data == data && cb.f.target<void>() == callback.target<void>();
|
||||||
}), this->callback_midi.end());
|
}), this->callback_midi.end());
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user