mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-02 06:40:42 -07:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1e6b4c16cc | |||
| d6eb8f7125 | |||
| dc6850e479 | |||
| 2d623e179b | |||
| 884d665c1c | |||
| 2842fe7ec6 | |||
| 954e6022d9 | |||
| 955c50a9f3 | |||
| dc82c980b1 | |||
| 408ed17521 | |||
| f4050e9adb | |||
| badae01357 | |||
| 38d9939eb7 | |||
| f5a3caf536 | |||
| 027445afb1 | |||
| 63a3900b79 | |||
| f542a78347 | |||
| 4d32dde83e | |||
| 927170ce73 | |||
| 875a0f0765 | |||
| 3ad88ef15c | |||
| 7a6cadb176 | |||
| 4feca23240 | |||
| 2e52bebd3d | |||
| aa218d5be7 | |||
| 94f009991a | |||
| c3b9ce3fdc | |||
| 1517cf9a7c | |||
| f0f46296f2 | |||
| 1d1981fcde | |||
| 2f538fe2e4 | |||
| 8e01b2e5c8 | |||
| cd14ca800e | |||
| cb59fe14e9 | |||
| 9fd9290cd3 | |||
| 1dd99aac74 | |||
| 9e376f373e | |||
| d4e51b77d1 | |||
| 61181836b0 | |||
| 4e26c8afa5 | |||
| 7684028f99 | |||
| 1d6be8fe4b | |||
| c70cf40af3 | |||
| 5715d4e605 | |||
| 821a705a7d | |||
| 86f2a1fd39 | |||
| 86f218329a |
@@ -7,7 +7,7 @@
|
||||
>
|
||||
> Feel free to remove this section after you have read it.
|
||||
|
||||
## Link to GitHub Issue, if one exists
|
||||
## Link to GitHub Issue or related Pull Request, if one exists
|
||||
#0
|
||||
|
||||
## Description of change
|
||||
|
||||
@@ -170,8 +170,8 @@ else()
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG")
|
||||
|
||||
# file prefix map for relative working directory
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -ffile-prefix-map=${CMAKE_SOURCE_DIR}=.")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffile-prefix-map=${CMAKE_SOURCE_DIR}=.")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -ffile-prefix-map=\"${CMAKE_SOURCE_DIR}=.\"")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffile-prefix-map=\"${CMAKE_SOURCE_DIR}=.\"")
|
||||
endif()
|
||||
|
||||
# release with debug info flags
|
||||
@@ -441,6 +441,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
games/scotto/io.cpp
|
||||
games/drs/drs.cpp
|
||||
games/drs/io.cpp
|
||||
games/drs/rgb_cam.cpp
|
||||
games/we/we.cpp
|
||||
games/we/io.cpp
|
||||
games/we/touchpanel.cpp
|
||||
@@ -554,6 +555,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
overlay/windows/camera_control.cpp
|
||||
overlay/windows/card_manager.cpp
|
||||
overlay/windows/drs_dancefloor.cpp
|
||||
overlay/windows/gfdm_sub.cpp
|
||||
overlay/windows/screen_resize.cpp
|
||||
overlay/windows/sdvx_sub.cpp
|
||||
overlay/windows/config.cpp
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace api::modules {
|
||||
void DRS::touch_set(Request &req, Response &res) {
|
||||
|
||||
// get all touch points
|
||||
games::drs::drs_touch_t touches[16];
|
||||
auto touches = std::make_unique<games::drs::drs_touch_t[]>(req.params.Size());
|
||||
size_t i = 0;
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
@@ -86,6 +86,6 @@ namespace api::modules {
|
||||
}
|
||||
|
||||
// apply touch points
|
||||
games::drs::fire_touches(touches, i);
|
||||
games::drs::fire_touches(touches.get(), i);
|
||||
}
|
||||
}
|
||||
|
||||
+24
-1
@@ -5,8 +5,9 @@
|
||||
#include "build/defs.h"
|
||||
#include "easrv/easrv.h"
|
||||
#include "easrv/smartea.h"
|
||||
#include "games/mfc/mfc.h"
|
||||
#include "games/gitadora/gitadora.h"
|
||||
#include "hooks/avshook.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/deferlog.h"
|
||||
#include "util/fileutils.h"
|
||||
@@ -528,6 +529,28 @@ namespace avs {
|
||||
}
|
||||
}
|
||||
|
||||
// update -WINDOW in app-config
|
||||
if (games::gitadora::is_arena_model()) {
|
||||
avs::core::property_search_remove_safe(app_config, nullptr, "/param/cmdline");
|
||||
std::string cmdline{};
|
||||
if (games::gitadora::is_drum()) {
|
||||
if (GRAPHICS_WINDOWED) {
|
||||
cmdline = "-d -DM -WINDOW";
|
||||
} else {
|
||||
cmdline = "-d -DM";
|
||||
}
|
||||
} else {
|
||||
if (GRAPHICS_WINDOWED) {
|
||||
cmdline = "-g -GF -WINDOW";
|
||||
} else {
|
||||
cmdline = "-g -GF";
|
||||
}
|
||||
}
|
||||
log_info("avs-ea3", "using cmdline: `{}`", cmdline);
|
||||
avs::core::property_node_create(app_config, nullptr,
|
||||
avs::core::NODE_TYPE_str, "/param/cmdline", cmdline.c_str());
|
||||
}
|
||||
|
||||
auto app_param = avs::core::property_search_safe(app_config, nullptr, "/param");
|
||||
|
||||
// call the game init
|
||||
|
||||
@@ -74,7 +74,7 @@ then
|
||||
BUILD_TYPE="Debug"
|
||||
BUILDDIR_32=${BUILDDIR_32_DEBUG}
|
||||
BUILDDIR_64=${BUILDDIR_64_DEBUG}
|
||||
DIST_NAME=$(echo ${DIST_NAME} | sed 's/\.[^.]*$/-dbg&/')
|
||||
DIST_NAME=$(echo "${DIST_NAME}" | sed 's/\.[^.]*$/-dbg&/')
|
||||
fi
|
||||
|
||||
# determine number of cores
|
||||
@@ -114,7 +114,7 @@ time (
|
||||
fi
|
||||
mkdir -p ${BUILDDIR_32}
|
||||
pushd ${BUILDDIR_32} > /dev/null
|
||||
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_32} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && ninja ${TARGETS_32}
|
||||
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_32} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} "$OLDPWD" && ninja ${TARGETS_32}
|
||||
popd > /dev/null
|
||||
|
||||
# 64 bit
|
||||
@@ -127,7 +127,7 @@ time (
|
||||
fi
|
||||
mkdir -p ${BUILDDIR_64}
|
||||
pushd ${BUILDDIR_64} > /dev/null
|
||||
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && ninja ${TARGETS_64}
|
||||
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} "$OLDPWD" && ninja ${TARGETS_64}
|
||||
popd > /dev/null
|
||||
|
||||
echo ""
|
||||
@@ -234,7 +234,7 @@ mkdir -p ${OUTDIR}/src
|
||||
if ((INCLUDE_SRC > 0))
|
||||
then
|
||||
echo "Generating source file archive..."
|
||||
git archive --format tar.gz --prefix=spice2x/ HEAD ./ > ${OUTDIR}/src/spice2x-${GIT_BRANCH}.tar.gz 2>/dev/null || \
|
||||
git archive --format tar.gz --prefix=spice2x/ HEAD ./ > "${OUTDIR}/src/spice2x-${GIT_BRANCH}.tar.gz" 2>/dev/null || \
|
||||
echo "WARNING: Couldn't get git to create the archive. Is this a git repository?"
|
||||
fi
|
||||
|
||||
@@ -249,11 +249,11 @@ if ((DIST_ENABLE > 0))
|
||||
then
|
||||
echo "Building dist..."
|
||||
mkdir -p ${DIST_FOLDER}
|
||||
rm -rf ${DIST_FOLDER}/${DIST_NAME}
|
||||
rm -rf "${DIST_FOLDER}/${DIST_NAME}"
|
||||
pushd ${OUTDIR}/.. > /dev/null
|
||||
zip -qrXT9 $OLDPWD/${DIST_FOLDER}/${DIST_NAME} spice2x -x "spice2x/extras/*" -z <<< "$DIST_COMMENT"
|
||||
zip -qrXT9 "$OLDPWD/${DIST_FOLDER}/${DIST_NAME}" spice2x -x "spice2x/extras/*" -z <<< "$DIST_COMMENT"
|
||||
echo "Building extras..."
|
||||
zip -qrXT9 $OLDPWD/${DIST_FOLDER}/${DIST_NAME_EXTRAS} spice2x -z <<< "$DIST_COMMENT"
|
||||
zip -qrXT9 "$OLDPWD/${DIST_FOLDER}/${DIST_NAME_EXTRAS}" spice2x -z <<< "$DIST_COMMENT"
|
||||
popd > /dev/null
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
docker build --pull $PWD/external/docker -t spicetools/deps --platform linux/x86_64
|
||||
docker build --build-context gitroot=$PWD/../../.git . -t spicetools/spice:latest
|
||||
docker run --rm -v $PWD/dist:/src/src/spice2x/dist -v $PWD/bin:/src/src/spice2x/bin -v $PWD/.ccache:/src/src/spice2x/.ccache spicetools/spice "$@"
|
||||
docker build --pull "$PWD/external/docker" -t spicetools/deps --platform linux/x86_64
|
||||
docker build --build-context gitroot="$PWD/../../.git" . -t spicetools/spice:latest
|
||||
docker run --rm -v "$PWD/dist:/src/src/spice2x/dist" -v "$PWD/bin:/src/src/spice2x/bin" -v "$PWD/.ccache:/src/src/spice2x/.ccache" spicetools/spice "$@"
|
||||
|
||||
@@ -644,10 +644,12 @@ float GameAPI::Analogs::getState(rawinput::Device *device, Analog &analog) {
|
||||
case rawinput::HID: {
|
||||
|
||||
// get value
|
||||
if (inverted) {
|
||||
value = 1.f - device->hidInfo->value_states[index];
|
||||
} else {
|
||||
value = device->hidInfo->value_states[index];
|
||||
if (index < device->hidInfo->value_states.size()) {
|
||||
if (inverted) {
|
||||
value = 1.f - device->hidInfo->value_states[index];
|
||||
} else {
|
||||
value = device->hidInfo->value_states[index];
|
||||
}
|
||||
}
|
||||
|
||||
// deadzone
|
||||
|
||||
+33
-13
@@ -58,8 +58,28 @@ std::string Button::getVKeyString() {
|
||||
return "Pause";
|
||||
case 0x14:
|
||||
return "Caps Lock";
|
||||
case 0x15:
|
||||
return "IME Kana/Hangul";
|
||||
case 0x16:
|
||||
return "IME On";
|
||||
case 0x17:
|
||||
return "IME Junja";
|
||||
case 0x18:
|
||||
return "IME Final";
|
||||
case 0x19:
|
||||
return "IME Hanja/Kanji";
|
||||
case 0x1A:
|
||||
return "IME Off";
|
||||
case 0x1B:
|
||||
return "Escape";
|
||||
case 0x1C:
|
||||
return "IME Convert";
|
||||
case 0x1D:
|
||||
return "IME Nonconvert";
|
||||
case 0x1E:
|
||||
return "IME Accept";
|
||||
case 0x1F:
|
||||
return "IME Mode Change";
|
||||
case 0x20:
|
||||
return "Space";
|
||||
case 0x21:
|
||||
@@ -255,9 +275,9 @@ std::string Button::getVKeyString() {
|
||||
case 0xA3:
|
||||
return "Right Control";
|
||||
case 0xA4:
|
||||
return "Left Menu";
|
||||
return "Left Alt";
|
||||
case 0xA5:
|
||||
return "Right Menu";
|
||||
return "Right Alt";
|
||||
default:
|
||||
|
||||
// check win API
|
||||
@@ -301,17 +321,17 @@ std::string Button::getDisplayString(rawinput::RawInputManager* manager) {
|
||||
switch (device->type) {
|
||||
case rawinput::MOUSE: {
|
||||
const char *btn = "Unknown";
|
||||
static const char *MOUSE_NAMES[] = {
|
||||
"Left Mouse",
|
||||
"Right Mouse",
|
||||
"Middle Mouse",
|
||||
"Mouse 1",
|
||||
"Mouse 2",
|
||||
"Mouse 3",
|
||||
"Mouse 4",
|
||||
"Mouse 5",
|
||||
};
|
||||
if (vKey < sizeof(MOUSE_NAMES)) {
|
||||
static const std::array<const char *, 8>MOUSE_NAMES = {
|
||||
"Left Mouse",
|
||||
"Right Mouse",
|
||||
"Middle Mouse",
|
||||
"Mouse 1",
|
||||
"Mouse 2",
|
||||
"Mouse 3",
|
||||
"Mouse 4",
|
||||
"Mouse 5",
|
||||
};
|
||||
if (vKey < MOUSE_NAMES.size()) {
|
||||
btn = MOUSE_NAMES[vKey];
|
||||
}
|
||||
return fmt::format("{} ({})", btn, device->desc);
|
||||
|
||||
@@ -45,6 +45,7 @@ private:
|
||||
double debounce_up = 0.0;
|
||||
double debounce_down = 0.0;
|
||||
bool invert = false;
|
||||
bool is_temporary = false;
|
||||
|
||||
GameAPI::Buttons::State last_state = GameAPI::Buttons::BUTTON_NOT_PRESSED;
|
||||
float last_velocity = 0.f;
|
||||
@@ -164,6 +165,14 @@ public:
|
||||
this->last_velocity = last_velocity;
|
||||
}
|
||||
|
||||
inline bool isTemporary() const {
|
||||
return this->is_temporary;
|
||||
}
|
||||
|
||||
inline void setTemporary(bool is_temporary) {
|
||||
this->is_temporary = is_temporary;
|
||||
}
|
||||
|
||||
inline unsigned short getVelocityThreshold() const {
|
||||
return this->velocity_threshold;
|
||||
}
|
||||
@@ -172,6 +181,20 @@ public:
|
||||
this->velocity_threshold = velocity_threshold;
|
||||
}
|
||||
|
||||
inline bool isValid() {
|
||||
// temporarily created by UI
|
||||
if (isTemporary()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// nothing is set
|
||||
if (getDeviceIdentifier().empty() && getVKey() == INVALID_VKEY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void getMidiVKey(int& channel, int& index);
|
||||
void setMidiVKey(rawinput::RawInputManager* manager, bool is_note, int channel, int index);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "config.h"
|
||||
#include "util/logging.h"
|
||||
#include "cfg/button.h"
|
||||
|
||||
/*
|
||||
* This code absolutely sucks.
|
||||
@@ -10,6 +11,14 @@
|
||||
// settings
|
||||
std::string CONFIG_PATH_OVERRIDE = "";
|
||||
|
||||
#define CFG_DEBUG_VERBOSE 0
|
||||
|
||||
#if CFG_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
|
||||
|
||||
///////////////////
|
||||
/// Constructor ///
|
||||
@@ -510,8 +519,10 @@ bool Config::updateBinding(const Game &game, const Button &button, int alternati
|
||||
return false;
|
||||
}
|
||||
|
||||
tinyxml2::XMLElement *gameButtonNode = nullptr;
|
||||
|
||||
// iterate button nodes
|
||||
tinyxml2::XMLElement *gameButtonNode = gameButtonsNode->FirstChildElement("button");
|
||||
gameButtonNode = gameButtonsNode->FirstChildElement("button");
|
||||
int button_count = 0;
|
||||
while (gameButtonNode != nullptr) {
|
||||
const char *buttonNodeName = gameButtonNode->Attribute("name");
|
||||
@@ -574,6 +585,39 @@ bool Config::updateBinding(const Game &game, const Button &button, int alternati
|
||||
}
|
||||
}
|
||||
|
||||
// trim the trailing invalid (unbound) alternatives
|
||||
tinyxml2::XMLElement *to_delete = nullptr;
|
||||
gameButtonNode = gameButtonsNode->LastChildElement("button");
|
||||
while (gameButtonNode != nullptr) {
|
||||
const char *name = gameButtonNode->Attribute("name");
|
||||
const char *devid = gameButtonNode->Attribute("devid");
|
||||
int vkey = INVALID_VKEY;
|
||||
gameButtonNode->QueryIntAttribute("vkey", &vkey);
|
||||
|
||||
// wrong button, ignore and keep iterating backwards
|
||||
if (name == nullptr || std::string(name) != button.getName()) {
|
||||
gameButtonNode = gameButtonNode->PreviousSiblingElement("button");
|
||||
continue;
|
||||
}
|
||||
|
||||
// found a node for this button, which means to_delete is not the first one
|
||||
// delete the node for to_delete
|
||||
if (to_delete != nullptr) {
|
||||
log_debug("cfg", "deleting excessive button binding for {}", name);
|
||||
gameButtonsNode->DeleteChild(to_delete);
|
||||
to_delete = nullptr;
|
||||
}
|
||||
|
||||
if ((devid == nullptr || strlen(devid) == 0) && vkey == INVALID_VKEY) {
|
||||
// unbound button with no device ID, make a note of it so it can be deleted later
|
||||
to_delete = gameButtonNode;
|
||||
gameButtonNode = gameButtonNode->PreviousSiblingElement("button");
|
||||
} else {
|
||||
// valid binding found; stop iterating
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check if button was not found
|
||||
if (button_count == 0) {
|
||||
return false;
|
||||
@@ -760,6 +804,37 @@ bool Config::updateBinding(const Game &game, const Light &light, int alternative
|
||||
}
|
||||
}
|
||||
|
||||
// trim the trailing invalid (unbound) alternatives
|
||||
tinyxml2::XMLElement *to_delete = nullptr;
|
||||
gameLightNode = gameLightsNode->LastChildElement("light");
|
||||
while (gameLightNode != nullptr) {
|
||||
const char *name = gameLightNode->Attribute("name");
|
||||
const char *devid = gameLightNode->Attribute("devid");
|
||||
|
||||
// wrong light, ignore and keep iterating backwards
|
||||
if (name == nullptr || std::string(name) != light.getName()) {
|
||||
gameLightNode = gameLightNode->PreviousSiblingElement("light");
|
||||
continue;
|
||||
}
|
||||
|
||||
// found a node for this button, which means to_delete is not the first one
|
||||
// delete the node for to_delete
|
||||
if (to_delete != nullptr) {
|
||||
log_debug("cfg", "deleting excessive light binding for {}", name);
|
||||
gameLightsNode->DeleteChild(to_delete);
|
||||
to_delete = nullptr;
|
||||
}
|
||||
|
||||
if (devid == nullptr || strlen(devid) == 0) {
|
||||
// unbound light, make a note of it so it can be deleted later
|
||||
to_delete = gameLightNode;
|
||||
gameLightNode = gameLightNode->PreviousSiblingElement("light");
|
||||
} else {
|
||||
// valid binding found; stop iterating
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check if light was not found
|
||||
if (light_count == 0) {
|
||||
return false;
|
||||
|
||||
@@ -13,6 +13,7 @@ private:
|
||||
std::string lightName;
|
||||
std::string deviceIdentifier = "";
|
||||
unsigned int index = 0;
|
||||
bool is_temporary = false;
|
||||
|
||||
public:
|
||||
float last_state = 0.f;
|
||||
@@ -65,4 +66,26 @@ public:
|
||||
inline void setIndex(unsigned int index) {
|
||||
this->index = index;
|
||||
}
|
||||
|
||||
inline bool isTemporary() const {
|
||||
return this->is_temporary;
|
||||
}
|
||||
|
||||
inline void setTemporary(bool is_temporary) {
|
||||
this->is_temporary = is_temporary;
|
||||
}
|
||||
|
||||
inline bool isValid() {
|
||||
// temporarily created by UI
|
||||
if (isTemporary()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// nothing is set
|
||||
if (getDeviceIdentifier().empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -13,6 +13,15 @@ enum class OptionType {
|
||||
Hex,
|
||||
};
|
||||
|
||||
enum class OptionPickerType {
|
||||
None,
|
||||
AsioDriver,
|
||||
EACard,
|
||||
CpuAffinity,
|
||||
FilePath,
|
||||
DirectoryPath,
|
||||
};
|
||||
|
||||
struct OptionDefinition {
|
||||
std::string title;
|
||||
// unique identifier used for flag matching but also stored in config files
|
||||
@@ -32,6 +41,10 @@ struct OptionDefinition {
|
||||
bool sensitive = false;
|
||||
std::vector<std::pair<std::string, std::string>> elements = {};
|
||||
bool disabled = false;
|
||||
OptionPickerType picker = OptionPickerType::None;
|
||||
|
||||
// for OptionPickerType::FilePath
|
||||
std::string file_extension = "";
|
||||
};
|
||||
|
||||
class Option {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "bi2x_hook.h"
|
||||
|
||||
#if SPICE64
|
||||
|
||||
#include <cstdint>
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
@@ -16,15 +18,19 @@ namespace games::ccj {
|
||||
*/
|
||||
|
||||
struct AIO_SCI_COMM {
|
||||
uint8_t data[0xf8];
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB2 {
|
||||
uint8_t data[0xe30];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_TBS {
|
||||
uint8_t data[0x4680];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_WRFIRM {
|
||||
uint8_t data[0x20f48];
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB__NODEINFO {
|
||||
@@ -72,6 +78,12 @@ namespace games::ccj {
|
||||
AIO_IOB2_BI2X_AC1__OUTPUTDATA OutputData;
|
||||
};
|
||||
|
||||
// checked with UJK-001-2024060400
|
||||
static_assert(sizeof(AIO_SCI_COMM) == 0xf8);
|
||||
static_assert(sizeof(AIO_NMGR_IOB2) == 0xe30);
|
||||
static_assert(sizeof(AIO_IOB2_BI2X_TBS) == 0x4680);
|
||||
static_assert(sizeof(AIO_IOB2_BI2X_WRFIRM) == 0x20f48);
|
||||
|
||||
static void write_iccr_led(Lights::ccj_lights_t light, uint8_t value);
|
||||
|
||||
/*
|
||||
@@ -621,4 +633,6 @@ namespace games::ccj {
|
||||
detour::trampoline_try(libaioDll, "aioNodeCtl_IsError",
|
||||
aioNodeCtl_IsError, &aioNodeCtl_IsError_orig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#if SPICE64
|
||||
|
||||
namespace games::ccj {
|
||||
void bi2x_hook_init();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -68,9 +68,13 @@ namespace games::ccj {
|
||||
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(11),
|
||||
(void*)execexe_CreateFileW_hook,(void**)&execexe_CreateFileW_orig);
|
||||
|
||||
#if SPICE64
|
||||
|
||||
// insert BI2X hooks
|
||||
bi2x_hook_init();
|
||||
|
||||
#endif
|
||||
|
||||
// insert trackball hooks
|
||||
trackball_hook_init();
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/memutils.h"
|
||||
#include "rgb_cam.h"
|
||||
|
||||
#pragma pack(push)
|
||||
|
||||
@@ -150,6 +151,7 @@ namespace games::drs {
|
||||
char DRS_TAPELED[DRS_TAPELED_COLS_TOTAL][3] {};
|
||||
bool DISABLE_TOUCH = false;
|
||||
bool TRANSPOSE_TOUCH = false;
|
||||
bool RGB_CAMERA_HOOK = false;
|
||||
|
||||
std::vector<TouchEvent> TOUCH_EVENTS;
|
||||
|
||||
@@ -313,6 +315,10 @@ namespace games::drs {
|
||||
} else {
|
||||
log_info("drs", "no native input method detected");
|
||||
}
|
||||
|
||||
if (RGB_CAMERA_HOOK) {
|
||||
init_rgb_camera_hook();
|
||||
}
|
||||
}
|
||||
|
||||
void DRSGame::detach() {
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace games::drs {
|
||||
extern std::vector<TouchEvent> TOUCH_EVENTS;
|
||||
extern bool DISABLE_TOUCH;
|
||||
extern bool TRANSPOSE_TOUCH;
|
||||
extern bool RGB_CAMERA_HOOK;
|
||||
|
||||
void fire_touches(drs_touch_t *events, size_t event_count);
|
||||
void start_touch();
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
#include "games/drs/rgb_cam.h"
|
||||
|
||||
// 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 <mfobjects.h>
|
||||
#include <mfidl.h>
|
||||
#include <string>
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "cfg/configurator.h"
|
||||
#include "hooks/cfgmgr32hook.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/memutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
static VTBL_TYPE(IMFActivate, GetAllocatedString) GetAllocatedString_orig = nullptr;
|
||||
static decltype(MFEnumDeviceSources) *MFEnumDeviceSources_orig = nullptr;
|
||||
|
||||
namespace games::drs {
|
||||
|
||||
/*
|
||||
* Camera related stuff
|
||||
*/
|
||||
static std::wstring CAMERA0_ID;
|
||||
|
||||
static HRESULT WINAPI GetAllocatedString_hook(IMFActivate* This, REFGUID guidKey, LPWSTR *ppwszValue,
|
||||
UINT32 *pcchLength)
|
||||
{
|
||||
// call the original
|
||||
HRESULT result = GetAllocatedString_orig(This, guidKey, ppwszValue, pcchLength);
|
||||
|
||||
// log the cam name
|
||||
log_misc("drs::rgbcam", "obtained {}", ws2s(*ppwszValue));
|
||||
|
||||
// try first camera
|
||||
wchar_t *pwc = nullptr;
|
||||
if (CAMERA0_ID.length() == 23) {
|
||||
pwc = wcsstr(*ppwszValue, CAMERA0_ID.c_str());
|
||||
}
|
||||
|
||||
// check if camera could be identified
|
||||
if (pwc) {
|
||||
|
||||
// fake the USB IDs
|
||||
pwc[4] = L'2';
|
||||
pwc[5] = L'8';
|
||||
pwc[6] = L'8';
|
||||
pwc[7] = L'c';
|
||||
|
||||
pwc[13] = L'0';
|
||||
pwc[14] = L'0';
|
||||
pwc[15] = L'0';
|
||||
pwc[16] = L'2';
|
||||
|
||||
pwc[21] = L'0';
|
||||
pwc[22] = L'0';
|
||||
|
||||
log_misc("drs::rgbcam", "replaced {}", ws2s(*ppwszValue));
|
||||
}
|
||||
|
||||
// return original result
|
||||
return result;
|
||||
}
|
||||
|
||||
static void hook_camera(std::wstring camera_id, std::string camera_instance) {
|
||||
// don't hook if camera 0 is already hooked
|
||||
if (CAMERA0_ID.length() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// save the camera ID
|
||||
CAMERA0_ID = camera_id;
|
||||
|
||||
// cfgmgr hook
|
||||
CFGMGR32_HOOK_SETTING camera_setting;
|
||||
camera_setting.device_instance = 0xDEADBEEF;
|
||||
camera_setting.parent_instance = ~camera_setting.device_instance;
|
||||
camera_setting.device_id = "USB\\VEN_8086&DEV_8C2D";
|
||||
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("drs::rgbcam", "hooked camera @ {}", ws2s(camera_id));
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
// get camera
|
||||
IMFActivate *camera = **pppSourceActivate;
|
||||
|
||||
// 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("drs::rgbcam", "found video capture device: {}", ws2s(camera_link_ws));
|
||||
|
||||
// only support cameras that start with \\?\usb
|
||||
if (!string_begins_with(camera_link_ws, L"\\\\?\\usb")) {
|
||||
return result_orig;
|
||||
}
|
||||
|
||||
// get camera instance
|
||||
std::string camera_link = ws2s(camera_link_ws);
|
||||
std::string camera_instance = camera_link.substr(32, 17);
|
||||
|
||||
// hook the camera
|
||||
hook_camera(camera_id, camera_instance);
|
||||
} else {
|
||||
log_warning("drs::rgbcam", "failed to open camera");
|
||||
}
|
||||
|
||||
// return result
|
||||
return result_orig;
|
||||
}
|
||||
|
||||
void init_rgb_camera_hook() {
|
||||
log_info("drs::rgbcam", "initializing camera hook");
|
||||
|
||||
cfgmgr32hook_init(avs::game::DLL_INSTANCE);
|
||||
|
||||
// camera media framework hook
|
||||
MFEnumDeviceSources_orig = detour::iat_try(
|
||||
"MFEnumDeviceSources", MFEnumDeviceSources_hook, avs::game::DLL_INSTANCE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace games::drs {
|
||||
void init_rgb_camera_hook();
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "bi2x_hook.h"
|
||||
|
||||
#if SPICE64
|
||||
|
||||
#include <optional>
|
||||
#include <cstdint>
|
||||
#include "util/detour.h"
|
||||
@@ -18,24 +20,29 @@ namespace games::gitadora {
|
||||
// */
|
||||
|
||||
struct AIO_NMGR_IOB2 {
|
||||
uint8_t data[0xe18];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1__SETTING {
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB5 {
|
||||
uint8_t data[0xb10];
|
||||
};
|
||||
|
||||
struct AIO_SCI_COMM {
|
||||
uint8_t data[0x100];
|
||||
};
|
||||
|
||||
struct AIO_IOB5_Y32D {
|
||||
uint8_t data[0x2e8];
|
||||
};
|
||||
|
||||
// struct AIO_IOB5_Y33I {
|
||||
// };
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1 {
|
||||
uint8_t data[0x4570];
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB__NODEINFO {
|
||||
@@ -43,6 +50,7 @@ namespace games::gitadora {
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_WRFIRM {
|
||||
uint8_t data[0x20f48];
|
||||
};
|
||||
|
||||
struct AIO_SCI_COMM_T {
|
||||
@@ -159,6 +167,14 @@ namespace games::gitadora {
|
||||
uint8_t unk[0xB1];
|
||||
};
|
||||
|
||||
// verified with M32-007-2025092400
|
||||
static_assert(sizeof(AIO_NMGR_IOB2) == 0xe18);
|
||||
static_assert(sizeof(AIO_NMGR_IOB5) == 0xb10);
|
||||
static_assert(sizeof(AIO_SCI_COMM) == 0x100);
|
||||
static_assert(sizeof(AIO_IOB5_Y32D) == 0x2e8);
|
||||
static_assert(sizeof(AIO_IOB2_BI2X_AC1) == 0x4570);
|
||||
static_assert(sizeof(AIO_IOB2_BI2X_WRFIRM) == 0x20f48);
|
||||
|
||||
/*
|
||||
* typedefs
|
||||
*/
|
||||
@@ -701,4 +717,6 @@ namespace games::gitadora {
|
||||
detour::trampoline_try(libaioDll, "aioNodeCtl_UpdateDevicesStatus",
|
||||
aioNodeCtl_UpdateDevicesStatus, &aioNodeCtl_UpdateDevicesStatus_orig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#if SPICE64
|
||||
|
||||
namespace games::gitadora {
|
||||
void bi2x_hook_init();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "cfg/configurator.h"
|
||||
#include "hooks/audio/mme.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "misc/wintouchemu.h"
|
||||
#include "overlay/overlay.h"
|
||||
#include "util/cpuutils.h"
|
||||
#include "util/detour.h"
|
||||
@@ -19,8 +20,11 @@ namespace games::gitadora {
|
||||
// settings
|
||||
bool TWOCHANNEL = false;
|
||||
std::optional<unsigned int> CAB_TYPE = std::nullopt;
|
||||
bool ARENA_SINGLE_WINDOW = false;
|
||||
bool P1_LEFTY = false;
|
||||
bool P2_LEFTY = false;
|
||||
std::optional<std::string> SUBSCREEN_OVERLAY_SIZE;
|
||||
std::optional<socd::SocdAlgorithm> PICK_ALGO = socd::SocdAlgorithm::PreferRecent;
|
||||
|
||||
/*
|
||||
* Prevent GitaDora from creating folders on F drive
|
||||
@@ -224,8 +228,15 @@ namespace games::gitadora {
|
||||
overlay::UI_SCALE_PERCENT = 250;
|
||||
}
|
||||
|
||||
// for guitar wail SOCD cleaning
|
||||
// for guitar wail SOCD cleaning
|
||||
socd::ALGORITHM = socd::SocdAlgorithm::PreferRecent;
|
||||
|
||||
// for guitar picking
|
||||
if (PICK_ALGO.has_value()) {
|
||||
log_info("gitadora", "guitar pick SOCD algorithm: {}", static_cast<int>(PICK_ALGO.value()));
|
||||
} else {
|
||||
log_info("gitadora", "guitar pick SOCD algorithm: legacy");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,6 +296,13 @@ namespace games::gitadora {
|
||||
|
||||
// volume change prevention
|
||||
hooks::audio::mme::init(avs::game::DLL_INSTANCE);
|
||||
|
||||
// touch hook
|
||||
if (ARENA_SINGLE_WINDOW) {
|
||||
wintouchemu::FORCE = true;
|
||||
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
|
||||
wintouchemu::hook("GITADORA", avs::game::DLL_INSTANCE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,14 +4,18 @@
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/game.h"
|
||||
#include "util/socd_cleaner.h"
|
||||
|
||||
namespace games::gitadora {
|
||||
|
||||
// settings
|
||||
extern bool TWOCHANNEL;
|
||||
extern std::optional<unsigned int> CAB_TYPE;
|
||||
extern bool ARENA_SINGLE_WINDOW;
|
||||
extern bool P1_LEFTY;
|
||||
extern bool P2_LEFTY;
|
||||
extern std::optional<std::string> SUBSCREEN_OVERLAY_SIZE;
|
||||
extern std::optional<socd::SocdAlgorithm> PICK_ALGO;
|
||||
|
||||
class GitaDoraGame : public games::Game {
|
||||
public:
|
||||
|
||||
@@ -65,17 +65,18 @@ std::vector<Button> &games::gitadora::get_buttons() {
|
||||
"Drum Help",
|
||||
"Drum Button Extra 1",
|
||||
"Drum Button Extra 2",
|
||||
|
||||
"Drum Left Cymbal",
|
||||
"Drum Hi-Hat",
|
||||
"Drum Hi-Hat Closed",
|
||||
"Drum Hi-Hat Half-Open",
|
||||
"Drum Left Pedal",
|
||||
"Drum Snare",
|
||||
"Drum Hi-Tom",
|
||||
"Drum Low-Tom",
|
||||
"Drum Right Cymbal",
|
||||
"Drum Bass Pedal",
|
||||
"Drum Left Cymbal",
|
||||
"Drum Left Pedal",
|
||||
"Drum Floor Tom"
|
||||
"Drum Low-Tom",
|
||||
"Drum Floor Tom",
|
||||
"Drum Right Cymbal",
|
||||
"Drum Hi-Hat Closed",
|
||||
"Drum Hi-Hat Half-Open"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -110,11 +111,11 @@ std::string games::gitadora::get_buttons_help() {
|
||||
"\n"
|
||||
" < R G B Y P --- Pick ] \n"
|
||||
"\n"
|
||||
" If you hold your guitar left-handed (frets on the right hand):\n"
|
||||
" * bind the buttons here as righty and use the in-game option\n"
|
||||
" to turn on LEFT mode to flip the order of buttons.\n"
|
||||
" * turn on GitaDora Lefty Guitar option in Options tab so that\n"
|
||||
" wailing is recognized correctly.\n"
|
||||
" If you normally hold your guitar left-handed (frets on right hand):\n"
|
||||
" * here, hold the guitar as right-handed (frets on left hand),\n"
|
||||
" and bind the buttons, pick, and wail controls\n"
|
||||
" * in Options tab, turn on GitaDora Lefty Guitar\n"
|
||||
" * finally, use the in-game option to turn on LEFT mode\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"drums:\n"
|
||||
@@ -126,8 +127,6 @@ std::string games::gitadora::get_buttons_help() {
|
||||
" Left Bass\n"
|
||||
" Pedal Pedal\n"
|
||||
"\n"
|
||||
" Drums are NOT velocity-sensitive!\n"
|
||||
"\n"
|
||||
" For MIDI drums with Open/Closed HiHat configurations or pads with\n"
|
||||
" multiple hit zones, ensure you bind all variation using the Pages\n"
|
||||
" button at the bottom."
|
||||
@@ -138,7 +137,7 @@ std::string games::gitadora::get_analogs_help() {
|
||||
return
|
||||
"guitar:\n"
|
||||
"\n"
|
||||
"X axis: 0% when body is held facing monitor, 50% when facing flat on a table\n"
|
||||
"X axis: 0% when body is held facing monitor, 50% when flat on a table\n"
|
||||
"Y axis: 50% when held horizontal, 0% when neck is raised, 100% pointing down\n"
|
||||
"Z axis: 50% at rest, 100% when swinging neck toward monitor (only used in XG series)\n"
|
||||
"\n"
|
||||
|
||||
@@ -66,18 +66,21 @@ namespace games::gitadora {
|
||||
DrumHelp,
|
||||
DrumButtonExtra1,
|
||||
DrumButtonExtra2,
|
||||
// note: this is the order they appear in the test menu
|
||||
// note: this is the order of drums in the default layout
|
||||
DrumLeftCymbal,
|
||||
DrumHiHat,
|
||||
DrumHiHatClosed,
|
||||
DrumHiHatHalfOpen,
|
||||
DrumLeftPedal,
|
||||
DrumSnare,
|
||||
DrumHiTom,
|
||||
DrumLowTom,
|
||||
DrumRightCymbal,
|
||||
DrumBassPedal,
|
||||
DrumLeftCymbal,
|
||||
DrumLeftPedal,
|
||||
DrumFloorTom
|
||||
DrumLowTom,
|
||||
DrumFloorTom,
|
||||
DrumRightCymbal,
|
||||
|
||||
// these only exist for back compat
|
||||
// (created before it was possible to map multiple input to a button)
|
||||
DrumHiHatClosed,
|
||||
DrumHiHatHalfOpen
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -92,22 +92,44 @@ bool games::gitadora::J33ISerialDevice::parse_msg(
|
||||
payload.buttons |= 1 << GUITAR_BTN_P;
|
||||
}
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1PickUp])) {
|
||||
if (!GFDM_GF_PICK_STATE_UP) {
|
||||
GFDM_GF_PICK_STATE_UP = true;
|
||||
payload.buttons |= 1 << GUITAR_PICK_UP;
|
||||
|
||||
const auto pick_up = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1PickUp]);
|
||||
const auto pick_down = GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1PickDown]);
|
||||
if (!games::gitadora::PICK_ALGO.has_value()) {
|
||||
// legacy behavior (detect rising edges only, input for 1 frame)
|
||||
if (pick_up) {
|
||||
if (!GFDM_GF_PICK_STATE_UP) {
|
||||
GFDM_GF_PICK_STATE_UP = true;
|
||||
payload.buttons |= 1 << GUITAR_PICK_UP;
|
||||
}
|
||||
} else {
|
||||
GFDM_GF_PICK_STATE_UP = false;
|
||||
}
|
||||
|
||||
if (pick_down) {
|
||||
if (!GFDM_GF_PICK_STATE_DOWN) {
|
||||
GFDM_GF_PICK_STATE_DOWN = true;
|
||||
payload.buttons |= 1 << GUITAR_PICK_DOWN;
|
||||
}
|
||||
} else {
|
||||
GFDM_GF_PICK_STATE_DOWN = false;
|
||||
}
|
||||
} else {
|
||||
GFDM_GF_PICK_STATE_UP = false;
|
||||
}
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::GuitarP1PickDown])) {
|
||||
if (!GFDM_GF_PICK_STATE_DOWN) {
|
||||
GFDM_GF_PICK_STATE_DOWN = true;
|
||||
const auto socd = socd::socd_clean(
|
||||
0,
|
||||
pick_up,
|
||||
pick_down,
|
||||
get_performance_milliseconds(),
|
||||
games::gitadora::PICK_ALGO.value()
|
||||
);
|
||||
if (socd == socd::SocdCCW) {
|
||||
payload.buttons |= 1 << GUITAR_PICK_UP;
|
||||
} else if (socd == socd::SocdCW) {
|
||||
payload.buttons |= 1 << GUITAR_PICK_DOWN;
|
||||
} else if (socd == socd::SocdBoth) {
|
||||
payload.buttons |= 1 << GUITAR_PICK_UP;
|
||||
payload.buttons |= 1 << GUITAR_PICK_DOWN;
|
||||
}
|
||||
} else {
|
||||
GFDM_GF_PICK_STATE_DOWN = false;
|
||||
}
|
||||
|
||||
auto &analogs = get_analogs();
|
||||
|
||||
@@ -82,6 +82,7 @@ namespace games::iidx {
|
||||
bool IIDXIO_LED_TICKER_READONLY = false;
|
||||
std::mutex IIDX_LED_TICKER_LOCK;
|
||||
bool IIDX_TDJ_MONITOR_WARNING = false;
|
||||
iidx_aio_emulation_state CURRENT_IO_EMULATION_STATE = iidx_aio_emulation_state::unknown;
|
||||
|
||||
// io
|
||||
static bool HAS_LIBAIO; // this is how we detect iidx27+
|
||||
@@ -453,7 +454,7 @@ namespace games::iidx {
|
||||
}
|
||||
|
||||
// windowed subscreen, enabled by default, unless turned off by user
|
||||
if (GRAPHICS_WINDOWED && !options->at(launcher::Options::spice2x_IIDXNoSub).value_bool()) {
|
||||
if (GRAPHICS_WINDOWED && !options->at(launcher::Options::spice2x_IIDXNoSub).value_bool() && TDJ_MODE) {
|
||||
GRAPHICS_IIDX_WSUB = true;
|
||||
}
|
||||
|
||||
@@ -934,6 +935,8 @@ namespace games::iidx {
|
||||
return;
|
||||
}
|
||||
|
||||
CURRENT_IO_EMULATION_STATE = state;
|
||||
|
||||
// log_warning below could be turned into log_fatal in the future once we gain confidence
|
||||
// that this isn't triggering when it's not supposed to
|
||||
if (state == iidx_aio_emulation_state::bi2a_com2 && TDJ_MODE) {
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
|
||||
namespace games::iidx {
|
||||
|
||||
enum class iidx_aio_emulation_state {
|
||||
unknown,
|
||||
bi2a_com2,
|
||||
bi2x_hook
|
||||
};
|
||||
|
||||
// settings
|
||||
|
||||
extern bool FLIP_CAMS;
|
||||
@@ -38,6 +44,7 @@ namespace games::iidx {
|
||||
constexpr int IIDX_TAPELED_TOTAL = 17;
|
||||
// data mapping
|
||||
extern tapeledutils::tape_led TAPELED_MAPPING[IIDX_TAPELED_TOTAL];
|
||||
extern iidx_aio_emulation_state CURRENT_IO_EMULATION_STATE;
|
||||
|
||||
class IIDXGame : public games::Game {
|
||||
public:
|
||||
@@ -64,9 +71,5 @@ namespace games::iidx {
|
||||
bool is_tdj_fhd();
|
||||
void apply_audio_hacks();
|
||||
|
||||
enum class iidx_aio_emulation_state {
|
||||
bi2a_com2,
|
||||
bi2x_hook
|
||||
};
|
||||
void update_io_emulation_state(iidx_aio_emulation_state state);
|
||||
}
|
||||
|
||||
@@ -324,7 +324,7 @@ namespace games {
|
||||
buttons_help.insert({ pc, pc::get_buttons_help() });
|
||||
analogs.insert({ pc, pc::get_analogs() });
|
||||
lights.insert({ pc, pc::get_lights() });
|
||||
file_hints[pc].emplace_back("game/svm_Data");
|
||||
file_hints[pc].emplace_back("game/svm.exe");
|
||||
}
|
||||
|
||||
const std::vector<std::string> &get_games() {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "bi2x_hook.h"
|
||||
|
||||
#if SPICE64
|
||||
|
||||
#include "util/execexe.h"
|
||||
#include "util/logging.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
@@ -9,9 +11,10 @@
|
||||
#include "util/tapeled.h"
|
||||
#include "pc.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/time.h"
|
||||
#include "util/socd_cleaner.h"
|
||||
|
||||
namespace games::pc {
|
||||
bool PC_KNOB_MODE = false;
|
||||
|
||||
constexpr float RELATIVE_DECAY = 0.02f;
|
||||
struct FADER_RELATIVE {
|
||||
@@ -75,18 +78,22 @@ namespace games::pc {
|
||||
*/
|
||||
|
||||
struct AIO_SCI_COMM {
|
||||
uint8_t data[0xf8];
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB2 {
|
||||
uint8_t data[0xe30];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1 {
|
||||
uint8_t data[0x4538];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1__SETTING {
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_WRFIRM {
|
||||
uint8_t data[0x20f48];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1__INPUT
|
||||
@@ -191,6 +198,12 @@ namespace games::pc {
|
||||
AIO_IOB2_BI2X_AC1__ICNPIN ICnPinHist[20];
|
||||
};
|
||||
|
||||
// verified with XIF-2024122300
|
||||
static_assert(sizeof(AIO_SCI_COMM) == 0xf8);
|
||||
static_assert(sizeof(AIO_NMGR_IOB2) == 0xe30);
|
||||
static_assert(sizeof(AIO_IOB2_BI2X_AC1) == 0x4538);
|
||||
static_assert(sizeof(AIO_IOB2_BI2X_WRFIRM) == 0x20f48);
|
||||
|
||||
/*
|
||||
* typedefs
|
||||
*/
|
||||
@@ -338,14 +351,21 @@ namespace games::pc {
|
||||
val = (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderL]) - 0.5f) * 2;
|
||||
}
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Left]) &&
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Right])) {
|
||||
val = 0.f;
|
||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Left])) {
|
||||
|
||||
const auto now = get_performance_milliseconds();
|
||||
|
||||
const auto fader_l_socd = socd::socd_clean(
|
||||
0, // Fader-L
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Left]),
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Right]),
|
||||
now);
|
||||
|
||||
if (fader_l_socd == socd::SocdCCW) {
|
||||
val = -1.0f;
|
||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Right])) {
|
||||
} else if (fader_l_socd == socd::SocdCW) {
|
||||
val = +1.0f;
|
||||
}
|
||||
|
||||
o_DevStatus->Input.CN15_7 = (val < 0.2f) ? 0xFF : 0;
|
||||
o_DevStatus->Input.CN15_6 = (val > -0.85f && val < 0.35f) ? 0xFF : 0;
|
||||
o_DevStatus->Input.CN15_5 = (val > -0.6f && val < 0.6f) ? 0xFF : 0;
|
||||
@@ -363,14 +383,19 @@ namespace games::pc {
|
||||
val = (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderR]) - 0.5f) * 2;
|
||||
}
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Left]) &&
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Right])) {
|
||||
val = 0.f;
|
||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Left])) {
|
||||
|
||||
const auto fader_r_socd = socd::socd_clean(
|
||||
1, // Fader-R
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Left]),
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Right]),
|
||||
now);
|
||||
|
||||
if (fader_r_socd == socd::SocdCCW) {
|
||||
val = -1.0f;
|
||||
} else if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Right])) {
|
||||
} else if (fader_r_socd == socd::SocdCW) {
|
||||
val = +1.0f;
|
||||
}
|
||||
|
||||
o_DevStatus->Input.CN11_20 = (val < 0.2f) ? 0xFF : 0;
|
||||
o_DevStatus->Input.CN11_19 = (val > -0.85f && val < 0.35f) ? 0xFF : 0;
|
||||
o_DevStatus->Input.CN9_10 = (val > -0.6f && val < 0.6f) ? 0xFF : 0;
|
||||
@@ -737,3 +762,5 @@ namespace games::pc {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#if SPICE64
|
||||
|
||||
namespace games::pc {
|
||||
void bi2x_hook_init();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -12,10 +12,12 @@
|
||||
#include "misc/wintouchemu.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/socd_cleaner.h"
|
||||
|
||||
namespace games::pc {
|
||||
std::string PC_INJECT_ARGS = "";
|
||||
bool PC_NO_IO = false;
|
||||
bool PC_KNOB_MODE = false;
|
||||
|
||||
static acioemu::ACIOHandle *acioHandle = nullptr;
|
||||
static std::wstring portName = L"COM1";
|
||||
@@ -61,6 +63,11 @@ namespace games::pc {
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
void PCGame::pre_attach() {
|
||||
// SOCD
|
||||
socd::ALGORITHM = socd::SocdAlgorithm::PreferRecent;
|
||||
}
|
||||
|
||||
void PCGame::attach() {
|
||||
Game::attach();
|
||||
|
||||
@@ -78,9 +85,14 @@ namespace games::pc {
|
||||
execexe::load_library("libaio-iob2_video.dll");
|
||||
execexe::load_library("win10actlog.dll");
|
||||
|
||||
#if SPICE64
|
||||
|
||||
if (!PC_NO_IO) {
|
||||
bi2x_hook_init();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
});
|
||||
|
||||
const auto user32Dll = "user32.dll";
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace games::pc {
|
||||
public:
|
||||
PCGame() : Game("Polaris Chord") {}
|
||||
|
||||
virtual void pre_attach() override;
|
||||
virtual void attach() override;
|
||||
virtual void detach() override;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "bi2x_hook.h"
|
||||
|
||||
#if SPICE64
|
||||
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
@@ -15,15 +17,19 @@ namespace games::qks {
|
||||
*/
|
||||
|
||||
struct AIO_SCI_COMM {
|
||||
uint8_t data[0xf8];
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB {
|
||||
uint8_t data[0xa00];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_AC1 {
|
||||
uint8_t data[0x3908];
|
||||
};
|
||||
|
||||
struct AIO_IOB2_BI2X_WRFIRM {
|
||||
uint8_t data[0x20f48];
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB__NODEINFO {
|
||||
@@ -174,6 +180,12 @@ namespace games::qks {
|
||||
AIO_IOB2_BI2X_AC1__SETTING Setting;
|
||||
};
|
||||
|
||||
// verified with UKS-001-2024030600
|
||||
static_assert(sizeof(AIO_IOB2_BI2X_AC1) == 0x3908);
|
||||
static_assert(sizeof(AIO_SCI_COMM) == 0xf8);
|
||||
static_assert(sizeof(AIO_NMGR_IOB) == 0xa00);
|
||||
static_assert(sizeof(AIO_IOB2_BI2X_WRFIRM) == 0x20f48);
|
||||
|
||||
/*
|
||||
* typedefs
|
||||
*/
|
||||
@@ -602,4 +614,6 @@ namespace games::qks {
|
||||
detour::trampoline_try(libaioDll, "aioNodeCtl_IsError",
|
||||
aioNodeCtl_IsError, &aioNodeCtl_IsError_orig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#if SPICE64
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace games::qks {
|
||||
void bi2x_hook_init();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -86,9 +86,13 @@ namespace games::qks {
|
||||
detour::trampoline_try("execexe.dll", MAKEINTRESOURCE(9),
|
||||
execexe_CreateFileA_hook,&execexe_CreateFileA_orig);
|
||||
|
||||
#if SPICE64
|
||||
|
||||
// insert BI2X hooks
|
||||
bi2x_hook_init();
|
||||
|
||||
#endif
|
||||
|
||||
// add card reader
|
||||
acioHandle = new acioemu::ACIOHandle(portName.c_str());
|
||||
devicehook_init_trampoline();
|
||||
|
||||
@@ -21,8 +21,9 @@
|
||||
#include "util/socd_cleaner.h"
|
||||
#include "util/time.h"
|
||||
#include "util/libutils.h"
|
||||
#include "misc/wintouchemu.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "misc/nativetouchhook.h"
|
||||
#include "misc/wintouchemu.h"
|
||||
#include "bi2x_hook.h"
|
||||
#include "camera.h"
|
||||
#include "io.h"
|
||||
@@ -65,10 +66,6 @@ namespace games::sdvx {
|
||||
|
||||
// prevent the game from randomly picking an ASIO device
|
||||
if (!USE_ASIO) {
|
||||
log_info(
|
||||
"sdvx::asio",
|
||||
"game tried to access HKLM\\SOFTWARE\\ASIO but ASIO is disabled; "
|
||||
"access prevented, ASIO init will fail");
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
// convince the game to use our hook (so we can swap in the preferred device)
|
||||
@@ -420,9 +417,11 @@ namespace games::sdvx {
|
||||
}
|
||||
|
||||
if (is_valkyrie_model()) {
|
||||
// hook touch window
|
||||
// in windowed mode, game can accept mouse input on the second screen
|
||||
if (!NATIVETOUCH && !GRAPHICS_WINDOWED) {
|
||||
if (NATIVETOUCH) {
|
||||
nativetouchhook::hook(avs::game::DLL_INSTANCE);
|
||||
} else if (!NATIVETOUCH && !GRAPHICS_WINDOWED) {
|
||||
// hook touch window
|
||||
// in windowed mode, game can accept mouse input on the second screen
|
||||
wintouchemu::FORCE = true;
|
||||
wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true;
|
||||
wintouchemu::hook_title_ends(
|
||||
|
||||
@@ -42,7 +42,8 @@ namespace hooks::audio {
|
||||
bool USE_DUMMY = false;
|
||||
WAVEFORMATEXTENSIBLE FORMAT {};
|
||||
std::optional<Backend> BACKEND = std::nullopt;
|
||||
size_t ASIO_DRIVER_ID = 0;
|
||||
std::optional<size_t> ASIO_DRIVER_ID = std::nullopt;
|
||||
std::string ASIO_DRIVER_NAME = "";
|
||||
bool ASIO_FORCE_UNLOAD_ON_STOP = false;
|
||||
|
||||
// private globals
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmreg.h>
|
||||
@@ -20,7 +21,8 @@ namespace hooks::audio {
|
||||
extern bool USE_DUMMY;
|
||||
extern WAVEFORMATEXTENSIBLE FORMAT;
|
||||
extern std::optional<Backend> BACKEND;
|
||||
extern size_t ASIO_DRIVER_ID;
|
||||
extern std::optional<size_t> ASIO_DRIVER_ID;
|
||||
extern std::string ASIO_DRIVER_NAME;
|
||||
extern bool ASIO_FORCE_UNLOAD_ON_STOP;
|
||||
extern bool LOW_LATENCY_SHARED_WASAPI;
|
||||
|
||||
|
||||
@@ -218,13 +218,40 @@ void AsioBackend::set_thread_state(AsioThreadState state) {
|
||||
bool AsioBackend::load_driver() {
|
||||
AsioDriverList asio_driver_list;
|
||||
|
||||
for (const auto &driver : asio_driver_list.driver_list) {
|
||||
log_info("audio::asio", "Driver {}", driver.id);
|
||||
log_info("audio::asio", "... Name : {}", driver.name);
|
||||
log_info("audio::asio", "... Path : {}", driver.dll_path);
|
||||
size_t driver_id = 0;
|
||||
std::string driver_name = "";
|
||||
if (hooks::audio::ASIO_DRIVER_ID.has_value()) {
|
||||
driver_id = hooks::audio::ASIO_DRIVER_ID.value();
|
||||
}
|
||||
|
||||
const auto driver_id = hooks::audio::ASIO_DRIVER_ID;
|
||||
for (const auto &driver : asio_driver_list.driver_list) {
|
||||
log_info("audio::asio", " Driver ID : {}", driver.id);
|
||||
log_info("audio::asio", " ... Name : {}", driver.name);
|
||||
log_info("audio::asio", " ... Path : {}", driver.dll_path);
|
||||
|
||||
if (!hooks::audio::ASIO_DRIVER_NAME.empty()) {
|
||||
// match on driver name
|
||||
if (driver.name == hooks::audio::ASIO_DRIVER_NAME) {
|
||||
driver_id = driver.id;
|
||||
driver_name = driver.name;
|
||||
}
|
||||
} else if (driver.id == driver_id) {
|
||||
// match on driver index (0 for default or user-specified)
|
||||
driver_name = driver.name;
|
||||
}
|
||||
}
|
||||
|
||||
if (driver_name.empty()) {
|
||||
if (!hooks::audio::ASIO_DRIVER_NAME.empty()) {
|
||||
log_warning(
|
||||
"audio::asio",
|
||||
"tried to look for ASIO driver but failed: {}", hooks::audio::ASIO_DRIVER_NAME);
|
||||
}
|
||||
log_fatal(
|
||||
"audio::asio",
|
||||
"could not find ASIO driver to use for conversion, fix your audio options and try again");
|
||||
}
|
||||
log_info("audio::asio", "will attempt to use the following ASIO driver: ID = {}, Name = {}", driver_id, driver_name);
|
||||
|
||||
IAsio *driver = nullptr;
|
||||
auto ret = asio_driver_list.open_driver(driver_id, reinterpret_cast<void **>(&driver));
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "cfg/screen_resize.h"
|
||||
#include "games/gitadora/gitadora.h"
|
||||
#include "games/iidx/iidx.h"
|
||||
#include "games/sdvx/sdvx.h"
|
||||
#include "games/mfc/mfc.h"
|
||||
@@ -944,26 +945,37 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
||||
|
||||
for (size_t i = 0; i < num_adapters; i++) {
|
||||
auto *params = &pPresentationParameters[i];
|
||||
if (!GRAPHICS_WINDOWED && i == 0) {
|
||||
GRAPHICS_FS_ORIGINAL_WIDTH = params->BackBufferWidth;
|
||||
GRAPHICS_FS_ORIGINAL_HEIGHT = params->BackBufferHeight;
|
||||
log_misc("graphics::d3d9", "original resolution: {}x{}", GRAPHICS_FS_ORIGINAL_WIDTH, GRAPHICS_FS_ORIGINAL_HEIGHT);
|
||||
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
||||
if (!GRAPHICS_WINDOWED){
|
||||
if (i == 0) {
|
||||
GRAPHICS_FS_ORIGINAL_WIDTH = params->BackBufferWidth;
|
||||
GRAPHICS_FS_ORIGINAL_HEIGHT = params->BackBufferHeight;
|
||||
log_misc("graphics::d3d9", "original resolution: {}x{}", GRAPHICS_FS_ORIGINAL_WIDTH, GRAPHICS_FS_ORIGINAL_HEIGHT);
|
||||
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
||||
log_misc(
|
||||
"graphics::d3d9",
|
||||
"use custom resolution {}x{} => {}x{}",
|
||||
params->BackBufferWidth, params->BackBufferHeight,
|
||||
GRAPHICS_FS_CUSTOM_RESOLUTION.value().first,
|
||||
GRAPHICS_FS_CUSTOM_RESOLUTION.value().second);
|
||||
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
||||
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
||||
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||
log_misc(
|
||||
"graphics::d3d9",
|
||||
"swap full orientation {}x{} => {}x{}",
|
||||
params->BackBufferWidth, params->BackBufferHeight,
|
||||
params->BackBufferHeight, params->BackBufferWidth);
|
||||
std::swap(params->BackBufferWidth, params->BackBufferHeight);
|
||||
}
|
||||
} else if (i == 1 && GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.has_value()) {
|
||||
log_misc(
|
||||
"graphics::d3d9",
|
||||
"use custom resolution {}x{} => {}x{}",
|
||||
"use custom sub resolution {}x{} => {}x{}",
|
||||
params->BackBufferWidth, params->BackBufferHeight,
|
||||
GRAPHICS_FS_CUSTOM_RESOLUTION.value().first,
|
||||
GRAPHICS_FS_CUSTOM_RESOLUTION.value().second);
|
||||
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
||||
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
||||
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||
log_misc(
|
||||
"graphics::d3d9",
|
||||
"swap full orientation {}x{} => {}x{}",
|
||||
params->BackBufferWidth, params->BackBufferHeight,
|
||||
params->BackBufferHeight, params->BackBufferWidth);
|
||||
std::swap(params->BackBufferWidth, params->BackBufferHeight);
|
||||
GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().first,
|
||||
GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().second);
|
||||
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().first;
|
||||
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().second;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -991,12 +1003,17 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
||||
for (size_t i = 0; i < num_adapters; i++) {
|
||||
auto *fullscreen_display_mode = &pFullscreenDisplayMode[i];
|
||||
|
||||
if (!GRAPHICS_WINDOWED && i == 0) {
|
||||
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
||||
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
||||
fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
||||
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||
std::swap(fullscreen_display_mode->Width, fullscreen_display_mode->Height);
|
||||
if (!GRAPHICS_WINDOWED) {
|
||||
if (i == 0) {
|
||||
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
||||
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
||||
fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
||||
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||
std::swap(fullscreen_display_mode->Width, fullscreen_display_mode->Height);
|
||||
}
|
||||
} else if (i == 1 && GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.has_value()) {
|
||||
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().first;
|
||||
fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().second;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1170,9 +1187,15 @@ IDirect3DSurface9 *graphics_d3d9_ldj_get_sub_screen() {
|
||||
}
|
||||
|
||||
static void graphics_d3d9_ldj_on_present(IDirect3DDevice9 *wrapped_device) {
|
||||
// iidx/sdvx
|
||||
int swapchain = 1;
|
||||
if (games::gitadora::is_arena_model()) {
|
||||
swapchain = 3;
|
||||
}
|
||||
|
||||
if (!ATTEMPTED_SUB_SWAP_CHAIN_ACQUIRE && SUB_SWAP_CHAIN == nullptr) {
|
||||
ATTEMPTED_SUB_SWAP_CHAIN_ACQUIRE = true;
|
||||
HRESULT hr = wrapped_device->GetSwapChain(1, &SUB_SWAP_CHAIN);
|
||||
HRESULT hr = wrapped_device->GetSwapChain(swapchain, &SUB_SWAP_CHAIN);
|
||||
if (FAILED(hr)) {
|
||||
log_warning(
|
||||
"graphics::d3d9",
|
||||
@@ -1383,7 +1406,9 @@ void graphics_d3d9_on_present(
|
||||
// for IIDX TDJ / SDVX UFC, handle subscreen
|
||||
const bool is_vm = games::sdvx::is_valkyrie_model();
|
||||
const bool is_tdj = avs::game::is_model("LDJ") && games::iidx::TDJ_MODE;
|
||||
if (is_vm || is_tdj) {
|
||||
const bool is_gfdm_arena =
|
||||
games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW;
|
||||
if (is_vm || is_tdj || is_gfdm_arena) {
|
||||
graphics_d3d9_ldj_on_present(wrapped_device);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <mutex>
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/gitadora/gitadora.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "overlay/overlay.h"
|
||||
#include "util/flags_helper.h"
|
||||
@@ -249,8 +250,16 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateAdditionalSwapChain(
|
||||
WRAP_VERBOSE;
|
||||
|
||||
HRESULT hr = pReal->CreateAdditionalSwapChain(pPresentationParameters, ppSwapChain);
|
||||
|
||||
bool create_swap_chain = false;
|
||||
if (avs::game::is_model({"LDJ", "KFC"})) {
|
||||
create_swap_chain = true;
|
||||
} else if (games::gitadora::is_arena_model() &&
|
||||
games::gitadora::ARENA_SINGLE_WINDOW &&
|
||||
pPresentationParameters->BackBufferWidth == 800) {
|
||||
create_swap_chain = true;
|
||||
}
|
||||
|
||||
if (create_swap_chain) {
|
||||
if (SUCCEEDED(hr) && !sub_swapchain) {
|
||||
sub_swapchain = new WrappedIDirect3DSwapChain9(this, *ppSwapChain);
|
||||
sub_swapchain->should_run_hooks = false;
|
||||
@@ -295,7 +304,17 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetSwapChain(
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
bool swap = false;
|
||||
if (iSwapChain == 1 && avs::game::is_model({"LDJ", "KFC"})) {
|
||||
swap = true;
|
||||
}
|
||||
if (games::gitadora::is_arena_model() &&
|
||||
games::gitadora::ARENA_SINGLE_WINDOW &&
|
||||
iSwapChain == 3) {
|
||||
swap = true;
|
||||
}
|
||||
|
||||
if (swap) {
|
||||
if (sub_swapchain) {
|
||||
sub_swapchain->AddRef();
|
||||
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(sub_swapchain);
|
||||
|
||||
@@ -61,7 +61,6 @@ bool GRAPHICS_SHOW_CURSOR = false;
|
||||
graphics_orientation GRAPHICS_ADJUST_ORIENTATION = ORIENTATION_NORMAL;
|
||||
bool GRAPHICS_WINDOWED = false;
|
||||
std::vector<HWND> GRAPHICS_WINDOWS;
|
||||
std::optional<HWND> GRAPHICS_WINDOW_MAIN;
|
||||
UINT GRAPHICS_FORCE_REFRESH = 0;
|
||||
std::optional<uint32_t> GRAPHICS_FORCE_REFRESH_SUB;
|
||||
std::optional<int> GRAPHICS_FORCE_VSYNC_BUFFER;
|
||||
@@ -72,6 +71,7 @@ bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME = false;
|
||||
bool SUBSCREEN_FORCE_REDRAW = false;
|
||||
bool D3D9_DEVICE_HOOK_DISABLE = false;
|
||||
std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
|
||||
std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION_SUB;
|
||||
bool GRAPHICS_FS_ORIENTATION_SWAP = false;
|
||||
uint32_t GRAPHICS_FS_ORIGINAL_WIDTH = 0;
|
||||
uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT = 0;
|
||||
@@ -98,6 +98,7 @@ static decltype(SetCursor) *SetCursor_orig = nullptr;
|
||||
static decltype(SetWindowLongA) *SetWindowLongA_orig = nullptr;
|
||||
static decltype(SetWindowLongW) *SetWindowLongW_orig = nullptr;
|
||||
static decltype(SetWindowPos) *SetWindowPos_orig = nullptr;
|
||||
static decltype(ShowWindow) *ShowWindow_orig = nullptr;
|
||||
|
||||
static void reset_window_hook(HWND hWnd) {
|
||||
overlay::destroy(hWnd);
|
||||
@@ -300,6 +301,30 @@ static BOOL WINAPI ClipCursor_hook(const RECT *lpRect) {
|
||||
return ClipCursor_orig(lpRect);
|
||||
}
|
||||
|
||||
static void fix_up_rb_auto_rotate_dimensions(DWORD &w, DWORD &h) {
|
||||
// reflec beat
|
||||
// for whatever reason, rb creates the first window it weird dimensions (774x1389 for reflesia, for example)
|
||||
// so doing ChangeDisplaySettings as-is ends up with DISP_CHANGE_BADMODE
|
||||
// while a second window (with title D3DProxyWindow) does get created with correct dimensions, it's
|
||||
// too late to rotate the window then; as a workaround force the display change now with patched up dimensions
|
||||
|
||||
bool changed = false;
|
||||
if (w == 774) {
|
||||
// LBR, MBR reflesia
|
||||
w = 768;
|
||||
h = 1360;
|
||||
changed = true;
|
||||
} else if (w == 1086) {
|
||||
// MBR (before reflesia)
|
||||
w = 1080;
|
||||
h = 1920;
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
log_misc("graphics", "fix auto-rotate dimensions for reflec beat: w={}, h={}", w, h);
|
||||
}
|
||||
}
|
||||
|
||||
static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName,
|
||||
DWORD dwStyle, int x, int y, int nWidth, int nHeight,
|
||||
HWND hWndParent, HMENU hMenu, HINSTANCE hInstance,
|
||||
@@ -324,10 +349,19 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
||||
// set display orientation and/or refresh rate
|
||||
// only set orientation when the target window is portrait
|
||||
// (avoid doing this for SDVX subscreen which is in landscape, for example)
|
||||
const auto adjust_orientation =
|
||||
auto adjust_orientation =
|
||||
(GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CW ||
|
||||
GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CCW);
|
||||
|
||||
// reflec beat
|
||||
if (avs::game::is_model("KBR")) {
|
||||
// KBR (rb1) launches landscape and draws rotated by itself, don't do any rotation
|
||||
adjust_orientation = false;
|
||||
} else if (avs::game::is_model({"LBR", "MBR"}) && window_name == "D3DProxyWindow") {
|
||||
// do not auto-rotate for second window (D3DProxyWindow)
|
||||
adjust_orientation = false;
|
||||
}
|
||||
|
||||
if ((nHeight > nWidth && adjust_orientation) || GRAPHICS_FORCE_REFRESH > 0) {
|
||||
DEVMODE mode {};
|
||||
|
||||
@@ -337,11 +371,16 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
||||
DWORD orientation = GRAPHICS_ADJUST_ORIENTATION == ORIENTATION_CW ? DMDO_90 : DMDO_270;
|
||||
log_misc(
|
||||
"graphics",
|
||||
"auto-rotate: call ChangeDisplaySettings and rotate display to DMDO_xx mode {}",
|
||||
orientation);
|
||||
"auto-rotate: call ChangeDisplaySettings and rotate display to DMDO_xx mode {}, w={}, h={}",
|
||||
orientation, nWidth, nHeight);
|
||||
mode.dmPelsWidth = nWidth;
|
||||
mode.dmPelsHeight = nHeight;
|
||||
mode.dmDisplayOrientation = orientation;
|
||||
|
||||
// reflec beat
|
||||
if (avs::game::is_model({"LBR", "MBR"})) {
|
||||
fix_up_rb_auto_rotate_dimensions(mode.dmPelsWidth, mode.dmPelsHeight);
|
||||
}
|
||||
}
|
||||
|
||||
if (GRAPHICS_FORCE_REFRESH > 0) {
|
||||
@@ -356,7 +395,7 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
||||
|
||||
const auto disp_res = ChangeDisplaySettings(&mode, CDS_FULLSCREEN);
|
||||
if (disp_res != DISP_CHANGE_SUCCESSFUL) {
|
||||
log_warning("graphics", "failed to change display settings: {}", disp_res);
|
||||
log_warning("graphics", "ChangeDisplaySettings failed: {}", disp_res);
|
||||
}
|
||||
} else {
|
||||
log_warning("graphics", "failed to get display settings");
|
||||
@@ -364,12 +403,10 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
||||
}
|
||||
|
||||
// gfdm
|
||||
bool is_gitadora_main_window = false;
|
||||
if (avs::game::is_model({"J32", "J33", "K32", "K33", "L32", "L33", "M32"})) {
|
||||
// set window name
|
||||
if (!lpWindowName) {
|
||||
lpWindowName = "GITADORA";
|
||||
is_gitadora_main_window = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,7 +414,7 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
||||
bool is_sdvx_sub_window = avs::game::is_model("KFC") && window_name.ends_with(" Sub Screen");
|
||||
bool is_gfdm_sub_window = games::gitadora::is_arena_model() && window_name.ends_with("SMALL");
|
||||
|
||||
// TDJ windowed mode with subscreen: hide maximize button
|
||||
// hide maximize button (prevent misaligned touches)
|
||||
if ((is_tdj_sub_window && GRAPHICS_IIDX_WSUB) || is_sdvx_sub_window || is_gfdm_sub_window) {
|
||||
dwStyle &= ~(WS_MAXIMIZEBOX);
|
||||
}
|
||||
@@ -385,7 +422,7 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
||||
if (is_gfdm_sub_window) {
|
||||
dwStyle &= ~(WS_SIZEBOX);
|
||||
}
|
||||
if ((is_tdj_sub_window || is_sdvx_sub_window || is_gfdm_sub_window) && GRAPHICS_WSUB_BORDERLESS) {
|
||||
if ((is_tdj_sub_window || is_sdvx_sub_window) && GRAPHICS_WSUB_BORDERLESS) {
|
||||
dwStyle &= ~(WS_OVERLAPPEDWINDOW);
|
||||
}
|
||||
|
||||
@@ -412,11 +449,6 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
||||
hWndParent, hMenu, hInstance, lpParam);
|
||||
GRAPHICS_WINDOWS.push_back(result);
|
||||
|
||||
// this is only really needed for arena model, but pre-arena model was single window anyway
|
||||
if (is_gitadora_main_window) {
|
||||
GRAPHICS_WINDOW_MAIN = result;
|
||||
}
|
||||
|
||||
if (is_tdj_sub_window) {
|
||||
// TDJ windowed mode: remember the subscreen window handle for later
|
||||
TDJ_SUBSCREEN_WINDOW = result;
|
||||
@@ -433,13 +465,19 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
||||
graphics_hook_subscreen_window(SDVX_SUBSCREEN_WINDOW);
|
||||
}
|
||||
|
||||
if (is_gfdm_sub_window) {
|
||||
// only hook touch window if multiple windows are allowed
|
||||
if (is_gfdm_sub_window && !games::gitadora::ARENA_SINGLE_WINDOW) {
|
||||
GFDM_SUBSCREEN_WINDOW = result;
|
||||
graphics_hook_subscreen_window(GFDM_SUBSCREEN_WINDOW);
|
||||
}
|
||||
|
||||
disable_touch_indicators(result);
|
||||
log_misc("graphics", "CreateWindowExA returned {}", fmt::ptr(result));
|
||||
log_misc(
|
||||
"graphics",
|
||||
"CreateWindowExA returned {}, {}",
|
||||
fmt::ptr(result),
|
||||
lpWindowName ? lpWindowName : "(null)");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -471,7 +509,8 @@ static HWND WINAPI CreateWindowExW_hook(DWORD dwExStyle, LPCWSTR lpClassName, LP
|
||||
}
|
||||
|
||||
// windowed mode adjustments
|
||||
if (GRAPHICS_WINDOWED) {
|
||||
// check the width to filter out invisible system-created windows
|
||||
if (GRAPHICS_WINDOWED && nWidth > 0) {
|
||||
|
||||
// change window style
|
||||
dwExStyle = 0;
|
||||
@@ -506,7 +545,12 @@ static HWND WINAPI CreateWindowExW_hook(DWORD dwExStyle, LPCWSTR lpClassName, LP
|
||||
hWndParent, hMenu, hInstance, lpParam);
|
||||
GRAPHICS_WINDOWS.push_back(result);
|
||||
|
||||
log_misc("graphics", "CreateWindowExW returned {}", fmt::ptr(result));
|
||||
log_misc(
|
||||
"graphics",
|
||||
"CreateWindowExW returned {}, {}",
|
||||
fmt::ptr(result),
|
||||
lpWindowName ? ws2s(lpWindowName) : "(null)");
|
||||
|
||||
disable_touch_indicators(result);
|
||||
return result;
|
||||
}
|
||||
@@ -673,7 +717,7 @@ static BOOL WINAPI SetWindowPos_hook(HWND hWnd, HWND hWndInsertAfter,
|
||||
|
||||
// prevent gitadora arena model from shifting windows around if the user has preferences
|
||||
if (GRAPHICS_WINDOWED && games::gitadora::is_arena_model() &&
|
||||
GRAPHICS_WINDOW_MAIN.has_value() && hWnd == GRAPHICS_WINDOW_MAIN.value() &&
|
||||
GRAPHICS_HOOKED_WINDOW.has_value() && hWnd == GRAPHICS_HOOKED_WINDOW.value() &&
|
||||
cfg::SCREENRESIZE->enable_window_resize) {
|
||||
return TRUE;
|
||||
}
|
||||
@@ -682,6 +726,18 @@ static BOOL WINAPI SetWindowPos_hook(HWND hWnd, HWND hWndInsertAfter,
|
||||
return SetWindowPos_orig(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags);
|
||||
}
|
||||
|
||||
static BOOL WINAPI ShowWindow_hook(HWND hWnd, int nCmdShow) {
|
||||
if (games::gitadora::is_arena_model() &&
|
||||
games::gitadora::ARENA_SINGLE_WINDOW &&
|
||||
hWnd != GRAPHICS_HOOKED_WINDOW) {
|
||||
log_info("graphics", "ShowWindow_hook - hiding sub window {}", fmt::ptr(hWnd));
|
||||
return true;
|
||||
}
|
||||
|
||||
// call original
|
||||
return ShowWindow_orig(hWnd, nCmdShow);
|
||||
}
|
||||
|
||||
static ATOM WINAPI RegisterClassA_hook(const WNDCLASSA *lpWndClass) {
|
||||
|
||||
// check for null
|
||||
@@ -824,6 +880,7 @@ void graphics_init() {
|
||||
SetWindowLongA_orig = detour::iat_try("SetWindowLongA", SetWindowLongA_hook);
|
||||
SetWindowLongW_orig = detour::iat_try("SetWindowLongW", SetWindowLongW_hook);
|
||||
SetWindowPos_orig = detour::iat_try("SetWindowPos", SetWindowPos_hook);
|
||||
ShowWindow_orig = detour::iat_try("ShowWindow", ShowWindow_hook);
|
||||
|
||||
detour::iat_try("MessageBoxA", MessageBoxA_hook);
|
||||
detour::iat_try("MessageBoxExA", MessageBoxExA_hook);
|
||||
|
||||
@@ -31,13 +31,13 @@ extern bool GRAPHICS_SHOW_CURSOR;
|
||||
extern bool GRAPHICS_WINDOWED;
|
||||
extern graphics_orientation GRAPHICS_ADJUST_ORIENTATION;
|
||||
extern std::vector<HWND> GRAPHICS_WINDOWS;
|
||||
extern std::optional<HWND> GRAPHICS_WINDOW_MAIN;
|
||||
extern UINT GRAPHICS_FORCE_REFRESH;
|
||||
extern std::optional<uint32_t> GRAPHICS_FORCE_REFRESH_SUB;
|
||||
extern std::optional<int> GRAPHICS_FORCE_VSYNC_BUFFER;
|
||||
extern bool GRAPHICS_FORCE_SINGLE_ADAPTER;
|
||||
extern bool GRAPHICS_PREVENT_SECONDARY_WINDOW;
|
||||
extern std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
|
||||
extern std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION_SUB;
|
||||
extern bool GRAPHICS_FS_ORIENTATION_SWAP;
|
||||
extern uint32_t GRAPHICS_FS_ORIGINAL_WIDTH;
|
||||
extern uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT;
|
||||
@@ -50,6 +50,7 @@ extern std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_WINDOW_SIZE;
|
||||
extern std::optional<std::string> GRAPHICS_WINDOW_POS;
|
||||
extern bool GRAPHICS_WINDOW_ALWAYS_ON_TOP;
|
||||
extern bool GRAPHICS_WINDOW_BACKBUFFER_SCALE;
|
||||
extern std::optional<HWND> GRAPHICS_HOOKED_WINDOW;
|
||||
|
||||
extern bool GRAPHICS_IIDX_WSUB;
|
||||
extern std::optional<std::string> GRAPHICS_WSUB_SIZE;
|
||||
@@ -101,5 +102,8 @@ void graphics_capture_initial_window(HWND hWnd);
|
||||
void graphics_update_window_style(HWND hWnd);
|
||||
void graphics_update_z_order(HWND hWnd, bool always_on_top);
|
||||
void graphics_move_resize_window(HWND hWnd);
|
||||
bool graphics_window_change_crashes_game();
|
||||
bool graphics_window_options_breaks_game();
|
||||
bool graphics_window_decoration_change_crashes_game();
|
||||
bool graphics_window_resize_breaks_game();
|
||||
bool graphics_window_move_and_resize_breaks_game();
|
||||
void graphics_load_windowed_subscreen_parameters();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "graphics.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/gitadora/gitadora.h"
|
||||
#include "cfg/screen_resize.h"
|
||||
#include "overlay/overlay.h"
|
||||
#include "util/logging.h"
|
||||
@@ -23,6 +24,7 @@ std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_WINDOW_SIZE;
|
||||
std::optional<std::string> GRAPHICS_WINDOW_POS;
|
||||
bool GRAPHICS_WINDOW_ALWAYS_ON_TOP = false;
|
||||
bool GRAPHICS_WINDOW_BACKBUFFER_SCALE = false;
|
||||
std::optional<HWND> GRAPHICS_HOOKED_WINDOW;
|
||||
|
||||
// IIDX Windowed Subscreen - starts out as false, enabled by IIDX module on pre-attach as needed
|
||||
bool GRAPHICS_IIDX_WSUB = false;
|
||||
@@ -55,6 +57,8 @@ void graphics_capture_initial_window(HWND hWnd) {
|
||||
|
||||
graphics_load_windowed_parameters();
|
||||
|
||||
GRAPHICS_HOOKED_WINDOW = hWnd;
|
||||
|
||||
cfg::SCREENRESIZE->init_window_style = GetWindowLong(hWnd, GWL_STYLE);
|
||||
cfg::SCREENRESIZE->init_window_style_ex = GetWindowLong(hWnd, GWL_EXSTYLE);
|
||||
|
||||
@@ -66,8 +70,9 @@ void graphics_capture_initial_window(HWND hWnd) {
|
||||
RECT rect;
|
||||
if (!GetClientRect(hWnd, &rect)) {
|
||||
log_warning(
|
||||
"graphics",
|
||||
"graphics_capture_initial_window - GetClientRect failed, GLE: {}",
|
||||
"graphics-windowed",
|
||||
"[{}] graphics_capture_initial_window - GetClientRect failed, GLE: {}",
|
||||
fmt::ptr(hWnd),
|
||||
GetLastError());
|
||||
return;
|
||||
}
|
||||
@@ -79,12 +84,22 @@ void graphics_capture_initial_window(HWND hWnd) {
|
||||
cfg::SCREENRESIZE->init_client_aspect_ratio = (float)client_w / (float)client_h;
|
||||
log_debug(
|
||||
"graphics-windowed",
|
||||
"graphics_capture_initial_window initial window size {}x{}, ratio {}",
|
||||
"[{}] graphics_capture_initial_window initial window size {}x{}, ratio {}",
|
||||
fmt::ptr(hWnd),
|
||||
client_w, client_h, cfg::SCREENRESIZE->init_client_aspect_ratio);
|
||||
|
||||
// ensure frame size is captured
|
||||
graphics_wm_style_changed(hWnd, false);
|
||||
|
||||
// if the game is not compatible, ignore user-supplied values and keep the current size
|
||||
if (graphics_window_resize_breaks_game()) {
|
||||
cfg::SCREENRESIZE->client_width = client_w;
|
||||
cfg::SCREENRESIZE->client_height = client_h;
|
||||
}
|
||||
if (graphics_window_move_and_resize_breaks_game()) {
|
||||
cfg::SCREENRESIZE->enable_window_resize = false;
|
||||
}
|
||||
|
||||
// if there was no user-supplied dimension, seed it with the current size
|
||||
// so that the next resize operation will work
|
||||
if (cfg::SCREENRESIZE->client_width == 0) {
|
||||
@@ -98,7 +113,8 @@ void graphics_capture_initial_window(HWND hWnd) {
|
||||
// resize must be done before applying the border
|
||||
if (cfg::SCREENRESIZE->enable_window_resize) {
|
||||
log_info(
|
||||
"graphics-windowed", "change window rect - window offset: {}x{}, client size: {}x{}",
|
||||
"graphics-windowed", "[{}] change window rect - window offset: {}x{}, client size: {}x{}",
|
||||
fmt::ptr(hWnd),
|
||||
cfg::SCREENRESIZE->window_offset_x,
|
||||
cfg::SCREENRESIZE->window_offset_y,
|
||||
cfg::SCREENRESIZE->client_width,
|
||||
@@ -108,12 +124,13 @@ void graphics_capture_initial_window(HWND hWnd) {
|
||||
// ddr hangs when window frame doesn't have overlapped
|
||||
if (cfg::SCREENRESIZE->window_decoration != cfg::WindowDecorationMode::Default) {
|
||||
log_info(
|
||||
"graphics-windowed", "change window style - decoration: {}",
|
||||
"graphics-windowed", "[{}] change window style - decoration: {}",
|
||||
fmt::ptr(hWnd),
|
||||
cfg::SCREENRESIZE->window_decoration);
|
||||
graphics_update_window_style(hWnd);
|
||||
}
|
||||
if (cfg::SCREENRESIZE->window_always_on_top) {
|
||||
log_info("graphics-windowed", "change window z-order - always on top");
|
||||
log_info("graphics-windowed", "[{}] change window z-order - always on top", fmt::ptr(hWnd));
|
||||
graphics_update_z_order(hWnd, true);
|
||||
}
|
||||
|
||||
@@ -156,7 +173,7 @@ void graphics_load_windowed_parameters() {
|
||||
cfg::SCREENRESIZE->window_offset_x = result.first;
|
||||
cfg::SCREENRESIZE->window_offset_y = result.second;
|
||||
} else {
|
||||
log_warning("graphics-windowed", "failed to parse -windowpos");
|
||||
log_fatal("graphics-windowed", "failed to parse -windowpos");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +202,7 @@ void graphics_load_windowed_subscreen_parameters() {
|
||||
GRAPHICS_WSUB_WIDTH = result.first;
|
||||
GRAPHICS_WSUB_HEIGHT = result.second;
|
||||
} else {
|
||||
log_warning("graphics-windowed", "failed to parse -wsubsize");
|
||||
log_fatal("graphics-windowed", "failed to parse -iidxwsubsize / -sdvxwsubsize");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,7 +216,7 @@ void graphics_load_windowed_subscreen_parameters() {
|
||||
GRAPHICS_WSUB_X = result.first;
|
||||
GRAPHICS_WSUB_Y = result.second;
|
||||
} else {
|
||||
log_warning("graphics-windowed", "failed to parse -wsubpos");
|
||||
log_fatal("graphics-windowed", "failed to parse -iidxwsubpos / -sdvxwsubpos");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -372,7 +389,7 @@ void graphics_update_window_style(HWND hWnd) {
|
||||
if (!GRAPHICS_WINDOWED) {
|
||||
return;
|
||||
}
|
||||
if (graphics_window_change_crashes_game()) {
|
||||
if (graphics_window_decoration_change_crashes_game()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -392,6 +409,9 @@ void graphics_update_window_style(HWND hWnd) {
|
||||
break;
|
||||
case cfg::WindowDecorationMode::ResizableFrame:
|
||||
style |= WS_OVERLAPPEDWINDOW;
|
||||
if (graphics_window_resize_breaks_game()) {
|
||||
style &= ~WS_SIZEBOX;
|
||||
}
|
||||
break;
|
||||
case cfg::WindowDecorationMode::Default:
|
||||
default:
|
||||
@@ -479,12 +499,19 @@ void graphics_move_resize_window(HWND hWnd) {
|
||||
log_debug("graphics-windowed", "graphics_move_resize_window returned");
|
||||
}
|
||||
|
||||
bool graphics_window_change_crashes_game() {
|
||||
bool graphics_window_decoration_change_crashes_game() {
|
||||
static std::once_flag flag;
|
||||
static bool result = false;
|
||||
std::call_once(flag, []() {
|
||||
// ddr crashes when frame style changes
|
||||
result = avs::game::is_model("MDX");
|
||||
|
||||
// changing window decoration also changes dimensions and position
|
||||
// so this must be prevented
|
||||
if (graphics_window_move_and_resize_breaks_game()) {
|
||||
result = true;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
log_warning(
|
||||
"graphics-windowed",
|
||||
@@ -493,3 +520,43 @@ bool graphics_window_change_crashes_game() {
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
bool graphics_window_move_and_resize_breaks_game() {
|
||||
static std::once_flag flag;
|
||||
static bool result = false;
|
||||
|
||||
std::call_once(flag, []() {
|
||||
// games like reflec beat in use spice touch overlay for windowed mode
|
||||
// and mouse input breaks if window is moved as the overlay does not
|
||||
// currently support window moves/resizes
|
||||
result = avs::game::is_model({"KBR", "LBR", "MBR"});
|
||||
if (result) {
|
||||
log_warning(
|
||||
"graphics-windowed",
|
||||
"ignoring changes to window properties due to incompatibility with this game");
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
bool graphics_window_resize_breaks_game() {
|
||||
// for whatever reason, gitadora games on dx/sd/sd2 cabs behave poorly when window is resized
|
||||
// (eihter at launch or while it's running)
|
||||
// usually results in: soft lock during scene transtions or really long transitions,
|
||||
// backgrounds of menus not rendering, etc.
|
||||
bool is_gitadora = avs::game::is_model({ "J32", "J33", "K32", "K33", "L32", "L33", "M32" });
|
||||
bool is_arena = games::gitadora::is_arena_model();
|
||||
|
||||
bool result = graphics_window_move_and_resize_breaks_game() || (is_gitadora && !is_arena);
|
||||
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, [&result]() {
|
||||
if (result) {
|
||||
log_warning(
|
||||
"graphics-windowed",
|
||||
"ignoring changes to window size due to incompatibility with this game");
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -589,9 +589,25 @@ int main_implementation(int argc, char *argv[]) {
|
||||
if (options[launcher::Options::GitaDoraCabinetType].is_active()) {
|
||||
games::gitadora::CAB_TYPE = options[launcher::Options::GitaDoraCabinetType].value_uint32();
|
||||
}
|
||||
if (options[launcher::Options::GitaDoraArenaSingleWindow].value_bool() && GRAPHICS_WINDOWED) {
|
||||
games::gitadora::ARENA_SINGLE_WINDOW = true;
|
||||
}
|
||||
if (options[launcher::Options::GitaDoraWailHold].is_active()) {
|
||||
socd::TILT_HOLD_MS = options[launcher::Options::GitaDoraWailHold].value_uint32();
|
||||
}
|
||||
if (options[launcher::Options::GitaDoraPickAlgo].is_active()) {
|
||||
const auto text = options[launcher::Options::GitaDoraPickAlgo].value_text();
|
||||
if (text == "legacy") {
|
||||
games::gitadora::PICK_ALGO.reset();
|
||||
} else if (text == "neutral") {
|
||||
games::gitadora::PICK_ALGO = socd::SocdAlgorithm::Neutral;
|
||||
} else if (text == "raw") {
|
||||
games::gitadora::PICK_ALGO = socd::SocdAlgorithm::None;
|
||||
}
|
||||
}
|
||||
if (options[launcher::Options::GitaDoraSubOverlaySize].is_active()) {
|
||||
games::gitadora::SUBSCREEN_OVERLAY_SIZE = options[launcher::Options::GitaDoraSubOverlaySize].value_text();
|
||||
}
|
||||
if (options[launcher::Options::LoadNostalgiaModule].value_bool()) {
|
||||
attach_nostalgia = true;
|
||||
}
|
||||
@@ -995,18 +1011,26 @@ int main_implementation(int argc, char *argv[]) {
|
||||
if (options[launcher::Options::spice2x_DisableVolumeHook].value_bool()) {
|
||||
hooks::audio::VOLUME_HOOK_ENABLED = false;
|
||||
}
|
||||
|
||||
if (options[launcher::Options::AudioBackend].is_active()) {
|
||||
auto &name = options[launcher::Options::AudioBackend].value_text();
|
||||
auto backend = hooks::audio::name_to_backend(name.c_str());
|
||||
if (!backend.has_value() && !cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_fatal("launcher", "invalid audio backend: {}", name);
|
||||
}
|
||||
|
||||
hooks::audio::BACKEND = backend;
|
||||
}
|
||||
|
||||
if (options[launcher::Options::AsioDriverId].is_active()) {
|
||||
hooks::audio::ASIO_DRIVER_ID = options[launcher::Options::AsioDriverId].value_uint32();
|
||||
}
|
||||
if (options[launcher::Options::AsioDriverName].is_active()) {
|
||||
hooks::audio::BACKEND = hooks::audio::name_to_backend("asio");
|
||||
hooks::audio::ASIO_DRIVER_NAME = options[launcher::Options::AsioDriverName].value_text();
|
||||
// this new option overrides the old one
|
||||
hooks::audio::ASIO_DRIVER_ID.reset();
|
||||
}
|
||||
|
||||
if (options[launcher::Options::AudioDummy].value_bool()) {
|
||||
hooks::audio::USE_DUMMY = true;
|
||||
}
|
||||
@@ -1067,8 +1091,8 @@ int main_implementation(int argc, char *argv[]) {
|
||||
std::pair<uint32_t, uint32_t> result;
|
||||
if (parse_width_height(options[launcher::Options::spice2x_WindowSize].value_text(), result)) {
|
||||
GRAPHICS_WINDOW_SIZE = result;
|
||||
} else {
|
||||
log_warning("launcher", "failed to parse -windowsize");
|
||||
} else if (!cfg_run && !cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_fatal("launcher", "failed to parse -windowsize");
|
||||
}
|
||||
}
|
||||
if (options[launcher::Options::spice2x_WindowPosition].is_active()) {
|
||||
@@ -1112,6 +1136,9 @@ int main_implementation(int argc, char *argv[]) {
|
||||
if (options[launcher::Options::spice2x_DRSTransposeTouch].value_bool()) {
|
||||
games::drs::TRANSPOSE_TOUCH = true;
|
||||
}
|
||||
if (options[launcher::Options::DRSRGBCameraHook].value_bool()) {
|
||||
games::drs::RGB_CAMERA_HOOK = true;
|
||||
}
|
||||
if (options[launcher::Options::spice2x_AutoCard].is_active()) {
|
||||
const auto text = options[launcher::Options::spice2x_AutoCard].value_text();
|
||||
if (text == "p1") {
|
||||
@@ -1366,12 +1393,33 @@ int main_implementation(int argc, char *argv[]) {
|
||||
);
|
||||
}
|
||||
|
||||
if (launcher::signal::DISABLE && !cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_warning(
|
||||
"launcher",
|
||||
"WARNING - user specified -signaldisable option\n\n\n"
|
||||
"!!! !!!\n"
|
||||
"!!! Using -signaldisable option disables all exception handling, !!!\n"
|
||||
"!!! which means when the game crashes it will likely just vanish !!!\n"
|
||||
"!!! without troubleshooting information in the logs. !!!\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)) {
|
||||
GRAPHICS_FS_CUSTOM_RESOLUTION = result;
|
||||
} else {
|
||||
log_warning("launcher", "failed to parse -forceres");
|
||||
} else if (!cfg_run && !cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_fatal("launcher", "failed to parse -forceres");
|
||||
}
|
||||
}
|
||||
|
||||
if (options[launcher::Options::FullscreenSubResolution].is_active()) {
|
||||
std::pair<uint32_t, uint32_t> result;
|
||||
if (parse_width_height(options[launcher::Options::FullscreenSubResolution].value_text(), result)) {
|
||||
GRAPHICS_FS_CUSTOM_RESOLUTION_SUB = result;
|
||||
} else if (!cfg_run && !cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_fatal("launcher", "failed to parse -forceressub");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1848,7 +1896,7 @@ int main_implementation(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
// Polaris Chord
|
||||
if (check_dll("kamunity.dll") && fileutils::dir_exists("game/svm_Data")) {
|
||||
if (check_dll("kamunity.dll") && fileutils::file_exists("game/svm.exe")) {
|
||||
avs::game::DLL_NAME = "kamunity.dll";
|
||||
attach_io = true;
|
||||
attach_pc = true;
|
||||
@@ -1887,7 +1935,7 @@ int main_implementation(int argc, char *argv[]) {
|
||||
"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"
|
||||
" 3. You passed in invalid options (do not specify -exec or -modules if you don't know what you're doing)\n\n"
|
||||
);
|
||||
|
||||
log_fatal("launcher", "module auto detection failed, see log.txt for troubleshooting");
|
||||
|
||||
+347
-241
File diff suppressed because it is too large
Load Diff
@@ -31,6 +31,7 @@ namespace launcher {
|
||||
GraphicsForceRefresh,
|
||||
FullscreenResolution,
|
||||
FullscreenOrientationFlip,
|
||||
FullscreenSubResolution,
|
||||
FullscreenSubRefreshRate,
|
||||
Graphics9On12,
|
||||
spice2x_Dx9On12,
|
||||
@@ -85,11 +86,11 @@ namespace launcher {
|
||||
SDVXPrinterJPGQuality,
|
||||
SDVXDisableCameras,
|
||||
SDVXNativeTouch,
|
||||
spice2x_SDVXSubRedraw,
|
||||
spice2x_SDVXDigitalKnobSensitivity,
|
||||
SDVXDigitalKnobSocd,
|
||||
spice2x_SDVXAsioDriver,
|
||||
spice2x_SDVXSubPos,
|
||||
spice2x_SDVXSubRedraw,
|
||||
LoadDDRModule,
|
||||
DDR43Mode,
|
||||
DDRSkipCodecRegisteration,
|
||||
@@ -100,8 +101,11 @@ namespace launcher {
|
||||
LoadGitaDoraModule,
|
||||
GitaDoraTwoChannelAudio,
|
||||
GitaDoraCabinetType,
|
||||
GitaDoraArenaSingleWindow,
|
||||
GitaDoraLefty,
|
||||
GitaDoraWailHold,
|
||||
GitaDoraPickAlgo,
|
||||
GitaDoraSubOverlaySize,
|
||||
LoadJubeatModule,
|
||||
LoadReflecBeatModule,
|
||||
LoadShogikaiModule,
|
||||
@@ -183,6 +187,7 @@ namespace launcher {
|
||||
spice2x_DisableVolumeHook,
|
||||
AudioBackend,
|
||||
AsioDriverId,
|
||||
AsioDriverName,
|
||||
AudioDummy,
|
||||
DelayBy5Seconds,
|
||||
spice2x_DelayByNSeconds,
|
||||
@@ -234,6 +239,7 @@ namespace launcher {
|
||||
spice2x_IIDXWindowedTDJ,
|
||||
spice2x_DRSDisableTouch,
|
||||
spice2x_DRSTransposeTouch,
|
||||
DRSRGBCameraHook,
|
||||
spice2x_IIDXNativeTouch,
|
||||
spice2x_IIDXNoSub,
|
||||
spice2x_IIDXEmulateSubscreenKeypadTouch,
|
||||
|
||||
@@ -185,21 +185,30 @@ static LONG WINAPI TopLevelExceptionFilter(struct _EXCEPTION_POINTERS *Exception
|
||||
}
|
||||
|
||||
static BOOL WINAPI SetConsoleCtrlHandler_hook(PHANDLER_ROUTINE pHandlerRoutine, BOOL Add) {
|
||||
log_misc("signal", "SetConsoleCtrlHandler hook hit");
|
||||
static std::once_flag printed;
|
||||
std::call_once(printed, []() {
|
||||
log_misc("signal", "SetConsoleCtrlHandler hook hit (printing once)");
|
||||
});
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter_hook(
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
|
||||
{
|
||||
log_info("signal", "SetUnhandledExceptionFilter hook hit");
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter) {
|
||||
|
||||
static std::once_flag printed;
|
||||
std::call_once(printed, []() {
|
||||
log_info("signal", "SetUnhandledExceptionFilter hook hit (printing once)");
|
||||
});
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static PVOID WINAPI AddVectoredExceptionHandler_hook(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler) {
|
||||
log_info("signal", "AddVectoredExceptionHandler hook hit");
|
||||
static std::once_flag printed;
|
||||
std::call_once(printed, []() {
|
||||
log_info("signal", "AddVectoredExceptionHandler hook hit (printing once)");
|
||||
});
|
||||
|
||||
return launcher::signal::USE_VEH_WORKAROUND ? INVALID_HANDLE_VALUE : nullptr;
|
||||
}
|
||||
|
||||
+52
-20
@@ -119,6 +119,7 @@ static std::string EAMIO_DLL_NAME = "eamio.dll";
|
||||
static robin_hood::unordered_map<int, std::thread *> BT5API_THREADS;
|
||||
static robin_hood::unordered_map<int, int> BT5API_THREAD_RESULTS;
|
||||
static uint8_t BT5API_CARD_STATES[] = {0, 0};
|
||||
static uint16_t BT5API_KEYPAD_STATES[] = {0, 0};
|
||||
|
||||
static void bt5api_log(const char *bt5_module, const char *fmt, ...) {
|
||||
|
||||
@@ -240,46 +241,77 @@ void bt5api_hook(HINSTANCE module) {
|
||||
|
||||
void bt5api_poll_reader_card(uint8_t unit_no) {
|
||||
|
||||
// check if initialized
|
||||
if (!EAMIO_DLL) {
|
||||
// check if initialized or out of bounds
|
||||
if (!EAMIO_DLL || unit_no >= eamuse_get_game_keypads()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// poll
|
||||
if (!eam_io_poll(unit_no)) {
|
||||
log_warning("bt5api", "polling bt5api reader {} returned failure",
|
||||
log_warning("bt5api", "polling bt5api reader card {} returned failure",
|
||||
static_cast<int>(unit_no));
|
||||
return;
|
||||
}
|
||||
|
||||
// get sensor state
|
||||
uint8_t sensor_state = eam_io_get_sensor_state(unit_no);
|
||||
|
||||
// if we have a change in state.
|
||||
if(sensor_state != BT5API_CARD_STATES[unit_no]) {
|
||||
|
||||
// check for card in
|
||||
if (sensor_state > BT5API_CARD_STATES[unit_no]) {
|
||||
uint8_t card_id[8];
|
||||
eam_io_read_card(unit_no, card_id, 8);
|
||||
eamuse_card_insert(unit_no, card_id);
|
||||
// card inserted into the back.
|
||||
if(sensor_state & (1 << EAM_IO_SENSOR_BACK)) {
|
||||
uint8_t card_id[8];
|
||||
|
||||
log_info("bt5api", "card unit {} inserted", static_cast<int>(unit_no));
|
||||
|
||||
// do the bt5 dance
|
||||
eam_io_card_slot_cmd(unit_no, EAM_IO_CARD_SLOT_CMD_CLOSE);
|
||||
eam_io_poll(unit_no);
|
||||
eam_io_card_slot_cmd(unit_no, EAM_IO_CARD_SLOT_CMD_READ);
|
||||
eam_io_poll(unit_no);
|
||||
|
||||
// ask the api for the card, then insert it into our engine.
|
||||
eam_io_read_card(unit_no, card_id, 8);
|
||||
eamuse_card_insert(unit_no, card_id);
|
||||
}
|
||||
|
||||
// if card removed entirely, do another bt5 dance!
|
||||
if(sensor_state == 0) {
|
||||
eam_io_card_slot_cmd(unit_no, EAM_IO_CARD_SLOT_CMD_CLOSE);
|
||||
eam_io_poll(unit_no);
|
||||
eam_io_card_slot_cmd(unit_no, EAM_IO_CARD_SLOT_CMD_OPEN);
|
||||
|
||||
log_info("bt5api", "card unit {} removed", static_cast<int>(unit_no));
|
||||
}
|
||||
}
|
||||
|
||||
// save state
|
||||
|
||||
// save prev state
|
||||
BT5API_CARD_STATES[unit_no] = sensor_state;
|
||||
}
|
||||
|
||||
|
||||
void bt5api_poll_reader_keypad(uint8_t unit_no) {
|
||||
|
||||
// check if initialized
|
||||
if (!EAMIO_DLL) {
|
||||
// check if initialized or out of bounds
|
||||
if (!EAMIO_DLL || unit_no >= eamuse_get_game_keypads()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// poll
|
||||
if (!eam_io_poll(unit_no)) {
|
||||
log_warning("bt5api", "polling bt5api reader {} returned failure",
|
||||
static_cast<int>(unit_no));
|
||||
|
||||
uint16_t keypad_current = eam_io_get_keypad_state(unit_no);
|
||||
uint16_t keypad_changes = ~BT5API_KEYPAD_STATES[unit_no] & keypad_current;
|
||||
|
||||
// if user has pressed decimal for the first time, eject the slotted reader.
|
||||
if(keypad_changes & (1 << EAM_IO_KEYPAD_DECIMAL)) {
|
||||
log_info("bt5api", "card unit {} ejecting", static_cast<int>(unit_no));
|
||||
|
||||
eam_io_card_slot_cmd(unit_no, EAM_IO_CARD_SLOT_CMD_EJECT);
|
||||
}
|
||||
|
||||
// save the last pressed.
|
||||
BT5API_KEYPAD_STATES[unit_no] = keypad_current;
|
||||
|
||||
// get keypad
|
||||
eamuse_set_keypad_overrides_bt5(unit_no, eam_io_get_keypad_state(unit_no));
|
||||
// send off the state to the main engine.
|
||||
eamuse_set_keypad_overrides_bt5(unit_no, keypad_current);
|
||||
}
|
||||
|
||||
void bt5api_dispose() {
|
||||
|
||||
@@ -202,7 +202,14 @@ bool eamuse_card_insert_consume(int active_count, int unit_id) {
|
||||
|
||||
// bt5api
|
||||
if (BT5API_ENABLED) {
|
||||
bt5api_poll_reader_card((uint8_t) index);
|
||||
// some bt5api want to be polled in order.
|
||||
if (eamuse_get_game_keypads() > 1) {
|
||||
bt5api_poll_reader_card(1);
|
||||
bt5api_poll_reader_card(0);
|
||||
}
|
||||
else {
|
||||
bt5api_poll_reader_card((uint8_t) index);
|
||||
}
|
||||
}
|
||||
|
||||
// auto insert card
|
||||
|
||||
+32
-14
@@ -522,24 +522,42 @@ static long __cdecl gfdm_unit_get_input_p(int device, size_t player) {
|
||||
if (games::gitadora::is_guitar()) {
|
||||
auto offset = player * 11;
|
||||
|
||||
// pick up
|
||||
if (Buttons::getState(RI_MGR, buttons.at(gitadora_button_mapping[3 + offset]))) {
|
||||
if (!GFDM_GF_PICK_STATE_UP[player]) {
|
||||
GFDM_GF_PICK_STATE_UP[player] = true;
|
||||
ret |= 0x80 | 0x20;
|
||||
const auto pick_up = Buttons::getState(RI_MGR, buttons.at(gitadora_button_mapping[3 + offset]));
|
||||
const auto pick_down = Buttons::getState(RI_MGR, buttons.at(gitadora_button_mapping[4 + offset]));
|
||||
if (!games::gitadora::PICK_ALGO.has_value()) {
|
||||
// legacy behavior (detect rising edges only, input for 1 frame)
|
||||
if (pick_up) {
|
||||
if (!GFDM_GF_PICK_STATE_UP[player]) {
|
||||
GFDM_GF_PICK_STATE_UP[player] = true;
|
||||
ret |= 0x80 | 0x20;
|
||||
}
|
||||
} else {
|
||||
GFDM_GF_PICK_STATE_UP[player] = false;
|
||||
}
|
||||
} else {
|
||||
GFDM_GF_PICK_STATE_UP[player] = false;
|
||||
}
|
||||
|
||||
// pick down
|
||||
if (Buttons::getState(RI_MGR, buttons.at(gitadora_button_mapping[4 + offset]))) {
|
||||
if (!GFDM_GF_PICK_STATE_DOWN[player]) {
|
||||
GFDM_GF_PICK_STATE_DOWN[player] = true;
|
||||
ret |= 0x100 | 0x20;
|
||||
if (pick_down) {
|
||||
if (!GFDM_GF_PICK_STATE_DOWN[player]) {
|
||||
GFDM_GF_PICK_STATE_DOWN[player] = true;
|
||||
ret |= 0x100 | 0x20;
|
||||
}
|
||||
} else {
|
||||
GFDM_GF_PICK_STATE_DOWN[player] = false;
|
||||
}
|
||||
} else {
|
||||
GFDM_GF_PICK_STATE_DOWN[player] = false;
|
||||
const auto socd = socd::socd_clean(
|
||||
player,
|
||||
pick_up,
|
||||
pick_down,
|
||||
get_performance_milliseconds(),
|
||||
games::gitadora::PICK_ALGO.value()
|
||||
);
|
||||
if (socd == socd::SocdCCW) {
|
||||
ret |= 0x80 | 0x20; // pick up
|
||||
} else if (socd == socd::SocdCW) {
|
||||
ret |= 0x100 | 0x20; // pick down
|
||||
} else if (socd == socd::SocdBoth) {
|
||||
ret |= 0x100 | 0x80 | 0x20; // up and down
|
||||
}
|
||||
}
|
||||
|
||||
// button R
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
// mingw otherwise doesn't load touch stuff
|
||||
#define _WIN32_WINNT 0x0601
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "wintouchemu.h"
|
||||
#include "rawinput/touch.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
@@ -63,6 +66,11 @@ namespace nativetouchhook {
|
||||
point->cyContact = 0;
|
||||
}
|
||||
|
||||
static void flip_touch_points(PTOUCHINPUT point) {
|
||||
point->x = rawinput::touch::DISPLAY_SIZE_X * 100 - point->x;
|
||||
point->y = rawinput::touch::DISPLAY_SIZE_Y * 100 - point->y;
|
||||
}
|
||||
|
||||
static BOOL WINAPI GetTouchInputInfoHook(
|
||||
HTOUCHINPUT hTouchInput, UINT cInputs, PTOUCHINPUT pInputs, int cbSize) {
|
||||
|
||||
@@ -72,9 +80,28 @@ namespace nativetouchhook {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool flip_values = false;
|
||||
if (avs::game::is_model("KFC") && rawinput::touch::DISPLAY_INITIALIZED) {
|
||||
log_debug(
|
||||
"touch::native", "DISPLAY_ORIENTATION = {}, DISPLAY_SIZE_X = {}, DISPLAY_SIZE_Y = {}",
|
||||
rawinput::touch::DISPLAY_ORIENTATION,
|
||||
rawinput::touch::DISPLAY_SIZE_X,
|
||||
rawinput::touch::DISPLAY_SIZE_Y);
|
||||
if (rawinput::touch::DISPLAY_ORIENTATION == DMDO_270) {
|
||||
flip_values = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < cInputs; i++) {
|
||||
PTOUCHINPUT point = &pInputs[i];
|
||||
strip_contact_size(point);
|
||||
|
||||
if (avs::game::is_model("LDJ")) {
|
||||
strip_contact_size(point);
|
||||
}
|
||||
|
||||
if (flip_values) {
|
||||
flip_touch_points(point);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <thread>
|
||||
|
||||
#include "cfg/screen_resize.h"
|
||||
#include "games/gitadora/gitadora.h"
|
||||
#include "games/iidx/iidx.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "overlay/overlay.h"
|
||||
@@ -19,6 +20,7 @@
|
||||
#include "util/logging.h"
|
||||
#include "util/time.h"
|
||||
#include "util/utils.h"
|
||||
#include "rawinput/touch.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
|
||||
@@ -125,6 +127,11 @@ namespace wintouchemu {
|
||||
WINDOW_TITLE_END = window_title_end;
|
||||
}
|
||||
|
||||
static void flip_touch_points(PTOUCHINPUT point) {
|
||||
point->x = rawinput::touch::DISPLAY_SIZE_X * 100 - point->x;
|
||||
point->y = rawinput::touch::DISPLAY_SIZE_Y * 100 - point->y;
|
||||
}
|
||||
|
||||
static BOOL WINAPI GetTouchInputInfoHook(HANDLE hTouchInput, UINT cInputs, PTOUCHINPUT pInputs, int cbSize) {
|
||||
|
||||
// check if original should be called
|
||||
@@ -219,6 +226,12 @@ namespace wintouchemu {
|
||||
touch_input->cxContact = 0;
|
||||
touch_input->cyContact = 0;
|
||||
|
||||
if (avs::game::is_model("KFC") &&
|
||||
rawinput::touch::DISPLAY_INITIALIZED &&
|
||||
rawinput::touch::DISPLAY_ORIENTATION == DMDO_270) {
|
||||
flip_touch_points(touch_input);
|
||||
}
|
||||
|
||||
} else if (USE_MOUSE && !mouse_used) {
|
||||
|
||||
// disable further mouse inputs this call
|
||||
@@ -390,7 +403,10 @@ namespace wintouchemu {
|
||||
} else if (avs::game::is_model("LDJ") && !GENERIC_SUB_WINDOW_FULLSIZE) {
|
||||
// overlay subscreen in IIDX
|
||||
// use mouse position as ImGui overlay will block the touch window
|
||||
log_info("wintouchemu", "use mouse cursor API for overlay subscreen");
|
||||
log_info("wintouchemu", "use mouse cursor API for ldj overlay subscreen");
|
||||
USE_MOUSE = true;
|
||||
} else if (games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW) {
|
||||
log_info("wintouchemu", "use mouse cursor API for gitadora overlay subscreen");
|
||||
USE_MOUSE = true;
|
||||
} else {
|
||||
// create touch window - create overlay if not yet existing at this point
|
||||
|
||||
@@ -236,6 +236,26 @@ namespace nvapi {
|
||||
return NvAPI_DRS_SetSetting(h_session, h_profile, &drs_setting);
|
||||
}
|
||||
|
||||
NvAPI_Status set_refresh_rate(NvDRSSessionHandle h_session, NvDRSProfileHandle h_profile) {
|
||||
auto drs_setting = NVDRS_SETTING {};
|
||||
drs_setting.version = NVDRS_SETTING_VER;
|
||||
drs_setting.settingId = REFRESH_RATE_OVERRIDE_ID;
|
||||
drs_setting.settingType = NVDRS_DWORD_TYPE;
|
||||
drs_setting.u32PredefinedValue = REFRESH_RATE_OVERRIDE_APPLICATION_CONTROLLED;
|
||||
drs_setting.u32CurrentValue = REFRESH_RATE_OVERRIDE_APPLICATION_CONTROLLED;
|
||||
return NvAPI_DRS_SetSetting(h_session, h_profile, &drs_setting);
|
||||
}
|
||||
|
||||
NvAPI_Status set_max_fps(NvDRSSessionHandle h_session, NvDRSProfileHandle h_profile) {
|
||||
auto drs_setting = NVDRS_SETTING {};
|
||||
drs_setting.version = NVDRS_SETTING_VER;
|
||||
drs_setting.settingId = FRL_FPS_ID;
|
||||
drs_setting.settingType = NVDRS_DWORD_TYPE;
|
||||
drs_setting.u32PredefinedValue = FRL_FPS_DISABLED;
|
||||
drs_setting.u32CurrentValue = FRL_FPS_DISABLED;
|
||||
return NvAPI_DRS_SetSetting(h_session, h_profile, &drs_setting);
|
||||
}
|
||||
|
||||
void set_profile_settings() {
|
||||
if (!NVAPI_MODULE_LOAD_SUCCEEDED) {
|
||||
return;
|
||||
@@ -266,22 +286,26 @@ namespace nvapi {
|
||||
log_info("nvapi", "setting VSync to Application Controlled...");
|
||||
if ((status = set_vsync_mode(h_session, h_profile)) != NVAPI_OK) {
|
||||
log_warning("nvapi", "could not set VSync mode: {}", get_error_message(status));
|
||||
NvAPI_DRS_DestroySession(h_session);
|
||||
return;
|
||||
}
|
||||
|
||||
log_info("nvapi", "applying preferred PState to Maximum Performance...");
|
||||
if ((status = set_gpu_power_state(h_session, h_profile)) != NVAPI_OK) {
|
||||
log_warning("nvapi", "could not set preferred PState: {}", get_error_message(status));
|
||||
NvAPI_DRS_DestroySession(h_session);
|
||||
return;
|
||||
}
|
||||
|
||||
log_info("nvapi", "disabling G-SYNC...");
|
||||
if ((status = set_gsync_mode(h_session, h_profile)) != NVAPI_OK) {
|
||||
log_warning("nvapi", "could not set G-Sync mode: {}", get_error_message(status));
|
||||
NvAPI_DRS_DestroySession(h_session);
|
||||
return;
|
||||
}
|
||||
|
||||
log_info("nvapi", "setting preferred refresh rate to Application Controlled...");
|
||||
if ((status = set_refresh_rate(h_session, h_profile)) != NVAPI_OK) {
|
||||
log_warning("nvapi", "could not set preferred refresh rate: {}", get_error_message(status));
|
||||
}
|
||||
|
||||
log_info("nvapi", "disabling max framerate...");
|
||||
if ((status = set_max_fps(h_session, h_profile)) != NVAPI_OK) {
|
||||
log_warning("nvapi", "could not disable max framerate: {}", get_error_message(status));
|
||||
}
|
||||
|
||||
log_info("nvapi", "saving settings for DRS session...");
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace ImGui {
|
||||
|
||||
void HelpMarker(const char* desc) {
|
||||
ImGui::TextDisabled("(?)");
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered(TOOLTIP_FLAGS)) {
|
||||
HelpTooltip(desc);
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ namespace ImGui {
|
||||
ImGui::PushStyleColor(ImGuiCol_TextDisabled, ImVec4(1.f, 1.f, 0.f, 1.f));
|
||||
ImGui::TextDisabled("(!)");
|
||||
ImGui::PopStyleColor();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered(TOOLTIP_FLAGS)) {
|
||||
WarnTooltip(desc, warn);
|
||||
}
|
||||
}
|
||||
@@ -92,4 +92,75 @@ namespace ImGui {
|
||||
ImGui::GetColorU32(ImGuiCol_PlotHistogram),
|
||||
thickness);
|
||||
}
|
||||
|
||||
std::string TruncateText(const std::string& p_text, float p_truncated_width, bool &truncated) {
|
||||
std::string truncated_text = p_text;
|
||||
|
||||
const float text_width =
|
||||
ImGui::CalcTextSize(p_text.c_str(), nullptr, true).x;
|
||||
|
||||
if (text_width > p_truncated_width) {
|
||||
truncated = true;
|
||||
constexpr const char* ELLIPSIS = " ...";
|
||||
const float ellipsis_size = ImGui::CalcTextSize(ELLIPSIS).x;
|
||||
|
||||
int visible_chars = 0;
|
||||
for (size_t i = 0; i < p_text.size(); i++) {
|
||||
const float current_width = ImGui::CalcTextSize(
|
||||
p_text.substr(0, i).c_str(), nullptr, true)
|
||||
.x;
|
||||
if (current_width + ellipsis_size > p_truncated_width) {
|
||||
break;
|
||||
}
|
||||
|
||||
visible_chars = i;
|
||||
}
|
||||
|
||||
truncated_text = (p_text.substr(0, visible_chars) + ELLIPSIS).c_str();
|
||||
}
|
||||
|
||||
return truncated_text;
|
||||
}
|
||||
|
||||
void TextTruncated(const std::string& p_text, float p_truncated_width) {
|
||||
if (p_text.empty()) {
|
||||
ImGui::TextDisabled("-");
|
||||
return;
|
||||
}
|
||||
bool truncated = false;
|
||||
ImGui::TextUnformatted(TruncateText(p_text, p_truncated_width, truncated).c_str());
|
||||
if (truncated && ImGui::IsItemHovered(TOOLTIP_FLAGS)) {
|
||||
ImGui::HelpTooltip(p_text.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
bool DeleteButton(const std::string& tooltip) {
|
||||
ImGui::PushID(tooltip.c_str());
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_Button, 0.7f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(3.f, 2.f));
|
||||
bool clicked = ImGui::SmallButton("\u00D7"); // multiplication sign (×)
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
if (!tooltip.empty() && ImGui::IsItemHovered(TOOLTIP_FLAGS)) {
|
||||
ImGui::HelpTooltip(tooltip.c_str());
|
||||
}
|
||||
ImGui::PopID();
|
||||
return clicked;
|
||||
}
|
||||
|
||||
bool ClearButton(const std::string& tooltip) {
|
||||
ImGui::PushID(tooltip.c_str());
|
||||
// same colors as a checkbox
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_FrameBg));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetColorU32(ImGuiCol_FrameBgHovered));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetColorU32(ImGuiCol_FrameBgActive));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.97f, 0.97f, 0.97f, 1.00f));
|
||||
bool clicked = ImGui::Button("\u00D7"); // multiplication sign (×)
|
||||
ImGui::PopStyleColor(4);
|
||||
if (!tooltip.empty() && ImGui::IsItemHovered(TOOLTIP_FLAGS)) {
|
||||
ImGui::HelpTooltip(tooltip.c_str());
|
||||
}
|
||||
ImGui::PopID();
|
||||
return clicked;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "external/imgui/imgui.h"
|
||||
|
||||
namespace ImGui {
|
||||
|
||||
constexpr ImGuiHoveredFlags TOOLTIP_FLAGS =
|
||||
(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_AllowWhenDisabled);
|
||||
|
||||
void HelpTooltip(const char* desc);
|
||||
void HelpMarker(const char* desc);
|
||||
void WarnTooltip(const char* desc, const char* warn);
|
||||
void WarnMarker(const char* desc, const char* warn);
|
||||
void DummyMarker();
|
||||
|
||||
void Knob(float fraction, float size, float thickness = 2.f,
|
||||
float pos_x = -1.f, float pos_y = -1.f);
|
||||
|
||||
void TextTruncated(const std::string& p_text, float p_truncated_width);
|
||||
bool DeleteButton(const std::string& tooltip);
|
||||
bool ClearButton(const std::string& tooltip);
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ void ImGui_ImplSpice_NewFrame() {
|
||||
memset(io.MouseDown, false, sizeof(io.MouseDown));
|
||||
|
||||
// early quit if window not in focus
|
||||
if (!superexit::has_focus()) {
|
||||
if (!superexit::has_focus() || rawinput::OS_WINDOW_ACTIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "windows/generic_sub.h"
|
||||
#include "windows/iidx_seg.h"
|
||||
#include "windows/iidx_sub.h"
|
||||
#include "windows/gfdm_sub.h"
|
||||
#include "windows/drs_dancefloor.h"
|
||||
#include "windows/iopanel.h"
|
||||
#include "windows/iopanel_ddr.h"
|
||||
@@ -410,7 +411,10 @@ void overlay::SpiceOverlay::init() {
|
||||
window_sub = new overlay::windows::DRSDanceFloorDisplay(this);
|
||||
} else if (avs::game::is_model("KFC")) {
|
||||
window_sub = new overlay::windows::SDVXSubScreen(this);
|
||||
} else if (games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW) {
|
||||
window_sub = new overlay::windows::GitaDoraSubScreen(this);
|
||||
}
|
||||
|
||||
if (window_sub) {
|
||||
this->window_add(window_sub);
|
||||
if (AUTO_SHOW_SUBSCREEN) {
|
||||
|
||||
+2293
-1729
File diff suppressed because it is too large
Load Diff
@@ -38,7 +38,6 @@ namespace overlay::windows {
|
||||
ConfigTab tab_selected = ConfigTab::CONFIG_TAB_INVALID;
|
||||
|
||||
// buttons tab
|
||||
int buttons_page = 0;
|
||||
bool buttons_keyboard_state[0xFF];
|
||||
bool buttons_bind_active = false;
|
||||
bool buttons_many_active = false;
|
||||
@@ -55,7 +54,6 @@ namespace overlay::windows {
|
||||
int analogs_devices_control_selected = -1;
|
||||
|
||||
// lights tab
|
||||
int lights_page = 0;
|
||||
std::vector<rawinput::Device *> lights_devices;
|
||||
int lights_devices_selected = -1;
|
||||
int lights_devices_control_selected = -1;
|
||||
@@ -78,6 +76,51 @@ namespace overlay::windows {
|
||||
std::string search_filter = "";
|
||||
std::string search_filter_in_lower_case = "";
|
||||
|
||||
std::filesystem::path file_picker_path;
|
||||
std::thread *file_picker_thread = nullptr;
|
||||
bool file_picker_done = false;
|
||||
|
||||
void build_buttons(const std::string &name, std::vector<Button> *buttons, int min = 0, int max = -1);
|
||||
void build_button(
|
||||
const std::string &name,
|
||||
Button &primary_button,
|
||||
Button *button,
|
||||
const int button_it,
|
||||
const int button_it_max,
|
||||
const int alt_index);
|
||||
|
||||
void bind_button_popup(const std::string &bind_name, Button *button, const int button_it_max, const int alt_index);
|
||||
void naive_button_popup(const std::string &naive_string, Button *button, const int button_it_max, const int alt_index);
|
||||
void edit_button_popup(
|
||||
const std::string &edit_name,
|
||||
const std::string &button_display,
|
||||
Button *button,
|
||||
const float button_velocity,
|
||||
const int alt_index);
|
||||
void clear_button(Button *button, const int alt_index);
|
||||
|
||||
void build_analogs(const std::string &name, std::vector<Analog> *analogs);
|
||||
void edit_analog_popup(Analog &analog);
|
||||
|
||||
void build_lights(const std::string &name, std::vector<Light> *lights);
|
||||
void build_light(Light &primary_light, Light *light, const int light_index, const int alt_index);
|
||||
void clear_light(Light *light, const int alt_index);
|
||||
void edit_light_popup(Light &primary_light, Light *light, const int alt_index);
|
||||
|
||||
void build_cards();
|
||||
std::string build_option_value_picker_title(const OptionDefinition& option);
|
||||
void build_option_value_picker(Option& option);
|
||||
void build_options(
|
||||
std::vector<Option> *options, const std::string &category, const std::string *filter=nullptr);
|
||||
void build_about();
|
||||
void build_launcher();
|
||||
void launch_shell(LPCSTR app, LPCSTR file=nullptr);
|
||||
|
||||
void build_menu(int *game_selected);
|
||||
void shutdown_system(bool force, bool reboot_instead);
|
||||
|
||||
void set_alternating_row_colors(const int row_index);
|
||||
|
||||
public:
|
||||
Config(SpiceOverlay *overlay);
|
||||
~Config() override;
|
||||
@@ -85,19 +128,6 @@ namespace overlay::windows {
|
||||
void read_card(int player = -1);
|
||||
void write_card(int player);
|
||||
void build_content() override;
|
||||
void build_buttons(const std::string &name, std::vector<Button> *buttons,
|
||||
int min = 0, int max = -1);
|
||||
void build_analogs(const std::string &name, std::vector<Analog> *analogs);
|
||||
void build_lights(const std::string &name, std::vector<Light> *lights);
|
||||
void build_cards();
|
||||
void build_options(
|
||||
std::vector<Option> *options, const std::string &category, const std::string *filter=nullptr);
|
||||
void build_about();
|
||||
void build_launcher();
|
||||
void launch_shell(LPCSTR app, LPCSTR file=nullptr);
|
||||
|
||||
static void build_page_selector(int *page);
|
||||
void build_menu(int *game_selected);
|
||||
void shutdown_system(bool force, bool reboot_instead);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/logging.h"
|
||||
#include "games/iidx/iidx.h"
|
||||
#include "games/gitadora/gitadora.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
@@ -69,6 +70,8 @@ namespace overlay::windows {
|
||||
sub = "Show Valkyrie Subscreen";
|
||||
} else if (avs::game::is_model("REC")) {
|
||||
sub = "Show DRS Dance Floor";
|
||||
} else if (games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW) {
|
||||
sub = "Show GITADORA Subscreen";
|
||||
}
|
||||
|
||||
build_button(this->overlay->window_sub, sub, size, NextItem::NEW_LINE, false);
|
||||
|
||||
@@ -85,10 +85,12 @@ namespace overlay::windows {
|
||||
}
|
||||
|
||||
void GenericSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {}
|
||||
void GenericSubScreen::check_for_errors() {}
|
||||
|
||||
void GenericSubScreen::build_content() {
|
||||
this->flags |= ImGuiWindowFlags_NoBackground;
|
||||
|
||||
check_for_errors();
|
||||
if (this->disabled_message.has_value()) {
|
||||
this->draws_window = true;
|
||||
this->flags &= ~ImGuiWindowFlags_NoBackground;
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace overlay::windows {
|
||||
|
||||
protected:
|
||||
virtual void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out);
|
||||
virtual void check_for_errors();
|
||||
ImVec2 overlay_content_top_left;
|
||||
ImVec2 overlay_content_size;
|
||||
std::optional<std::string> disabled_message = std::nullopt;
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "avs/game.h"
|
||||
#include "gfdm_sub.h"
|
||||
#include "games/gitadora/gitadora.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "touch/touch.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
GitaDoraSubScreen::GitaDoraSubScreen(SpiceOverlay *overlay) : GenericSubScreen(overlay) {
|
||||
this->title = "GITADORA Subscreen";
|
||||
|
||||
if (!games::gitadora::is_arena_model()) {
|
||||
this->disabled_message = "Requires Arena Model!";
|
||||
}
|
||||
|
||||
this->resize_callback = keep_10_by_16;
|
||||
|
||||
// medium size - perfect for "layout B" in GW Delta, though in drums it covers the pacemaker
|
||||
float size = 0.64f;
|
||||
if (games::gitadora::SUBSCREEN_OVERLAY_SIZE.has_value()) {
|
||||
if (games::gitadora::SUBSCREEN_OVERLAY_SIZE.value() == "large") {
|
||||
// enough to not cover the FPS overlay
|
||||
size = 0.88f;
|
||||
} else if (games::gitadora::SUBSCREEN_OVERLAY_SIZE.value() == "small") {
|
||||
size = 0.48f;
|
||||
}
|
||||
}
|
||||
|
||||
const float height = ImGui::GetIO().DisplaySize.y * size;
|
||||
const float width = height * 10 / 16;
|
||||
this->init_size = ImVec2(width, height + ImGui::GetFrameHeight());
|
||||
|
||||
// bottom right
|
||||
this->init_pos = ImVec2(
|
||||
ImGui::GetIO().DisplaySize.x - ImGui::GetFrameHeight() / 4 - this->init_size.x,
|
||||
ImGui::GetIO().DisplaySize.y - this->init_size.y - (ImGui::GetFrameHeight() / 4));
|
||||
}
|
||||
|
||||
void GitaDoraSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {
|
||||
if (!this->get_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GRAPHICS_WINDOWED) {
|
||||
// Touch needs to be registered on global coords
|
||||
*x_out = SPICETOUCH_TOUCH_X + xy_in.x * SPICETOUCH_TOUCH_WIDTH;
|
||||
*y_out = SPICETOUCH_TOUCH_Y + xy_in.y * SPICETOUCH_TOUCH_HEIGHT;
|
||||
} else {
|
||||
// Fullscreen mode, scale to game coords
|
||||
*x_out = xy_in.x * ImGui::GetIO().DisplaySize.x;
|
||||
*y_out = xy_in.y * ImGui::GetIO().DisplaySize.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "overlay/window.h"
|
||||
#include "overlay/windows/generic_sub.h"
|
||||
|
||||
namespace overlay::windows {
|
||||
|
||||
class GitaDoraSubScreen : public GenericSubScreen {
|
||||
public:
|
||||
GitaDoraSubScreen(SpiceOverlay *overlay);
|
||||
|
||||
protected:
|
||||
void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) override;
|
||||
|
||||
private:
|
||||
static void keep_10_by_16(ImGuiSizeCallbackData* data) {
|
||||
data->DesiredSize.y = (data->DesiredSize.x * 16.f / 10.f) + ImGui::GetFrameHeight();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -14,6 +14,7 @@ namespace overlay::windows {
|
||||
|
||||
static const size_t TICKER_SIZE = 9;
|
||||
static const ImVec4 DARK_GRAY(0.1f, 0.1f, 0.1f, 1.f);
|
||||
static const ImVec4 YELLOW(1.f, 1.f, 0.f, 1.f);
|
||||
static const ImVec4 RED(1.f, 0.f, 0.f, 1.f);
|
||||
static const float PADDING_Y = 8.f;
|
||||
static const float PADDING_X = 4.f;
|
||||
@@ -91,6 +92,23 @@ namespace overlay::windows {
|
||||
}
|
||||
|
||||
void IIDXSegmentDisplay::build_content() {
|
||||
|
||||
// explicitly check if TDJ I/O is enabled to account for the fact that I/O emulation may be
|
||||
// disabled
|
||||
if (games::iidx::CURRENT_IO_EMULATION_STATE == games::iidx::iidx_aio_emulation_state::bi2x_hook) {
|
||||
ImGui::TextUnformatted("");
|
||||
ImGui::TextColored(
|
||||
YELLOW,
|
||||
"%s",
|
||||
"Invalid I/O mode; DLL is configured for TDJ I/O!\n"
|
||||
"Enable -iidxtdj if you want TDJ subscreen overlay.");
|
||||
ImGui::TextUnformatted("");
|
||||
if (ImGui::Button("Close")) {
|
||||
this->set_active(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
char input_ticker[TICKER_SIZE];
|
||||
|
||||
// get ticker content from game
|
||||
|
||||
@@ -12,11 +12,9 @@ namespace overlay::windows {
|
||||
this->title = "IIDX TDJ Subscreen";
|
||||
|
||||
if (GRAPHICS_IIDX_WSUB) {
|
||||
this->disabled_message =
|
||||
"Close this overlay and use the second window.\n"
|
||||
"If you don't see the window, double check your DLL type and apply TDJ I/O patches as needed.";
|
||||
this->disabled_message = "Close this overlay and use the second window. (try ALT+TAB)";
|
||||
} else if (games::iidx::IIDX_TDJ_MONITOR_WARNING) {
|
||||
this->disabled_message = "TDJ mode subscreen overlay is not compatible with -monitor option";
|
||||
this->disabled_message = "TDJ mode subscreen overlay is not compatible with -monitor option.";
|
||||
}
|
||||
|
||||
float size = 0.5f;
|
||||
@@ -44,6 +42,18 @@ namespace overlay::windows {
|
||||
ImGui::GetIO().DisplaySize.y - this->init_size.y - (ImGui::GetFrameHeight() / 2));
|
||||
}
|
||||
|
||||
void IIDXSubScreen::check_for_errors() {
|
||||
if (games::iidx::CURRENT_IO_EMULATION_STATE ==
|
||||
games::iidx::iidx_aio_emulation_state::bi2a_com2) {
|
||||
|
||||
// explicitly check if LDJ I/O is enabled (as opposed to checking if NOT TDJ I/O)
|
||||
// to account for the fact that I/O emulation may be disabled
|
||||
this->disabled_message =
|
||||
"LDJ I/O detected; TDJ mode subscreen overlay requires TDJ I/O.\n"
|
||||
"Ensure you apply applicable TDJ I/O patches, or run with TDJ DLL.";
|
||||
}
|
||||
}
|
||||
|
||||
void IIDXSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {
|
||||
if (!this->get_active()) {
|
||||
return;
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace overlay::windows {
|
||||
|
||||
protected:
|
||||
void touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) override;
|
||||
void check_for_errors() override;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace overlay::windows {
|
||||
{
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - ImGui::GetFrameHeightWithSpacing());
|
||||
this->build_button("+", tall, this->test_button, this->service_button);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
||||
ImGui::HelpTooltip("SERVICE + TEST");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ namespace overlay::windows {
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetFrameHeightWithSpacing());
|
||||
this->build_button("<", leftright_size, this->left[p], nullptr, this->leftright_light[p]);
|
||||
this->build_button("+", tiny_size, this->help[p], this->start[p], nullptr);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
||||
ImGui::HelpTooltip("HELP + START");
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,7 @@ namespace overlay::windows {
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
this->build_button("?", tiny_size, this->help[p], nullptr, this->help_light[p]);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
||||
ImGui::HelpTooltip("HELP");
|
||||
}
|
||||
this->build_button(">", leftright_size, this->right[p], nullptr, this->leftright_light[p]);
|
||||
|
||||
@@ -120,7 +120,7 @@ namespace overlay::windows {
|
||||
"Lightning Model cabinets (TDJ) do not have any keypads; they use the subscreen.\n\n"
|
||||
"Fullscreen mode: bind a key in Overlay tab, and press it in game to show the subscreen, "
|
||||
"then use your mouse to click. Page Up button is the default binding.\n\n"
|
||||
"Windowed mode: look for the second window in the taskbar.\n\n"
|
||||
"Windowed mode: look for the second window in the taskbar (or ALT+TAB).\n\n"
|
||||
"Windowed mode with -iidxnosub: bring up the subscreen overlay (default Page Up).\n\n"
|
||||
);
|
||||
|
||||
|
||||
@@ -721,7 +721,7 @@ namespace overlay::windows {
|
||||
if (style_color_pushed) {
|
||||
ImGui::PopStyleColor(style_color_pushed);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
||||
show_patch_tooltip(patch);
|
||||
}
|
||||
|
||||
@@ -762,7 +762,7 @@ namespace overlay::windows {
|
||||
patch.last_status = is_patch_active(patch);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
||||
show_patch_tooltip(patch);
|
||||
}
|
||||
|
||||
@@ -791,7 +791,7 @@ namespace overlay::windows {
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
||||
show_patch_tooltip(patch);
|
||||
}
|
||||
} else if (patch.type == PatchType::Integer) {
|
||||
@@ -807,7 +807,7 @@ namespace overlay::windows {
|
||||
apply_patch(patch, true);
|
||||
config_dirty = true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
||||
show_patch_tooltip(patch);
|
||||
}
|
||||
}
|
||||
@@ -824,7 +824,7 @@ namespace overlay::windows {
|
||||
ImGui::InputInt("##dummy_int_input", &patch.patch_number.value);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered(ImGui::TOOLTIP_FLAGS)) {
|
||||
show_patch_tooltip(patch);
|
||||
}
|
||||
}
|
||||
@@ -930,6 +930,13 @@ namespace overlay::windows {
|
||||
}
|
||||
}
|
||||
|
||||
// match on model and ext, ignoring dest/spec/rev
|
||||
// sample: LDJ:J:E:A:2025011400
|
||||
bool PatchManager::is_game_id_wildcard_matched(const std::string& id_from_config) {
|
||||
return ((id_from_config.compare(0, 3, avs::game::MODEL) == 0) &&
|
||||
(id_from_config.compare(10, 10, avs::game::EXT) == 0));
|
||||
}
|
||||
|
||||
void PatchManager::config_load() {
|
||||
log_info("patchmanager", "loading config");
|
||||
|
||||
@@ -955,7 +962,7 @@ namespace overlay::windows {
|
||||
if (auto_apply != doc.MemberEnd() && auto_apply->value.IsArray()) {
|
||||
|
||||
// get game id
|
||||
auto game_id = avs::game::get_identifier();
|
||||
const auto game_id = avs::game::get_identifier();
|
||||
|
||||
// iterate entries
|
||||
setting_auto_apply = false;
|
||||
@@ -964,15 +971,37 @@ namespace overlay::windows {
|
||||
if (entry.IsString()) {
|
||||
|
||||
// check if this is our game identifier
|
||||
std::string entry_id = entry.GetString();
|
||||
if (game_id == entry_id) {
|
||||
setting_auto_apply = true;
|
||||
const std::string entry_id = entry.GetString();
|
||||
|
||||
if (!setting_auto_apply) {
|
||||
if (game_id == entry_id) {
|
||||
// exact match
|
||||
setting_auto_apply = true;
|
||||
log_misc(
|
||||
"patchmanager",
|
||||
"matched auto apply entry by full game identifier: {}",
|
||||
entry_id);
|
||||
|
||||
} else if (is_game_id_wildcard_matched(entry_id)) {
|
||||
// match on model and ext, ignoring dest/spec/rev
|
||||
// sample: LDJ:J:E:A:2025011400
|
||||
setting_auto_apply = true;
|
||||
log_misc(
|
||||
"patchmanager",
|
||||
"matched auto apply entry by partial game identifier: {}:?:?:?:{}",
|
||||
avs::game::MODEL, avs::game::EXT);
|
||||
}
|
||||
}
|
||||
|
||||
// move to list
|
||||
setting_auto_apply_list.emplace_back(entry_id);
|
||||
}
|
||||
}
|
||||
if (!setting_auto_apply) {
|
||||
log_misc(
|
||||
"patchmanager",
|
||||
"no auto apply entry matched, patches will not load automatically");
|
||||
}
|
||||
}
|
||||
|
||||
// read enabled patches
|
||||
@@ -1060,7 +1089,7 @@ namespace overlay::windows {
|
||||
auto game_id = avs::game::get_identifier();
|
||||
bool game_id_added = false;
|
||||
for (auto &entry : setting_auto_apply_list) {
|
||||
if (entry == game_id) {
|
||||
if (entry == game_id || is_game_id_wildcard_matched(entry)) {
|
||||
if (!setting_auto_apply) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -143,6 +143,8 @@ namespace overlay::windows {
|
||||
std::string pe_identifier_for_patch = "");
|
||||
|
||||
void show_patch_tooltip(const PatchData& patch);
|
||||
|
||||
bool is_game_id_wildcard_matched(const std::string& id_from_config);
|
||||
};
|
||||
|
||||
PatchStatus is_patch_active(PatchData &patch);
|
||||
|
||||
@@ -27,11 +27,12 @@ namespace overlay::windows {
|
||||
}
|
||||
|
||||
HWND ScreenResize::get_first_window() {
|
||||
// hack for gitadora arena model which races to create many windows
|
||||
// so [0] is not always the main one
|
||||
if (GRAPHICS_WINDOW_MAIN.has_value()) {
|
||||
return GRAPHICS_WINDOW_MAIN.value();
|
||||
// this is ideal
|
||||
if (GRAPHICS_HOOKED_WINDOW.has_value()) {
|
||||
return GRAPHICS_HOOKED_WINDOW.value();
|
||||
}
|
||||
// we typically do not want to check GRAPHICS_WINDOWS because it may include
|
||||
// system windows (e.g., CicMarshalWnd)
|
||||
if (GRAPHICS_WINDOWS.size() == 0) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -201,7 +202,7 @@ namespace overlay::windows {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::BeginDisabled(graphics_window_change_crashes_game());
|
||||
ImGui::BeginDisabled(graphics_window_decoration_change_crashes_game());
|
||||
if (ImGui::Combo(
|
||||
"Window Style",
|
||||
&cfg::SCREENRESIZE->window_decoration,
|
||||
@@ -226,42 +227,59 @@ namespace overlay::windows {
|
||||
"Reduces pixelated scaling artifacts. Works great on some games, but completely broken on others.\n\n"
|
||||
"This can't be changed in-game; instead, set -windowscale option in spicecfg and restart.");
|
||||
|
||||
ImGui::Checkbox("Keep Aspect Ratio", &cfg::SCREENRESIZE->client_keep_aspect_ratio);
|
||||
ImGui::Checkbox("Manual window move/resize", &cfg::SCREENRESIZE->enable_window_resize);
|
||||
|
||||
ImGui::BeginDisabled(graphics_window_move_and_resize_breaks_game());
|
||||
{
|
||||
ImGui::Checkbox("Keep Aspect Ratio", &cfg::SCREENRESIZE->client_keep_aspect_ratio);
|
||||
ImGui::Checkbox("Manual window move/resize", &cfg::SCREENRESIZE->enable_window_resize);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::BeginDisabled(!cfg::SCREENRESIZE->enable_window_resize);
|
||||
|
||||
bool changed = false;
|
||||
const uint32_t step = 1;
|
||||
const uint32_t step_fast = 10;
|
||||
ImGui::BeginDisabled(cfg::SCREENRESIZE->client_keep_aspect_ratio);
|
||||
ImGui::InputScalar(
|
||||
"Width",
|
||||
ImGuiDataType_U32,
|
||||
&cfg::SCREENRESIZE->client_width,
|
||||
&step, &step_fast, nullptr);
|
||||
changed |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
|
||||
ImGui::BeginDisabled(graphics_window_resize_breaks_game());
|
||||
{
|
||||
ImGui::BeginDisabled(cfg::SCREENRESIZE->client_keep_aspect_ratio);
|
||||
{
|
||||
ImGui::InputScalar(
|
||||
"Width",
|
||||
ImGuiDataType_U32,
|
||||
&cfg::SCREENRESIZE->client_width,
|
||||
&step, &step_fast, nullptr);
|
||||
changed |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::InputScalar(
|
||||
"Height",
|
||||
ImGuiDataType_U32,
|
||||
&cfg::SCREENRESIZE->client_height,
|
||||
&step, &step_fast, nullptr);
|
||||
changed |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::InputScalar(
|
||||
"Height",
|
||||
ImGuiDataType_U32,
|
||||
&cfg::SCREENRESIZE->client_height,
|
||||
&step, &step_fast, nullptr);
|
||||
changed |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
ImGui::BeginDisabled(graphics_window_move_and_resize_breaks_game());
|
||||
{
|
||||
ImGui::InputScalar(
|
||||
"X Offset",
|
||||
ImGuiDataType_S32,
|
||||
&cfg::SCREENRESIZE->window_offset_x,
|
||||
&step, &step_fast, nullptr);
|
||||
changed |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
|
||||
ImGui::InputScalar(
|
||||
"X Offset",
|
||||
ImGuiDataType_S32,
|
||||
&cfg::SCREENRESIZE->window_offset_x,
|
||||
&step, &step_fast, nullptr);
|
||||
changed |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
|
||||
ImGui::InputScalar(
|
||||
"Y Offset",
|
||||
ImGuiDataType_S32,
|
||||
&cfg::SCREENRESIZE->window_offset_y,
|
||||
&step, &step_fast, nullptr);
|
||||
changed |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
ImGui::InputScalar(
|
||||
"Y Offset",
|
||||
ImGuiDataType_S32,
|
||||
&cfg::SCREENRESIZE->window_offset_y,
|
||||
&step, &step_fast, nullptr);
|
||||
changed |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (changed) {
|
||||
if (cfg::SCREENRESIZE->client_keep_aspect_ratio) {
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace overlay::windows {
|
||||
if (GRAPHICS_PREVENT_SECONDARY_WINDOW) {
|
||||
this->disabled_message = "Subscreen has been disabled by the user (-sdvxnosub).";
|
||||
} else {
|
||||
this->disabled_message = "Overlay unavailable in windowed mode! Use the second window instead.";
|
||||
this->disabled_message = "Overlay unavailable in windowed mode! Use the second window instead. (ALT+TAB)";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,10 @@ namespace rawinput {
|
||||
uint32_t MIDI_NOTE_SUSTAIN = 20;
|
||||
|
||||
static MidiNoteAlgorithm MIDI_NOTE_ALGORITHM = MidiNoteAlgorithm::V2;
|
||||
|
||||
// the price we pay for making spice overlay consume from raw input
|
||||
// making focus detection a nightmare
|
||||
bool OS_WINDOW_ACTIVE = false;
|
||||
}
|
||||
|
||||
rawinput::MidiNoteAlgorithm rawinput::get_midi_algorithm() {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "device.h"
|
||||
#include "hotplug.h"
|
||||
#include "util/scope_guard.h"
|
||||
|
||||
namespace rawinput {
|
||||
|
||||
@@ -31,6 +32,15 @@ namespace rawinput {
|
||||
extern bool NAIVE_REQUIRE_FOCUS;
|
||||
extern bool RAWINPUT_REQUIRE_FOCUS;
|
||||
|
||||
// while active, prevents overlay from accepting any input
|
||||
// can be used while OS modal dialog is shown on top of overlay/spicecfg
|
||||
// always prefer RAII set_os_window_focus_guard instead of the global bool
|
||||
extern bool OS_WINDOW_ACTIVE;
|
||||
inline scope_guard set_os_window_focus_guard() {
|
||||
rawinput::OS_WINDOW_ACTIVE = true;
|
||||
return scope_guard {[]() { rawinput::OS_WINDOW_ACTIVE = false; }};
|
||||
}
|
||||
|
||||
struct DeviceCallback {
|
||||
void *data;
|
||||
std::function<void(void*, Device*)> f;
|
||||
|
||||
@@ -26,10 +26,10 @@ namespace rawinput::touch {
|
||||
bool INVERTED = false;
|
||||
|
||||
// state
|
||||
static bool DISPLAY_INITIALIZED = false;
|
||||
static DWORD DISPLAY_ORIENTATION = DMDO_DEFAULT;
|
||||
static long DISPLAY_SIZE_X = 1920L;
|
||||
static long DISPLAY_SIZE_Y = 1080L;
|
||||
DWORD DISPLAY_ORIENTATION = DMDO_DEFAULT;
|
||||
long DISPLAY_SIZE_X = 1920L;
|
||||
long DISPLAY_SIZE_Y = 1080L;
|
||||
bool DISPLAY_INITIALIZED = false;
|
||||
|
||||
bool is_touchscreen(Device *device) {
|
||||
|
||||
|
||||
@@ -9,6 +9,12 @@ namespace rawinput::touch {
|
||||
extern bool DISABLED;
|
||||
extern bool INVERTED;
|
||||
|
||||
// global state
|
||||
extern DWORD DISPLAY_ORIENTATION;
|
||||
extern long DISPLAY_SIZE_X;
|
||||
extern long DISPLAY_SIZE_Y;
|
||||
extern bool DISPLAY_INITIALIZED;
|
||||
|
||||
bool is_touchscreen(Device *device);
|
||||
void enable(Device *device);
|
||||
void disable(Device *device);
|
||||
|
||||
@@ -15,18 +15,23 @@ namespace socd {
|
||||
|
||||
SocdAlgorithm ALGORITHM = SocdAlgorithm::Neutral;
|
||||
|
||||
static double last_rising_edge[2][2] = {};
|
||||
static bool last_button_state[2][2] = {};
|
||||
static double last_rising_edge[4][2] = {};
|
||||
static bool last_button_state[4][2] = {};
|
||||
|
||||
// this has no locking, use only if you know there is only one i/o thread that will call this
|
||||
SocdResult socd_clean(uint8_t device, bool ccw, bool cw, double time_now) {
|
||||
if (device >= 2) {
|
||||
SocdResult socd_clean(
|
||||
uint8_t device, bool ccw, bool cw, double time_now,
|
||||
std::optional<SocdAlgorithm> algorithm_override) {
|
||||
|
||||
if (device >= 4) {
|
||||
log_fatal("socd", "invalid device index in socd_clean: {}", device);
|
||||
}
|
||||
|
||||
// SOCD last input algorithm needs to keep track of rising edge times
|
||||
if (ALGORITHM != SocdAlgorithm::Neutral) {
|
||||
// detect rising edges
|
||||
SocdAlgorithm algo =
|
||||
algorithm_override.has_value() ? algorithm_override.value() : ALGORITHM;
|
||||
|
||||
// keep track of rising edge times
|
||||
if (algo == SocdAlgorithm::PreferRecent || algo == SocdAlgorithm::PreferFirst) {
|
||||
if (!last_button_state[device][SocdCCW] && ccw) {
|
||||
last_rising_edge[device][SocdCCW] = time_now;
|
||||
}
|
||||
@@ -55,7 +60,7 @@ namespace socd {
|
||||
const auto cw_time = last_rising_edge[device][SocdCW];
|
||||
log_debug("socd", "ccw={}, cw ={}", ccw_time, cw_time);
|
||||
|
||||
if (ALGORITHM == SocdAlgorithm::PreferRecent) {
|
||||
if (algo == SocdAlgorithm::PreferRecent) {
|
||||
// SOCD: prefer last input
|
||||
if (ccw_time < cw_time) {
|
||||
// while CCW is being held, CW got pressed
|
||||
@@ -67,7 +72,7 @@ namespace socd {
|
||||
// it's a tie; instead of none, we'll pick a direction
|
||||
return SocdCW;
|
||||
}
|
||||
} else if (ALGORITHM == SocdAlgorithm::PreferFirst) {
|
||||
} else if (algo == SocdAlgorithm::PreferFirst) {
|
||||
// SOCD: keep first input
|
||||
if (ccw_time < cw_time) {
|
||||
// while CCW is being held, CW got pressed
|
||||
@@ -79,9 +84,15 @@ namespace socd {
|
||||
// it's a tie; instead of none, we'll pick a direction
|
||||
return SocdCW;
|
||||
}
|
||||
} else {
|
||||
} else if (algo == SocdAlgorithm::Neutral) {
|
||||
// SOCD: neutral when both are pressed
|
||||
return SocdNone;
|
||||
} else if (algo == SocdAlgorithm::None) {
|
||||
// SOCD: both are pressed
|
||||
return SocdBoth;
|
||||
} else {
|
||||
log_fatal("socd", "invalid SOCD algorithm");
|
||||
return SocdNone;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +108,8 @@ namespace socd {
|
||||
log_fatal("socd", "invalid device index in socd_clean: {}", device);
|
||||
}
|
||||
|
||||
const auto socd_result = socd_clean(device, up, down, time_now);
|
||||
const auto socd_result = socd_clean(
|
||||
device + 2, up, down, time_now, SocdAlgorithm::PreferRecent);
|
||||
|
||||
if (up) {
|
||||
most_recent_active[device][TiltUp] = time_now;
|
||||
@@ -125,7 +137,7 @@ namespace socd {
|
||||
result = TiltUp;
|
||||
} else if (socd_result == SocdCW) {
|
||||
result = TiltDown;
|
||||
} else {
|
||||
} else if (socd_result == SocdBoth) {
|
||||
result = TiltUp;
|
||||
}
|
||||
} else if (is_up) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <cstdint>
|
||||
|
||||
namespace socd {
|
||||
@@ -9,7 +10,8 @@ namespace socd {
|
||||
enum class SocdAlgorithm {
|
||||
Neutral,
|
||||
PreferRecent,
|
||||
PreferFirst
|
||||
PreferFirst,
|
||||
None,
|
||||
};
|
||||
|
||||
extern SocdAlgorithm ALGORITHM;
|
||||
@@ -17,10 +19,16 @@ namespace socd {
|
||||
typedef enum _SocdResult {
|
||||
SocdCCW = 0,
|
||||
SocdCW = 1,
|
||||
SocdNone = 2
|
||||
SocdNone = 2,
|
||||
SocdBoth = 3
|
||||
} SocdResult;
|
||||
|
||||
SocdResult socd_clean(uint8_t device, bool ccw, bool cw, double time_now);
|
||||
SocdResult socd_clean(
|
||||
uint8_t device,
|
||||
bool ccw,
|
||||
bool cw,
|
||||
double time_now,
|
||||
std::optional<SocdAlgorithm> algorithm = std::nullopt);
|
||||
|
||||
// for guitar wail (up/down only)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user