Compare commits

...

19 Commits

Author SHA1 Message Date
sp2xdev 4bc849ccdb update changelog 2025-05-09 14:26:51 -07:00
bicarus-dev 58481a035b printer: fix orientation of resulting image (#324)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Rotate the printer output so that it's the correct orientation
(portrait, not landscape).

## Testing
Tested via test image, and also in-game, for LP and SDVX.
2025-05-09 09:48:59 -07:00
bicarus-dev 66b3de8e5c loveplus: add printer options (#325)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Add the same options as SDVX printer.

## Testing
Tested all options with LP.
2025-05-09 09:48:00 -07:00
bicarus-dev 257afe0cd3 loveplus: remove log spam (#326)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Remove spammy logging in LP

## Testing
It works 👍
2025-05-09 09:47:38 -07:00
bicarus-dev aafea3b1c1 loveplus: stub impl of cpusbxpkm.dll (printer) (#319)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Add a stub for `cpusbxpkm.dll`. The stub doesn't work, it's only enough
to allow the game to load; but that is irrelevant since spice hooks the
printer at runtime.

## Testing
Observed the game boot, and was able to print out the test image from
test menu. Will do more testing on a real server.
2025-05-08 23:03:26 -07:00
bicarus-dev 346a159157 loveplus: full screen mode (#323)
## Link to GitHub Issue, if one exists
Fixes #322 

## Description of change
Ensure game launches in full screen when windowed mode setting is off.
Before this PR, the game always launched in windowed mode.

## Testing
Seems to run fine in fullscreen with the monitor in portrait mode.
It does seem to run at even lower framerate (around 24fps, vs. 30fps in
windowed) but it might be expected.
Some weirdness with the mouse cursor when the overlay is active (two
cursors).
2025-05-08 17:25:15 -07:00
bicarus-dev 54028d8cbb loveplus: improve window searching for touch hook (#321)
## Link to GitHub Issue, if one exists
Fixes #320 

## Description of change
Replacing the existing logic that searches for game window to hook touch
on.

Eventually, we should fix all the other games that hook touch by
matching on window title with a call to `FindProcessWindowBeginsWith`.

## Testing
Tested with notepad open that was also titled `LovePlusAC`. Game hooked
correctly when the game was in focus during boot, and also when notepad
was in focus.
2025-05-07 21:41:48 -07:00
bicarus-dev 621b702ed4 loveplus: enable cam access (#318)
## Link to GitHub Issue, if one exists
#316 
(this does not fix the issue!)

## Description of change
Add an option to omit `-noCamera` option. Doing so allows the game to
boot and search for a compatible camera using KS API, but will likely
fail since we don't know what cameras are compatible. You end up with a
camera error during boot, which can be ignored by flipping a switch in
the test menu to disable the camera.

No hooks are currently done to make the camera work!

## Testing
Tested to see that turning this on triggers the camera error. No known
camera at the moment.
2025-05-07 20:43:16 -07:00
bicarus-dev 26ea8a1b92 api: check for window focus when accepting input (#317)
## Link to GitHub Issue, if one exists
Fixes #139 

## Description of change
Add an option for detecting window focus when accepting input.

The new default will be to have the following:
 * naive bindings will check for window focus
* rawinput binds are not affected, will continue to be accepted
regardless of focus

Other options (always / never choices) can be picked by the user to
tweak the behavior if needed.

Clean up usage of `0xff` constant to represent invalid naive vkey.

## Testing
Still in progress, especially in regards to performance.

On a i7-9700 system, 10000 calls to `superexit::has_focus()` takes
250-350 microseconds, so this should not be a big deal at all.
2025-05-07 13:22:29 -07:00
bicarus-dev 6e7bf99af8 util: print dimensions and refresh rate of monitors (#314)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Dump monitor resolution and refresh rate to the log on launch.

## Testing
Tested with two monitors.
2025-05-05 16:48:55 -07:00
bicarus-dev df9d9d8673 api: quality options for screen mirroring (#313)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Allow the server to override client's quality options for screen
mirroring.

On Windows / Android versions (flutter-based) clients, this is already
in the options of those apps. However, the iOS version lacks the quality
options.

## Testing
Tested with iOS and Windows clients.
2025-05-04 20:10:56 -07:00
sp2xdev b3cee1dfcc fix build 2025-05-04 15:53:46 -07:00
bicarus-dev f40c382903 net: mark netadapter IP as sensitive option (#312)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Mark the option as "sensitive" which censors the option value (adapter's
IP address)
Remove the log entry from spice

This isn't perfect because most of the games like to log the IP address
and subnet anyway, but at least spice itself will stop doing it.

## Testing
Tested 64 bit LDJ with parameter set.
2025-05-04 13:20:37 -07:00
sp2xdev e4cfb808bf Update CONTRIBUTING.md 2025-05-04 00:38:10 -07:00
sp2xdev 61d14a16e8 Update CONTRIBUTING.md 2025-05-04 00:33:43 -07:00
bicarus-dev 521e87ea41 patchmanager: always log patch identifier of main DLL (#311)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Always log the DLL identifier string in patch manager, even if there are
no patches.

## Testing
Tested 32 / 64 bits.
2025-05-04 00:23:51 -07:00
bicarus-dev 2b735ffd55 launcher: check for custom d3d9.dll in user's game directory (#309)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Log a message if d3d9.dll (and friends) are found in the game root or
the modules directory.

This is to help with troubleshooting in cases where the user is unaware
that d3d9 is being used to inject other DLLs (e.g., ifs_layeredfs), or
when DX9 reimplementation is causing issues / crashes (e.g., dxvk).

## Testing
Observed the new messages being logged in both modules / root directory.
2025-05-02 01:51:23 -07:00
bicarus-dev 4f72b0a98b graphics: more window options for windowed subscreen (#308)
## Link to GitHub Issue, if one exists
Fixes #307 

## Description of change
Add an option for making TDJ/UFC subscreen window borderless in windowed
mode

Add another option for keeping the subscreen "always on top".

Add size / position arguments to SDVX windowed subscreen, exactly the
same as what is allowed for IIDX.

## Testing
Seems to work....
2025-04-27 21:20:55 -07:00
sp2xdev 474f5f5203 fatal error for sdvxlandscape 2025-04-25 15:53:58 -07:00
32 changed files with 657 additions and 106 deletions
+8 -4
View File
@@ -30,12 +30,11 @@ Do not change the names of options, buttons binds, analogs, etc - since they are
* All currently supported games / versions must continue to work. * All currently supported games / versions must continue to work.
* Backwards compatibility must be preserved, unless there is a really good reason to break it. This includes (but not limited to): global/local config files, command line parameters, game patches, device interop (e.g., card readers), and SpiceAPI / Companion interop. * Backwards compatibility must be preserved, unless there is a really good reason to break it. This includes (but not limited to): global/local config files, command line parameters, game patches, device interop (e.g., card readers), and SpiceAPI / Companion interop.
* Reasonable level of compatibility with the last release of original spicetools is expected. The stated goal of spice2x is to be a drop-in replacement for spicetools. * Reasonable level of compatibility with the last release of original spicetools is expected. The stated goal of spice2x is to be a drop-in replacement for spicetools.
* Simply put, if someone has an existing install of spicetools/spice2x, copying over new version of spice2x should not result in different behavior or loss of functionality. * Simply put, if someone has an existing install of spicetools/spice2x, copying over new version of spice2x should not result in vastly different behavior or major loss of functionality.
* Make sure you compile with the included Docker script and ensure you do not introduce **any** new compiler warnings or build breaks. The Docker script is the standard build environment, your custom Linux build environment or MSVC can be used during development, but you must validate the final build using Docker. * Make sure you compile with the included Docker script and ensure you do not introduce **any** new compiler warnings or build breaks. The Docker script is the standard build environment, your custom Linux build environment or MSVC can be used during development, but you must validate the final build using Docker.
* Do not submit snippets of code as a "patch". Exceptions can be made for trivial changes (correct a typo, fix a single line of code...), but otherwise, a successfully compiled & fully tested patch file is required when submitting for review.
* Do not make code changes in unrelated areas; i.e., do not run code linters and auto-formatters for parts of the code that you didn't modify. * Do not make code changes in unrelated areas; i.e., do not run code linters and auto-formatters for parts of the code that you didn't modify.
* Try to submit smaller chunks of code, instead one gigantic patch. For example, don't submit a patch for "Improve feature XYZ"; instead, submit "Change how A works to prepare for feature XYZ" "Refactor B for feature XYZ" "Add feature B to enable feature XYZ". * Try to submit smaller chunks of code, instead one gigantic patch. For example, don't submit a patch for "Improve feature XYZ"; instead, submit "Change how A works to prepare for feature XYZ" "Refactor B for feature XYZ" "Add feature B to enable feature XYZ".
* Write to the log for anything useful - it helps immensely with debugging post-mortem. At the same time though, avoid spamming the log for something trivial. * Write to the log for anything useful - it helps immensely with troubleshooting and debugging. At the same time though, avoid spamming the log for something trivial.
### UI text ### UI text
spice2x has a global audience; majority of the user base do not speak English as their first language. spice2x has a global audience; majority of the user base do not speak English as their first language.
@@ -44,7 +43,7 @@ Use simple English, avoid colloquialism, and use concise language, even if it's
### Using OS APIs ### Using OS APIs
Avoid making permanent changes to user's OS configuration. For example, spice should not make a call to set power profile to Maximum Performance, or switch default audio device. Making the reverse call to restore settings on game shutdown is **not** good enough; there is no guarantee that spice will gracefully shutdown, since games (or spice) can crash. This is to avoid inadvertently putting user's PC into a bad state, which can be seen as malware-like behavior. Avoid making permanent changes to user's OS configuration. For example, spice must not make a call to set power profile to Maximum Performance, or switch default audio device. Making the reverse call to restore settings on game shutdown is **not** good enough; there is no guarantee that spice will gracefully shutdown, since games (or spice) can crash. This is to avoid inadvertently putting user's PC into a bad state, which can be seen as malware-like behavior.
Watch out for legacy OS compatibility. Currently, the minimum support floor is Windows 7. If you use any Windows API, make sure it's supported in Windows 7. If you need to use API that is not present in Windows 7, you must not directly link against it, otherwise spice will fail to launch on older OSes. Take a look at Windows 8 touch code (win8.cpp) for examples on how to discover OS APIs via pointers. Watch out for legacy OS compatibility. Currently, the minimum support floor is Windows 7. If you use any Windows API, make sure it's supported in Windows 7. If you need to use API that is not present in Windows 7, you must not directly link against it, otherwise spice will fail to launch on older OSes. Take a look at Windows 8 touch code (win8.cpp) for examples on how to discover OS APIs via pointers.
@@ -87,4 +86,9 @@ if (conditional)
} }
``` ```
* Please give [C++ Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) a read. A few things to point out in spice codebase:
* We don't use GSL.
* Don't throw exceptions.
* Stick to smart pointers. For interfacing with C Win32 API and raw buffers, use `unique_plain_ptr`.
* Writing in C is also completely acceptable.
* Other than that, there are no strict rules for code formatting, but please attempt to emulate the style around the code you are modifying. * Other than that, there are no strict rules for code formatting, but please attempt to emulate the style around the code you are modifying.
+17 -7
View File
@@ -124,8 +124,8 @@ else()
# https://github.com/msys2/MINGW-packages/pull/6880 # https://github.com/msys2/MINGW-packages/pull/6880
# some games crash if some DLLS load above 4GB VA, so manually set base address to standard 32-bit VA, # some games crash if some DLLS load above 4GB VA, so manually set base address to standard 32-bit VA,
# and might as well double make sure ASLR is disabled here # and might as well double make sure ASLR is disabled here
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000,--enable-stdcall-fixup")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000,--enable-stdcall-fixup")
# set visibility to hidden # set visibility to hidden
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fvisibility=hidden") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fvisibility=hidden")
@@ -160,8 +160,8 @@ else()
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-fno-ident -ffunction-sections -fdata-sections") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-fno-ident -ffunction-sections -fdata-sections")
# linker fix to load below 4GB # linker fix to load below 4GB
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000,--enable-stdcall-fixup")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000,--enable-stdcall-fixup")
# set visibility to hidden # set visibility to hidden
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -fvisibility=hidden") set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -fvisibility=hidden")
@@ -184,8 +184,8 @@ else()
set(CMAKE_CXX_FLAGS_DEBUG "-gdwarf") set(CMAKE_CXX_FLAGS_DEBUG "-gdwarf")
# linker fix to load below 4GB # linker fix to load below 4GB
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--disable-dynamicbase,--image-base=0x400000") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--disable-dynamicbase,--image-base=0x400000,--enable-stdcall-fixup")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--disable-dynamicbase,--image-base=0x400000") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--disable-dynamicbase,--image-base=0x400000,--enable-stdcall-fixup")
# enable debug symbols on level 3 and keep frame pointers # enable debug symbols on level 3 and keep frame pointers
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g3 -fno-omit-frame-pointer") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g3 -fno-omit-frame-pointer")
@@ -738,6 +738,16 @@ if(NOT MSVC)
set_target_properties(spicetools_stubs_nvEncodeAPI64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64") set_target_properties(spicetools_stubs_nvEncodeAPI64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
endif() endif()
# cpusbxpkm.dll (32 bit)
set(SOURCE_FILES stubs/cpusbxpkm.cpp)
add_library(spicetools_stubs_cpusbxpkm SHARED ${SOURCE_FILES} stubs/cpusbxpkm.def)
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES PREFIX "")
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES OUTPUT_NAME "cpusbxpkm")
if(NOT MSVC)
set_target_properties(spicetools_stubs_cpusbxpkm PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
endif()
# output directories # output directories
#################### ####################
@@ -747,7 +757,7 @@ set_target_properties(spicetools_cfg
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools") RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools")
# output 32bit # output 32bit
set_target_properties(spicetools_spice spicetools_spice_laa spicetools_stubs_kbt spicetools_stubs_kld set_target_properties(spicetools_spice spicetools_spice_laa spicetools_stubs_kbt spicetools_stubs_kld spicetools_stubs_cpusbxpkm
PROPERTIES PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive32" ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive32"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32" LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32"
+1 -1
View File
@@ -31,7 +31,7 @@ namespace acio2emu {
} }
}; };
IOBHandle::IOBHandle(std::wstring device) : device_(device) { IOBHandle::IOBHandle(LPCWSTR device) : device_(device) {
nodes_[0] = std::make_unique<MasterNode>(this); nodes_[0] = std::make_unique<MasterNode>(this);
} }
+1 -1
View File
@@ -26,7 +26,7 @@ namespace acio2emu {
void forward_packet_(const Packet &packet); void forward_packet_(const Packet &packet);
public: public:
IOBHandle(std::wstring device); IOBHandle(LPCWSTR device);
bool register_node(std::unique_ptr<Node> node); bool register_node(std::unique_ptr<Node> node);
int number_of_nodes() const; int number_of_nodes() const;
+15 -3
View File
@@ -9,6 +9,9 @@ using namespace rapidjson;
namespace api::modules { namespace api::modules {
std::optional<uint32_t> CAPTURE_QUALITY;
std::optional<uint32_t> CAPTURE_DIVIDE;
static thread_local std::vector<uint8_t> CAPTURE_BUFFER; static thread_local std::vector<uint8_t> CAPTURE_BUFFER;
Capture::Capture() : Module("capture") { Capture::Capture() : Module("capture") {
@@ -44,12 +47,21 @@ namespace api::modules {
int screen = 0; int screen = 0;
int quality = 70; int quality = 70;
int divide = 1; int divide = 1;
if (req.params.Size() > 0 && req.params[0].IsUint()) if (req.params.Size() > 0 && req.params[0].IsUint()) {
screen = req.params[0].GetUint(); screen = req.params[0].GetUint();
if (req.params.Size() > 1 && req.params[1].IsUint()) }
if (CAPTURE_QUALITY.has_value()) {
quality = CAPTURE_QUALITY.value();
} else if (req.params.Size() > 1 && req.params[1].IsUint()) {
quality = req.params[1].GetUint(); quality = req.params[1].GetUint();
if (req.params.Size() > 2 && req.params[2].IsUint()) }
if (CAPTURE_DIVIDE.has_value()) {
divide = CAPTURE_DIVIDE.value();
} else if (req.params.Size() > 2 && req.params[2].IsUint()) {
divide = req.params[2].GetUint(); divide = req.params[2].GetUint();
}
// receive JPEG data // receive JPEG data
uint64_t timestamp = 0; uint64_t timestamp = 0;
+5
View File
@@ -1,10 +1,15 @@
#pragma once #pragma once
#include <optional>
#include "api/module.h" #include "api/module.h"
#include "api/request.h" #include "api/request.h"
namespace api::modules { namespace api::modules {
extern std::optional<uint32_t> CAPTURE_QUALITY;
extern std::optional<uint32_t> CAPTURE_DIVIDE;
class Capture : public Module { class Capture : public Module {
public: public:
Capture(); Capture();
+4 -2
View File
@@ -60,7 +60,7 @@ DIST_ENABLE=1
DIST_FOLDER="./dist" DIST_FOLDER="./dist"
DIST_NAME="spice2x-$(date +%y)-$(date +%m)-$(date +%d).zip" DIST_NAME="spice2x-$(date +%y)-$(date +%m)-$(date +%d).zip"
DIST_COMMENT=${DIST_NAME}$'\n'"$GIT_BRANCH - $GIT_HEAD"$'\nThank you for playing.' DIST_COMMENT=${DIST_NAME}$'\n'"$GIT_BRANCH - $GIT_HEAD"$'\nThank you for playing.'
TARGETS_32="spicetools_stubs_kbt spicetools_stubs_kld spicetools_cfg spicetools_spice spicetools_spice_laa" TARGETS_32="spicetools_stubs_kbt spicetools_stubs_kld spicetools_cfg spicetools_spice spicetools_spice_laa spicetools_stubs_cpusbxpkm"
TARGETS_64="spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvEncodeAPI64 spicetools_stubs_nvcuvid spicetools_stubs_nvcuda spicetools_spice64" TARGETS_64="spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvEncodeAPI64 spicetools_stubs_nvcuvid spicetools_stubs_nvcuda spicetools_spice64"
# determine build type # determine build type
@@ -181,7 +181,7 @@ fi
echo "Copy files to output directory..." echo "Copy files to output directory..."
rm -rf ${OUTDIR} rm -rf ${OUTDIR}
mkdir -p ${OUTDIR} mkdir -p ${OUTDIR}
#mkdir -p ${OUTDIR}/stubs/32 mkdir -p ${OUTDIR}/stubs/32
mkdir -p ${OUTDIR}/stubs/64 mkdir -p ${OUTDIR}/stubs/64
mkdir -p ${OUTDIR}/extras/largeaddressaware mkdir -p ${OUTDIR}/extras/largeaddressaware
if false # ((DEBUG > 0)) if false # ((DEBUG > 0))
@@ -200,6 +200,7 @@ then
#cp ${BUILDDIR_64}/spicetools/64/nvEncodeAPI64.dll ${OUTDIR}/stubs/64 2>/dev/null #cp ${BUILDDIR_64}/spicetools/64/nvEncodeAPI64.dll ${OUTDIR}/stubs/64 2>/dev/null
#cp ${BUILDDIR_64}/spicetools/64/nvcuda.dll ${OUTDIR}/stubs/64 2>/dev/null #cp ${BUILDDIR_64}/spicetools/64/nvcuda.dll ${OUTDIR}/stubs/64 2>/dev/null
#cp ${BUILDDIR_64}/spicetools/64/nvcuvid.dll ${OUTDIR}/stubs/64 2>/dev/null #cp ${BUILDDIR_64}/spicetools/64/nvcuvid.dll ${OUTDIR}/stubs/64 2>/dev/null
#cp ${BUILDDIR_32}/spicetools/32/cpusbxpkm.dll ${OUTDIR}/stubs/32 2>/dev/null
else else
# release files # release files
cp ${BUILDDIR_32}/spicetools/spicecfg.exe ${OUTDIR} 2>/dev/null cp ${BUILDDIR_32}/spicetools/spicecfg.exe ${OUTDIR} 2>/dev/null
@@ -213,6 +214,7 @@ else
cp ${BUILDDIR_64}/spicetools/64/nvEncodeAPI64.dll ${OUTDIR}/stubs/64 2>/dev/null cp ${BUILDDIR_64}/spicetools/64/nvEncodeAPI64.dll ${OUTDIR}/stubs/64 2>/dev/null
cp ${BUILDDIR_64}/spicetools/64/nvcuda.dll ${OUTDIR}/stubs/64 2>/dev/null cp ${BUILDDIR_64}/spicetools/64/nvcuda.dll ${OUTDIR}/stubs/64 2>/dev/null
cp ${BUILDDIR_64}/spicetools/64/nvcuvid.dll ${OUTDIR}/stubs/64 2>/dev/null cp ${BUILDDIR_64}/spicetools/64/nvcuvid.dll ${OUTDIR}/stubs/64 2>/dev/null
cp ${BUILDDIR_32}/spicetools/32/cpusbxpkm.dll ${OUTDIR}/stubs/32 2>/dev/null
fi fi
# pack source files to output directory # pack source files to output directory
+34 -4
View File
@@ -1,5 +1,8 @@
#include "api.h" #include "api.h"
#include <optional>
#include "launcher/superexit.h"
#include "rawinput/rawinput.h" #include "rawinput/rawinput.h"
#include "rawinput/piuio.h" #include "rawinput/piuio.h"
#include "util/time.h" #include "util/time.h"
@@ -64,18 +67,29 @@ GameAPI::Buttons::State GameAPI::Buttons::getState(rawinput::RawInputManager *ma
auto current_button = &_button; auto current_button = &_button;
auto alternatives = check_alts ? &current_button->getAlternatives() : nullptr; auto alternatives = check_alts ? &current_button->getAlternatives() : nullptr;
unsigned int button_count = 0; unsigned int button_count = 0;
std::optional<bool> window_has_focus;
while (true) { while (true) {
// naive behavior // naive behavior
if (current_button->isNaive()) { if (current_button->isNaive()) {
GameAPI::Buttons::State state;
auto vkey = current_button->getVKey();
// check for focus
if (vkey != INVALID_VKEY && rawinput::NAIVE_REQUIRE_FOCUS) {
if (!window_has_focus.has_value()) {
window_has_focus = superexit::has_focus();
}
if (!window_has_focus.value()) {
vkey = INVALID_VKEY;
}
}
// read // read
auto vkey = current_button->getVKey(); if (vkey == INVALID_VKEY) {
GameAPI::Buttons::State state;
if (vkey == 0xFF) {
state = BUTTON_NOT_PRESSED; state = BUTTON_NOT_PRESSED;
} else { } else {
state = (GetAsyncKeyState(current_button->getVKey()) & 0x8000) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED; state = (GetAsyncKeyState(vkey) & 0x8000) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
} }
// invert // invert
@@ -106,6 +120,16 @@ GameAPI::Buttons::State GameAPI::Buttons::getState(rawinput::RawInputManager *ma
auto &devid = current_button->getDeviceIdentifier(); auto &devid = current_button->getDeviceIdentifier();
auto device = manager->devices_get(devid, false); // TODO: fix to update only auto device = manager->devices_get(devid, false); // TODO: fix to update only
// check for focus
if (device && rawinput::RAWINPUT_REQUIRE_FOCUS) {
if (!window_has_focus.has_value()) {
window_has_focus = superexit::has_focus();
}
if (!window_has_focus.value()) {
device = nullptr;
}
}
// get state if device was marked as updated // get state if device was marked as updated
GameAPI::Buttons::State state = current_button->getLastState(); GameAPI::Buttons::State state = current_button->getLastState();
double *last_up = nullptr; double *last_up = nullptr;
@@ -571,6 +595,12 @@ float GameAPI::Analogs::getState(rawinput::Device *device, Analog &analog) {
switch (device->type) { switch (device->type) {
case rawinput::MOUSE: { case rawinput::MOUSE: {
// check for focus
if (rawinput::NAIVE_REQUIRE_FOCUS && !superexit::has_focus()) {
value = analog.getLastState();
break;
}
// get mouse position // get mouse position
auto mouse = device->mouseInfo; auto mouse = device->mouseInfo;
long pos; long pos;
+1 -1
View File
@@ -284,7 +284,7 @@ std::string Button::getDisplayString(rawinput::RawInputManager* manager) {
std::string vKeyString = fmt::format("{:#x}", vKey); std::string vKeyString = fmt::format("{:#x}", vKey);
// device must be existing // device must be existing
if (this->device_identifier.empty() && vKey == 0xFF) { if (this->device_identifier.empty() && vKey == INVALID_VKEY) {
return ""; return "";
} }
+6 -4
View File
@@ -33,12 +33,14 @@ enum ButtonAnalogType {
extern const char *ButtonAnalogTypeStr[]; extern const char *ButtonAnalogTypeStr[];
constexpr unsigned short INVALID_VKEY = UINT16_C(0xFF);
class Button { class Button {
private: private:
std::vector<Button> alternatives; std::vector<Button> alternatives;
std::string name; std::string name;
std::string device_identifier = ""; std::string device_identifier = "";
unsigned short vKey = 0xFF; unsigned short vKey = INVALID_VKEY;
ButtonAnalogType analog_type = BAT_NONE; ButtonAnalogType analog_type = BAT_NONE;
double debounce_up = 0.0; double debounce_up = 0.0;
double debounce_down = 0.0; double debounce_down = 0.0;
@@ -68,12 +70,12 @@ public:
if (this->override_enabled) { if (this->override_enabled) {
return true; return true;
} }
if (this->vKey != 0xFF) { if (this->vKey != INVALID_VKEY) {
return true; return true;
} }
for (auto &alternative : this->alternatives) { for (auto &alternative : this->alternatives) {
if (alternative.vKey != 0xFF) { if (alternative.vKey != INVALID_VKEY) {
return true; return true;
} }
} }
@@ -82,7 +84,7 @@ public:
} }
inline void clearBindings() { inline void clearBindings() {
vKey = 0xFF; vKey = INVALID_VKEY;
alternatives.clear(); alternatives.clear();
device_identifier = ""; device_identifier = "";
analog_type = BAT_NONE; analog_type = BAT_NONE;
+11
View File
@@ -1,3 +1,14 @@
05/09/2025 [spice2x]
Check for window focus when processing input (-inputfocus)
More options for IIDX/SDVX windowed subscreen
SpiceCompanion screen mirroring quality options
Various LovePlus improvements and fixes:
Allow camera access
Full screen mode
Add printer options
Add stub for printer DLL (cpusbxpkm.dll)
Logging improvements for better troubleshooting
04/25/2025 [spice2x] 04/25/2025 [spice2x]
LargeAddressAware variant of spice.exe for Metal Gear LargeAddressAware variant of spice.exe for Metal Gear
Save new JSON config files to %appdata%/spice2x Save new JSON config files to %appdata%/spice2x
+2 -2
View File
@@ -266,8 +266,8 @@ namespace games::iidx::poke {
y *= rawinput::TOUCHSCREEN_RANGE_Y; y *= rawinput::TOUCHSCREEN_RANGE_Y;
} else if (GRAPHICS_IIDX_WSUB) { } else if (GRAPHICS_IIDX_WSUB) {
// Scale to windowed subscreen // Scale to windowed subscreen
x *= GRAPHICS_IIDX_WSUB_WIDTH; x *= GRAPHICS_WSUB_WIDTH;
y *= GRAPHICS_IIDX_WSUB_HEIGHT; y *= GRAPHICS_WSUB_HEIGHT;
} else if (GENERIC_SUB_WINDOW_FULLSIZE || !overlay::OVERLAY->get_active()) { } else if (GENERIC_SUB_WINDOW_FULLSIZE || !overlay::OVERLAY->get_active()) {
// Overlay is not present, scale to main screen // Overlay is not present, scale to main screen
if (GRAPHICS_WINDOWED) { if (GRAPHICS_WINDOWED) {
+34 -9
View File
@@ -13,10 +13,14 @@
namespace games::loveplus { namespace games::loveplus {
bool CAMERA_ENABLE = false;
// touch stuff // touch stuff
static bool TOUCH_ENABLE = false; static bool TOUCH_ENABLE = false;
static bool TOUCH_ATTACHED = false; static bool TOUCH_ATTACHED = false;
static std::string lp_args = "-noWatchDog -noIOError -noIrda -notarget";
void touch_update() { void touch_update() {
// check if touch enabled // check if touch enabled
@@ -28,16 +32,15 @@ namespace games::loveplus {
if (!TOUCH_ATTACHED) { if (!TOUCH_ATTACHED) {
// Find the game window. // Find the game window.
// We check the foreground window first, then fall back to searching for the window title HWND wnd = FindProcessWindowBeginsWith("LovePlusAC");
// All game versions seem to have their model first in the window title
HWND wnd = GetForegroundWindow();
if (!string_begins_with(GetActiveWindowTitle(), "LovePlus")) {
wnd = FindWindowBeginsWith(avs::game::MODEL);
}
// attach touch hook // attach touch hook
if (wnd) { if (wnd) {
log_info("loveplus", "using window handle for touch: {}", fmt::ptr(wnd)); log_info(
"loveplus",
"using window handle for touch: {} ({})",
fmt::ptr(wnd),
get_window_title(wnd));
touch_create_wnd(wnd); touch_create_wnd(wnd);
} else { } else {
log_info("loveplus", "falling back to the DirectX window handle for touch"); log_info("loveplus", "falling back to the DirectX window handle for touch");
@@ -117,12 +120,26 @@ namespace games::loveplus {
} }
static LPSTR __stdcall GetCommandLineA_hook() { static LPSTR __stdcall GetCommandLineA_hook() {
static std::string lp_args = "-win -noCamera -noWatchDog -noIOError -noIrda -notarget";
return lp_args.data(); return lp_args.data();
} }
static bool lp_spam_remover(void *user, const std::string &data, logger::Style style, std::string &out) {
if (data.empty() || data[0] != '[') {
return false;
}
if (data.find("> [INFO] err_code: 0, err_title: , err_str:") != std::string::npos) {
out = "";
return true;
}
return false;
}
LovePlusGame::LovePlusGame() : Game("LovePlus") { LovePlusGame::LovePlusGame() : Game("LovePlus") {
logger::hook_add(lp_spam_remover, nullptr);
}
LovePlusGame::~LovePlusGame() {
logger::hook_remove(lp_spam_remover, this);
} }
void LovePlusGame::attach() { void LovePlusGame::attach() {
@@ -157,9 +174,17 @@ namespace games::loveplus {
HMODULE seteq = libutils::try_library("ad_hd_seteq_dll.dll"); HMODULE seteq = libutils::try_library("ad_hd_seteq_dll.dll");
detour::inline_hook((void *) SetEqualizer, libutils::try_proc(seteq, "SetEqualizer")); detour::inline_hook((void *) SetEqualizer, libutils::try_proc(seteq, "SetEqualizer"));
// set up command line args
if (!CAMERA_ENABLE) {
lp_args += " -noCamera";
}
if (GRAPHICS_WINDOWED) {
lp_args += " -win";
}
// get command line hook // get command line hook
HMODULE lpac = libutils::try_library("lpac.dll"); HMODULE lpac = libutils::try_library("lpac.dll");
detour::iat("GetCommandLineA", GetCommandLineA_hook, lpac); detour::iat("GetCommandLineA", GetCommandLineA_hook, lpac);
} }
void LovePlusGame::detach() { void LovePlusGame::detach() {
+3
View File
@@ -4,12 +4,15 @@
namespace games::loveplus { namespace games::loveplus {
extern bool CAMERA_ENABLE;
// touch stuff // touch stuff
void touch_update(); void touch_update();
class LovePlusGame : public games::Game { class LovePlusGame : public games::Game {
public: public:
LovePlusGame(); LovePlusGame();
virtual ~LovePlusGame() override;
virtual void attach() override; virtual void attach() override;
virtual void detach() override; virtual void detach() override;
}; };
+37 -17
View File
@@ -294,7 +294,7 @@ namespace games::shared {
// error // error
if (!clear) { if (!clear) {
log_fatal("sdvx", "could not find path"); log_fatal("printer", "could not find path");
} }
return "DUMMY"; return "DUMMY";
@@ -326,7 +326,7 @@ namespace games::shared {
memcpy(image_data, pBandImage->baseAddr, (size_t) (image_width * image_height * 3)); memcpy(image_data, pBandImage->baseAddr, (size_t) (image_width * image_height * 3));
// convert BGR to RGB // convert BGR to RGB
log_info("printer", "converting BGR to RGB..."); log_misc("printer", "converting BGR to RGB...");
for (int pixel = 0; pixel < image_width * image_height; pixel++) { for (int pixel = 0; pixel < image_width * image_height; pixel++) {
int index = pixel * 3; int index = pixel * 3;
uint8_t tmp = image_data[index]; uint8_t tmp = image_data[index];
@@ -334,25 +334,45 @@ namespace games::shared {
image_data[index + 2] = tmp; image_data[index + 2] = tmp;
} }
// flip horizontally if (avs::game::is_model({"KLP", "KFC"})) {
for (int x = 0; x < image_width / 2; x++) { // rotate image clockwise
for (int y = 0; y < image_height; y++) { log_misc("printer", "rotate clockwise...");
int index1 = (y * image_width + x) * 3; auto rotated = new uint8_t[image_width * image_height * 3];
int index2 = (y * image_width + image_width - x - 1) * 3; for (int x = 0; x < image_width; x++) {
uint8_t r = image_data[index1 + 0]; for (int y = 0; y < image_height; y++) {
uint8_t g = image_data[index1 + 1]; int index1 = (y * image_width + x) * 3;
uint8_t b = image_data[index1 + 2]; int index2 = (x * image_height + y) * 3;
image_data[index1 + 0] = image_data[index2 + 0]; rotated[index2 + 0] = image_data[index1 + 0];
image_data[index1 + 1] = image_data[index2 + 1]; rotated[index2 + 1] = image_data[index1 + 1];
image_data[index1 + 2] = image_data[index2 + 2]; rotated[index2 + 2] = image_data[index1 + 2];
image_data[index2 + 0] = r; }
image_data[index2 + 1] = g; }
image_data[index2 + 2] = b; delete[] image_data;
image_data = rotated;
std::swap(image_width, image_height);
} else {
// flip horizontally
log_misc("printer", "flip horizontally...");
for (int x = 0; x < image_width / 2; x++) {
for (int y = 0; y < image_height; y++) {
int index1 = (y * image_width + x) * 3;
int index2 = (y * image_width + image_width - x - 1) * 3;
uint8_t r = image_data[index1 + 0];
uint8_t g = image_data[index1 + 1];
uint8_t b = image_data[index1 + 2];
image_data[index1 + 0] = image_data[index2 + 0];
image_data[index1 + 1] = image_data[index2 + 1];
image_data[index1 + 2] = image_data[index2 + 2];
image_data[index2 + 0] = r;
image_data[index2 + 1] = g;
image_data[index2 + 2] = b;
}
} }
} }
// iterate folders // iterate folders
log_info("printer", "writing files..."); log_misc("printer", "writing files...");
for (const auto &path : PRINTER_PATH) { for (const auto &path : PRINTER_PATH) {
for (const auto &format : PRINTER_FORMAT) { for (const auto &format : PRINTER_FORMAT) {
+27 -3
View File
@@ -374,6 +374,27 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
if ((is_tdj_sub_window && GRAPHICS_IIDX_WSUB) || is_sdvx_sub_window) { if ((is_tdj_sub_window && GRAPHICS_IIDX_WSUB) || is_sdvx_sub_window) {
dwStyle &= ~(WS_MAXIMIZEBOX); dwStyle &= ~(WS_MAXIMIZEBOX);
} }
if ((is_tdj_sub_window || is_sdvx_sub_window) && GRAPHICS_WSUB_BORDERLESS) {
dwStyle &= ~(WS_OVERLAPPEDWINDOW);
}
if (is_sdvx_sub_window) {
graphics_load_windowed_subscreen_parameters();
if (GRAPHICS_WSUB_SIZE.has_value()) {
nWidth = GRAPHICS_WSUB_WIDTH;
nHeight = GRAPHICS_WSUB_HEIGHT;
} else {
GRAPHICS_WSUB_WIDTH = nWidth;
GRAPHICS_WSUB_HEIGHT = nHeight;
}
if (GRAPHICS_WSUB_POS.has_value()) {
x = GRAPHICS_WSUB_X;
y = GRAPHICS_WSUB_Y;
} else {
GRAPHICS_WSUB_X = x;
GRAPHICS_WSUB_Y = y;
}
}
// call original // call original
HWND result = CreateWindowExA_orig(dwExStyle, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, HWND result = CreateWindowExA_orig(dwExStyle, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight,
@@ -529,11 +550,11 @@ static BOOL WINAPI MoveWindow_hook(HWND hWnd, int X, int Y, int nWidth, int nHei
dwStyle = GetWindowLongA(hWnd, GWL_STYLE); dwStyle = GetWindowLongA(hWnd, GWL_STYLE);
SetRect(&rect, 0, 0, GRAPHICS_IIDX_WSUB_WIDTH, GRAPHICS_IIDX_WSUB_HEIGHT); SetRect(&rect, 0, 0, GRAPHICS_WSUB_WIDTH, GRAPHICS_WSUB_HEIGHT);
AdjustWindowRect(&rect, dwStyle, 0); AdjustWindowRect(&rect, dwStyle, 0);
X = GRAPHICS_IIDX_WSUB_X; X = GRAPHICS_WSUB_X;
Y = GRAPHICS_IIDX_WSUB_Y; Y = GRAPHICS_WSUB_Y;
nWidth = rect.right - rect.left; nWidth = rect.right - rect.left;
nHeight = rect.bottom - rect.top; nHeight = rect.bottom - rect.top;
@@ -865,6 +886,9 @@ void graphics_hook_subscreen_window(HWND hWnd) {
WSUB_WNDPROC_ORIG = reinterpret_cast<WNDPROC>(GetWindowLongPtrA(hWnd, GWLP_WNDPROC)); WSUB_WNDPROC_ORIG = reinterpret_cast<WNDPROC>(GetWindowLongPtrA(hWnd, GWLP_WNDPROC));
SetWindowLongPtrA(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(WsubWindowProc)); SetWindowLongPtrA(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(WsubWindowProc));
} }
if (GRAPHICS_WSUB_ALWAYS_ON_TOP) {
graphics_update_z_order(hWnd, true);
}
} }
void graphics_screens_register(int screen) { void graphics_screens_register(int screen) {
+9 -7
View File
@@ -50,12 +50,14 @@ extern bool GRAPHICS_WINDOW_ALWAYS_ON_TOP;
extern bool GRAPHICS_WINDOW_BACKBUFFER_SCALE; extern bool GRAPHICS_WINDOW_BACKBUFFER_SCALE;
extern bool GRAPHICS_IIDX_WSUB; extern bool GRAPHICS_IIDX_WSUB;
extern std::optional<std::string> GRAPHICS_IIDX_WSUB_SIZE; extern std::optional<std::string> GRAPHICS_WSUB_SIZE;
extern std::optional<std::string> GRAPHICS_IIDX_WSUB_POS; extern std::optional<std::string> GRAPHICS_WSUB_POS;
extern int GRAPHICS_IIDX_WSUB_WIDTH; extern int GRAPHICS_WSUB_WIDTH;
extern int GRAPHICS_IIDX_WSUB_HEIGHT; extern int GRAPHICS_WSUB_HEIGHT;
extern int GRAPHICS_IIDX_WSUB_X; extern int GRAPHICS_WSUB_X;
extern int GRAPHICS_IIDX_WSUB_Y; extern int GRAPHICS_WSUB_Y;
extern bool GRAPHICS_WSUB_BORDERLESS;
extern bool GRAPHICS_WSUB_ALWAYS_ON_TOP;
extern HWND TDJ_SUBSCREEN_WINDOW; extern HWND TDJ_SUBSCREEN_WINDOW;
extern HWND SDVX_SUBSCREEN_WINDOW; extern HWND SDVX_SUBSCREEN_WINDOW;
@@ -94,7 +96,7 @@ std::string graphics_screenshot_genpath();
void graphics_windowed_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); void graphics_windowed_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void graphics_capture_initial_window(HWND hWnd); void graphics_capture_initial_window(HWND hWnd);
void graphics_update_window_style(HWND hWnd); void graphics_update_window_style(HWND hWnd);
void graphics_update_z_order(HWND hWnd); void graphics_update_z_order(HWND hWnd, bool always_on_top);
void graphics_move_resize_window(HWND hWnd); void graphics_move_resize_window(HWND hWnd);
bool graphics_window_change_crashes_game(); bool graphics_window_change_crashes_game();
void graphics_load_windowed_subscreen_parameters(); void graphics_load_windowed_subscreen_parameters();
@@ -26,12 +26,14 @@ bool GRAPHICS_WINDOW_BACKBUFFER_SCALE = false;
// IIDX Windowed Subscreen - starts out as false, enabled by IIDX module on pre-attach as needed // IIDX Windowed Subscreen - starts out as false, enabled by IIDX module on pre-attach as needed
bool GRAPHICS_IIDX_WSUB = false; bool GRAPHICS_IIDX_WSUB = false;
std::optional<std::string> GRAPHICS_IIDX_WSUB_SIZE; std::optional<std::string> GRAPHICS_WSUB_SIZE;
std::optional<std::string> GRAPHICS_IIDX_WSUB_POS; std::optional<std::string> GRAPHICS_WSUB_POS;
int GRAPHICS_IIDX_WSUB_WIDTH = 1280; int GRAPHICS_WSUB_WIDTH = 1280;
int GRAPHICS_IIDX_WSUB_HEIGHT = 720; int GRAPHICS_WSUB_HEIGHT = 720;
int GRAPHICS_IIDX_WSUB_X = 0; int GRAPHICS_WSUB_X = 0;
int GRAPHICS_IIDX_WSUB_Y = 0; int GRAPHICS_WSUB_Y = 0;
bool GRAPHICS_WSUB_BORDERLESS = false;
bool GRAPHICS_WSUB_ALWAYS_ON_TOP = false;
// these flags are carefully constructed to ensure maximum compatibility // these flags are carefully constructed to ensure maximum compatibility
// (e.g., DDR likes to hang when SetWindowPos is called with certain params) // (e.g., DDR likes to hang when SetWindowPos is called with certain params)
@@ -108,7 +110,7 @@ void graphics_capture_initial_window(HWND hWnd) {
} }
if (cfg::SCREENRESIZE->window_always_on_top) { 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");
graphics_update_z_order(hWnd); graphics_update_z_order(hWnd, true);
} }
// ensure spicetouch coordinates are initialized // ensure spicetouch coordinates are initialized
@@ -169,29 +171,29 @@ void graphics_load_windowed_subscreen_parameters() {
log_debug("graphics-windowed", "graphics_load_windowed_subscreen_parameters called"); log_debug("graphics-windowed", "graphics_load_windowed_subscreen_parameters called");
if (GRAPHICS_IIDX_WSUB_SIZE.has_value()) { if (GRAPHICS_WSUB_SIZE.has_value()) {
log_debug( log_debug(
"graphics-windowed", "graphics-windowed",
"graphics_load_windowed_parameters - load GRAPHICS_IIDX_WSUB_SIZE"); "graphics_load_windowed_parameters - load GRAPHICS_WSUB_SIZE");
std::pair<uint32_t, uint32_t> result; std::pair<uint32_t, uint32_t> result;
if (parse_width_height(GRAPHICS_IIDX_WSUB_SIZE.value(), result)) { if (parse_width_height(GRAPHICS_WSUB_SIZE.value(), result)) {
GRAPHICS_IIDX_WSUB_WIDTH = result.first; GRAPHICS_WSUB_WIDTH = result.first;
GRAPHICS_IIDX_WSUB_HEIGHT = result.second; GRAPHICS_WSUB_HEIGHT = result.second;
} else { } else {
log_warning("graphics-windowed", "failed to parse -wsubsize"); log_warning("graphics-windowed", "failed to parse -wsubsize");
} }
} }
if (GRAPHICS_IIDX_WSUB_POS.has_value()) { if (GRAPHICS_WSUB_POS.has_value()) {
log_debug( log_debug(
"graphics-windowed", "graphics-windowed",
"graphics_load_windowed_parameters - load GRAPHICS_IIDX_WSUB_POS"); "graphics_load_windowed_parameters - load GRAPHICS_WSUB_POS");
std::pair<uint32_t, uint32_t> result; std::pair<uint32_t, uint32_t> result;
if (parse_width_height(GRAPHICS_IIDX_WSUB_POS.value(), result)) { if (parse_width_height(GRAPHICS_WSUB_POS.value(), result)) {
GRAPHICS_IIDX_WSUB_X = result.first; GRAPHICS_WSUB_X = result.first;
GRAPHICS_IIDX_WSUB_Y = result.second; GRAPHICS_WSUB_Y = result.second;
} else { } else {
log_warning("graphics-windowed", "failed to parse -wsubpos"); log_warning("graphics-windowed", "failed to parse -wsubpos");
} }
@@ -398,7 +400,7 @@ void graphics_update_window_style(HWND hWnd) {
log_debug("graphics-windowed", "graphics_update_window_style returned"); log_debug("graphics-windowed", "graphics_update_window_style returned");
} }
void graphics_update_z_order(HWND hWnd) { void graphics_update_z_order(HWND hWnd, bool always_on_top) {
if (!GRAPHICS_WINDOWED) { if (!GRAPHICS_WINDOWED) {
return; return;
} }
@@ -406,7 +408,7 @@ void graphics_update_z_order(HWND hWnd) {
log_debug("graphics-windowed", "graphics_update_z_order called"); log_debug("graphics-windowed", "graphics_update_z_order called");
HWND insert_after = nullptr; HWND insert_after = nullptr;
if (cfg::SCREENRESIZE->window_always_on_top) { if (always_on_top) {
insert_after = HWND_TOPMOST; insert_after = HWND_TOPMOST;
} else { } else {
insert_after = HWND_NOTOPMOST; insert_after = HWND_NOTOPMOST;
+1 -1
View File
@@ -179,7 +179,7 @@ void networkhook_init() {
strncpy(s_prefix, inet_ntoa(prefix), 16); strncpy(s_prefix, inet_ntoa(prefix), 16);
// log preferences // log preferences
log_info("network", "Network preferences: {}", s_network, s_subnet, s_prefix); // log_info("network", "Network preferences: {}, {}, {}", s_network, s_subnet, s_prefix);
// GetAdaptersInfo hook // GetAdaptersInfo hook
auto orig_addr = detour::iat_try( auto orig_addr = detour::iat_try(
+79 -10
View File
@@ -6,6 +6,7 @@
#include <shlwapi.h> #include <shlwapi.h>
#include <cfg/configurator.h> #include <cfg/configurator.h>
#include "api/modules/capture.h"
#include "acio/acio.h" #include "acio/acio.h"
#include "acio/icca/icca.h" #include "acio/icca/icca.h"
#include "api/controller.h" #include "api/controller.h"
@@ -661,21 +662,42 @@ int main_implementation(int argc, char *argv[]) {
if (options[launcher::Options::SDVXPrinterEmulation].value_bool()) { if (options[launcher::Options::SDVXPrinterEmulation].value_bool()) {
attach_cpusbxpkm_printer = true; attach_cpusbxpkm_printer = true;
} }
if (options[launcher::Options::SDVXPrinterOutputOverwrite].value_bool()) {
// printer overwrite
if (options[launcher::Options::SDVXPrinterOutputOverwrite].value_bool() ||
options[launcher::Options::LovePlusPrinterOutputOverwrite].value_bool()) {
games::shared::PRINTER_OVERWRITE_FILE = true; games::shared::PRINTER_OVERWRITE_FILE = true;
} }
// printer output path
for (auto &path : options[launcher::Options::SDVXPrinterOutputPath].values_text()) { for (auto &path : options[launcher::Options::SDVXPrinterOutputPath].values_text()) {
games::shared::PRINTER_PATH.push_back(path); games::shared::PRINTER_PATH.push_back(path);
} }
for (auto &path : options[launcher::Options::LovePlusPrinterOutputPath].values_text()) {
games::shared::PRINTER_PATH.push_back(path);
}
// printer file format
for (auto &path : options[launcher::Options::SDVXPrinterOutputFormat].values_text()) { for (auto &path : options[launcher::Options::SDVXPrinterOutputFormat].values_text()) {
games::shared::PRINTER_FORMAT.push_back(path); games::shared::PRINTER_FORMAT.push_back(path);
} }
for (auto &path : options[launcher::Options::LovePlusPrinterOutputFormat].values_text()) {
games::shared::PRINTER_FORMAT.push_back(path);
}
// printer JPG quality
if (options[launcher::Options::SDVXPrinterJPGQuality].is_active()) { if (options[launcher::Options::SDVXPrinterJPGQuality].is_active()) {
games::shared::PRINTER_JPG_QUALITY = options[launcher::Options::SDVXPrinterJPGQuality].value_uint32(); games::shared::PRINTER_JPG_QUALITY = options[launcher::Options::SDVXPrinterJPGQuality].value_uint32();
} else if (options[launcher::Options::LovePlusPrinterJPGQuality].is_active()) {
games::shared::PRINTER_JPG_QUALITY = options[launcher::Options::LovePlusPrinterJPGQuality].value_uint32();
} }
if (options[launcher::Options::SDVXPrinterOutputClear].value_bool()) {
// printer output clear
if (options[launcher::Options::SDVXPrinterOutputClear].value_bool() ||
options[launcher::Options::LovePlusPrinterOutputClear].value_bool()) {
games::shared::PRINTER_CLEAR = true; games::shared::PRINTER_CLEAR = true;
} }
if (options[launcher::Options::HTTP11].is_active()) { if (options[launcher::Options::HTTP11].is_active()) {
avs::ea3::HTTP11 = options[launcher::Options::HTTP11].value_uint32(); avs::ea3::HTTP11 = options[launcher::Options::HTTP11].value_uint32();
} }
@@ -813,6 +835,7 @@ int main_implementation(int argc, char *argv[]) {
if (options[launcher::Options::AdapterSubnet].is_active()) { if (options[launcher::Options::AdapterSubnet].is_active()) {
NETWORK_SUBNET = options[launcher::Options::AdapterSubnet].value_text(); NETWORK_SUBNET = options[launcher::Options::AdapterSubnet].value_text();
} }
if (options[launcher::Options::APITCPPort].is_active()) { if (options[launcher::Options::APITCPPort].is_active()) {
api_enable = true; api_enable = true;
api_port = options[launcher::Options::APITCPPort].value_uint32(); api_port = options[launcher::Options::APITCPPort].value_uint32();
@@ -835,6 +858,13 @@ int main_implementation(int argc, char *argv[]) {
if (options[launcher::Options::APIDebugMode].value_bool()) { if (options[launcher::Options::APIDebugMode].value_bool()) {
api_debug = true; api_debug = true;
} }
if (options[launcher::Options::APIScreenMirrorQuality].is_active()) {
api::modules::CAPTURE_QUALITY = options[launcher::Options::APIScreenMirrorQuality].value_uint32();
}
if (options[launcher::Options::APIScreenMirrorDivide].is_active()) {
api::modules::CAPTURE_DIVIDE = options[launcher::Options::APIScreenMirrorDivide].value_uint32();
}
if (options[launcher::Options::DisableDebugHooks].value_bool()) { if (options[launcher::Options::DisableDebugHooks].value_bool()) {
debughook::DEBUGHOOK_LOGGING = false; debughook::DEBUGHOOK_LOGGING = false;
} }
@@ -976,12 +1006,24 @@ int main_implementation(int argc, char *argv[]) {
GRAPHICS_WINDOW_ALWAYS_ON_TOP = options[launcher::Options::spice2x_WindowAlwaysOnTop].value_bool(); GRAPHICS_WINDOW_ALWAYS_ON_TOP = options[launcher::Options::spice2x_WindowAlwaysOnTop].value_bool();
GRAPHICS_WINDOW_BACKBUFFER_SCALE = options[launcher::Options::WindowForceScaling].value_bool(); GRAPHICS_WINDOW_BACKBUFFER_SCALE = options[launcher::Options::WindowForceScaling].value_bool();
// IIDX Windowed Subscreen // IIDX/SDVX Windowed Subscreen
if (options[launcher::Options::spice2x_IIDXWindowedSubscreenSize].is_active()) { if (options[launcher::Options::spice2x_IIDXWindowedSubscreenSize].is_active()) {
GRAPHICS_IIDX_WSUB_SIZE = options[launcher::Options::spice2x_IIDXWindowedSubscreenSize].value_text(); GRAPHICS_WSUB_SIZE = options[launcher::Options::spice2x_IIDXWindowedSubscreenSize].value_text();
} else if (options[launcher::Options::SDVXWindowedSubscreenSize].is_active()) {
GRAPHICS_WSUB_SIZE = options[launcher::Options::SDVXWindowedSubscreenSize].value_text();
} }
if (options[launcher::Options::spice2x_IIDXWindowedSubscreenPosition].is_active()) { if (options[launcher::Options::spice2x_IIDXWindowedSubscreenPosition].is_active()) {
GRAPHICS_IIDX_WSUB_POS = options[launcher::Options::spice2x_IIDXWindowedSubscreenPosition].value_text(); GRAPHICS_WSUB_POS = options[launcher::Options::spice2x_IIDXWindowedSubscreenPosition].value_text();
} else if (options[launcher::Options::SDVXWindowedSubscreenPosition].is_active()) {
GRAPHICS_WSUB_POS = options[launcher::Options::SDVXWindowedSubscreenPosition].value_text();
}
if (options[launcher::Options::IIDXWindowedSubscreenBorderless].value_bool() ||
options[launcher::Options::SDVXWindowedSubscreenBorderless].value_bool()) {
GRAPHICS_WSUB_BORDERLESS = true;
}
if (options[launcher::Options::IIDXWindowedSubscreenAlwaysOnTop].value_bool() ||
options[launcher::Options::SDVXWindowedSubscreenAlwaysOnTop].value_bool()) {
GRAPHICS_WSUB_ALWAYS_ON_TOP = true;
} }
if (options[launcher::Options::spice2x_JubeatLegacyTouch].value_bool()) { if (options[launcher::Options::spice2x_JubeatLegacyTouch].value_bool()) {
@@ -1056,6 +1098,17 @@ int main_implementation(int argc, char *argv[]) {
if (options[launcher::Options::spice2x_EnableSMXDedicab].value_bool()) { if (options[launcher::Options::spice2x_EnableSMXDedicab].value_bool()) {
rawinput::ENABLE_SMX_DEDICAB = true; rawinput::ENABLE_SMX_DEDICAB = true;
} }
if (options[launcher::Options::InputRequiresFocus].is_active()) {
const auto &text = options[launcher::Options::InputRequiresFocus].value_text();
if (text == "always") {
rawinput::NAIVE_REQUIRE_FOCUS = true;
rawinput::RAWINPUT_REQUIRE_FOCUS = true;
} else if (text == "never") {
rawinput::NAIVE_REQUIRE_FOCUS = false;
rawinput::RAWINPUT_REQUIRE_FOCUS = false;
}
}
if (options[launcher::Options::MidiAlgoVer].is_active()) { if (options[launcher::Options::MidiAlgoVer].is_active()) {
if (options[launcher::Options::MidiAlgoVer].value_text() == "legacy") { if (options[launcher::Options::MidiAlgoVer].value_text() == "legacy") {
midi_algo = rawinput::MidiNoteAlgorithm::LEGACY; midi_algo = rawinput::MidiNoteAlgorithm::LEGACY;
@@ -1077,6 +1130,10 @@ int main_implementation(int argc, char *argv[]) {
rawinput::MIDI_NOTE_SUSTAIN = options[launcher::Options::MidiNoteSustain].value_uint32(); rawinput::MIDI_NOTE_SUSTAIN = options[launcher::Options::MidiNoteSustain].value_uint32();
} }
if (options[launcher::Options::LovePlusCamEnable].value_bool()) {
games::loveplus::CAMERA_ENABLE = true;
}
// API debugging // API debugging
if (api_debug && !cfg::CONFIGURATOR_STANDALONE) { if (api_debug && !cfg::CONFIGURATOR_STANDALONE) {
API_CONTROLLER = std::make_unique<api::Controller>(api_port, api_pass, api_pretty); API_CONTROLLER = std::make_unique<api::Controller>(api_port, api_pass, api_pretty);
@@ -1172,16 +1229,18 @@ int main_implementation(int argc, char *argv[]) {
} }
} }
// // SDVXFullscreenLandscape disabled due to it messing with in-game camera angle // SDVXFullscreenLandscape disabled due to it messing with in-game camera angle
// // FullscreenOrientationFlip continues to live on, however. // FullscreenOrientationFlip continues to live on, however.
// if (options[launcher::Options::SDVXFullscreenLandscape].value_bool()) {
// if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !GRAPHICS_WINDOWED) { log_fatal("launcher", "-sdvxlandscape removed due to a camera bug in SDVX!");
}
// if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !GRAPHICS_WINDOWED) {
// #if SPICE64 // #if SPICE64
// GRAPHICS_FS_ORIENTATION_SWAP = true; // GRAPHICS_FS_ORIENTATION_SWAP = true;
// #else // #else
// log_warning("launcher", "-sdvxlandscape is not supported in 32-bit SDVX, ignoring..."); // log_warning("launcher", "-sdvxlandscape is not supported in 32-bit SDVX, ignoring...");
// #endif // #endif
// } // }
if (options[launcher::Options::FullscreenOrientationFlip].value_bool() && !GRAPHICS_WINDOWED) { if (options[launcher::Options::FullscreenOrientationFlip].value_bool() && !GRAPHICS_WINDOWED) {
GRAPHICS_FS_ORIENTATION_SWAP = true; GRAPHICS_FS_ORIENTATION_SWAP = true;
@@ -1806,6 +1865,16 @@ int main_implementation(int argc, char *argv[]) {
exit(spicecfg_run(sextet_devices)); exit(spicecfg_run(sextet_devices));
} }
// log some DLLs found in path (purely for troubleshooting purposes to detect
// dxvk, ForceD3D9On12, ifs_layeredfs, etc)
libutils::warn_if_dll_exists("d3d8.dll");
libutils::warn_if_dll_exists("d3d9.dll");
libutils::warn_if_dll_exists("d3d10core.dll");
libutils::warn_if_dll_exists("d3d11.dll");
libutils::warn_if_dll_exists("d3d12.dll");
libutils::warn_if_dll_exists("dxgi.dll");
libutils::warn_if_dll_exists("opengl32.dll");
// complain loudly & early about dll load ordering issue // complain loudly & early about dll load ordering issue
libutils::check_duplicate_dlls(); libutils::check_duplicate_dlls();
+149 -2
View File
@@ -306,6 +306,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
"Force the use of an adapter with the specified network. Must also provide -subnet", "Force the use of an adapter with the specified network. Must also provide -subnet",
.type = OptionType::Text, .type = OptionType::Text,
.category = "Network Adapters", .category = "Network Adapters",
.sensitive = true,
}, },
{ {
.title = "Preferred NetAdapter Subnet", .title = "Preferred NetAdapter Subnet",
@@ -693,7 +694,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
{ {
.title = "SDVX Printer Output Format", .title = "SDVX Printer Output Format",
.name = "printerformat", .name = "printerformat",
.desc = "Path to folder where images will be stored", .desc = "File format for printer output",
.type = OptionType::Text, .type = OptionType::Text,
.setting_name = "(png/bmp/tga/jpg)", .setting_name = "(png/bmp/tga/jpg)",
.game_name = "Sound Voltex", .game_name = "Sound Voltex",
@@ -1193,6 +1194,28 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.type = OptionType::Bool, .type = OptionType::Bool,
.category = "API (Dev)", .category = "API (Dev)",
}, },
{
// APIScreenMirrorQuality
.title = "API Screen Mirror Quality Override",
.name = "apiscreenq",
.desc = "JPEG compression level of mirrored images, overriding any client request. "
"Value must be between 0 (poor quality) and 100 (best quality), inclusive. Default: 70",
.type = OptionType::Integer,
.setting_name = "(0-100)",
.category = "SpiceCompanion and API",
},
{
// APIScreenMirrorDivide
.title = "API Screen Mirror Divide Override",
.name = "apiscreendiv",
.desc = "Divide value of mirrored images, overriding any client request. "
"Divide of 1 means send every pixel, 2 means every other pixel, 3 means every third pixel, and so on; "
"increasing the value drastically reduces transfer size at the cost of image quality. "
"Value must be 1 or greater. Default: 1",
.type = OptionType::Integer,
.setting_name = "1",
.category = "SpiceCompanion and API",
},
{ {
.title = "Enable All IO Modules", .title = "Enable All IO Modules",
.name = "io", .name = "io",
@@ -1842,6 +1865,24 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.game_name = "Beatmania IIDX", .game_name = "Beatmania IIDX",
.category = "Graphics (Windowed)", .category = "Graphics (Windowed)",
}, },
{
// IIDXWindowedSubscreenBorderless
.title = "IIDX Windowed Subscreen Borderless",
.name = "iidxwsubborderless",
.desc = "Remove window decoration from windowed subscreen",
.type = OptionType::Bool,
.game_name = "Beatmania IIDX",
.category = "Graphics (Windowed)",
},
{
// IIDXWindowedSubscreenAlwaysOnTop
.title = "IIDX Windowed Subscreen Always On Top",
.name = "iidxwsubtop",
.desc = "Keep windowed subscreen on top",
.type = OptionType::Bool,
.game_name = "Beatmania IIDX",
.category = "Graphics (Windowed)",
},
{ {
// spice2x_JubeatLegacyTouch // spice2x_JubeatLegacyTouch
.title = "JB Legacy Touch Targets", .title = "JB Legacy Touch Targets",
@@ -2129,6 +2170,23 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.setting_name = "20", .setting_name = "20",
.category = "I/O Options", .category = "I/O Options",
}, },
{
// InputRequiresFocus
.title = "Input Requires Focus",
.name = "inputfocus",
.desc =
"Determine input behavior when game window is not in focus.\n\n"
"never: allow input regardless of focus\n"
"naive (default): only naive bindings check for window focus, rawinput binds are always allowed\n"
"always: only process input when window is in focus",
.type = OptionType::Enum,
.category = "I/O Options",
.elements = {
{"never", ""},
{"naive", ""},
{"always", ""},
},
},
{ {
// NostalgiaPoke // NostalgiaPoke
.title = "Nost Screen Poke", .title = "Nost Screen Poke",
@@ -2153,7 +2211,96 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
{"2", "double buffer"}, {"2", "double buffer"},
{"3", "triple buffer"}, {"3", "triple buffer"},
}, },
} },
{
// SDVXWindowedSubscreenSize
.title = "SDVX Windowed Subscreen Size",
.name = "sdvxwsubsize",
.desc = "Size of the subscreen window. Defaults to (1920,1080)",
.type = OptionType::Text,
.setting_name = "1920,1080",
.game_name = "Sound Voltex",
.category = "Graphics (Windowed)",
},
{
// SDVXWindowedSubscreenPosition
.title = "SDVX Windowed Subscreen Position",
.name = "sdvxwsubpos",
.desc = "Initial position of the subscreen window. Defaults to (0,0)",
.type = OptionType::Text,
.setting_name = "0,0",
.game_name = "Sound Voltex",
.category = "Graphics (Windowed)",
},
{
// SDVXWindowedSubscreenBorderless
.title = "SDVX Windowed Subscreen Borderless",
.name = "sdvxwsubborderless",
.desc = "Remove window decoration from windowed subscreen",
.type = OptionType::Bool,
.game_name = "Sound Voltex",
.category = "Graphics (Windowed)",
},
{
// SDVXWindowedSubscreenAlwaysOnTop
.title = "SDVX Windowed Subscreen Always On Top",
.name = "sdvxwsubtop",
.desc = "Keep windowed subscreen on top",
.type = OptionType::Bool,
.game_name = "Sound Voltex",
.category = "Graphics (Windowed)",
},
{
// LovePlusCamEnable
.title = "LovePlus Camera Enable",
.name = "lovepluscam",
.desc = "Allow game to access camera; camera must be compatible with game",
.type = OptionType::Bool,
.game_name = "LovePlus",
.category = "Game Options",
},
{
.title = "LovePlus Printer Output Path",
.name = "lpprinterpath",
.desc = "Path to folder where images will be stored",
.type = OptionType::Text,
.game_name = "LovePlus",
.category = "Game Options (Advanced)",
},
{
.title = "LovePlus Printer Output Clear",
.name = "lpprinterclear",
.desc = "Clean up saved images in the output directory on startup",
.type = OptionType::Bool,
.game_name = "LovePlus",
.category = "Game Options (Advanced)",
},
{
.title = "LovePlus Printer Output Overwrite",
.name = "lpprinteroverwrite",
.desc = "Always overwrite the same file in output directory",
.type = OptionType::Bool,
.game_name = "LovePlus",
.category = "Game Options (Advanced)",
},
{
.title = "LovePlus Printer Output Format",
.name = "lpprinterformat",
.desc = "File format for printer output",
.type = OptionType::Text,
.setting_name = "(png/bmp/tga/jpg)",
.game_name = "LovePlus",
.category = "Game Options (Advanced)",
},
{
.title = "LovePlus Printer JPG Quality",
.name = "lpprinterjpgquality",
.desc = "Quality setting in percent if JPG format is used",
.type = OptionType::Integer,
.setting_name = "(0-100)",
.game_name = "LovePlus",
.category = "Game Options (Advanced)",
},
}; };
const std::vector<std::string> &launcher::get_categories(Options::OptionsCategory category) { const std::vector<std::string> &launcher::get_categories(Options::OptionsCategory category) {
+15
View File
@@ -136,6 +136,8 @@ namespace launcher {
APISerialBaud, APISerialBaud,
APIPretty, APIPretty,
APIDebugMode, APIDebugMode,
APIScreenMirrorQuality,
APIScreenMirrorDivide,
EnableAllIOModules, EnableAllIOModules,
EnableACIOModule, EnableACIOModule,
EnableICCAModule, EnableICCAModule,
@@ -207,6 +209,8 @@ namespace launcher {
WindowForceScaling, WindowForceScaling,
spice2x_IIDXWindowedSubscreenSize, spice2x_IIDXWindowedSubscreenSize,
spice2x_IIDXWindowedSubscreenPosition, spice2x_IIDXWindowedSubscreenPosition,
IIDXWindowedSubscreenBorderless,
IIDXWindowedSubscreenAlwaysOnTop,
spice2x_JubeatLegacyTouch, spice2x_JubeatLegacyTouch,
spice2x_RBTouchScale, spice2x_RBTouchScale,
spice2x_AsioForceUnload, spice2x_AsioForceUnload,
@@ -229,8 +233,19 @@ namespace launcher {
IIDXRecQuality, IIDXRecQuality,
MidiAlgoVer, MidiAlgoVer,
MidiNoteSustain, MidiNoteSustain,
InputRequiresFocus,
NostalgiaPoke, NostalgiaPoke,
ForceBackBufferCount, ForceBackBufferCount,
SDVXWindowedSubscreenSize,
SDVXWindowedSubscreenPosition,
SDVXWindowedSubscreenBorderless,
SDVXWindowedSubscreenAlwaysOnTop,
LovePlusCamEnable,
LovePlusPrinterOutputPath,
LovePlusPrinterOutputClear,
LovePlusPrinterOutputOverwrite,
LovePlusPrinterOutputFormat,
LovePlusPrinterJPGQuality,
}; };
enum class OptionsCategory { enum class OptionsCategory {
@@ -1645,6 +1645,8 @@ namespace overlay::windows {
std::string first_id = get_game_identifier(MODULE_PATH / firstDll); std::string first_id = get_game_identifier(MODULE_PATH / firstDll);
std::filesystem::path firstPath = fmt::format("patches/{}.json", first_id); std::filesystem::path firstPath = fmt::format("patches/{}.json", first_id);
log_misc("patchmanager", "patch identifier of {}: {}", firstDll, first_id);
auto extraDlls = getExtraDlls(firstDll); auto extraDlls = getExtraDlls(firstDll);
std::erase_if(extraDlls, [](const std::string& dll) { std::erase_if(extraDlls, [](const std::string& dll) {
auto identifier = get_game_identifier(MODULE_PATH / dll); auto identifier = get_game_identifier(MODULE_PATH / dll);
@@ -173,8 +173,8 @@ namespace overlay::windows {
"Change window decoration. Resizable Window may not cause your mouse cursor to change, " "Change window decoration. Resizable Window may not cause your mouse cursor to change, "
"but you can still drag to resize. Disabled for some games due to incompatibility."); "but you can still drag to resize. Disabled for some games due to incompatibility.");
if (ImGui::Checkbox("Always on Top", &cfg::SCREENRESIZE->window_always_on_top) ) { if (ImGui::Checkbox("Always On Top", &cfg::SCREENRESIZE->window_always_on_top) ) {
graphics_update_z_order(window); graphics_update_z_order(window, cfg::SCREENRESIZE->window_always_on_top);
} }
ImGui::BeginDisabled(); ImGui::BeginDisabled();
ImGui::Checkbox("Forced Render Scaling", &GRAPHICS_WINDOW_BACKBUFFER_SCALE); ImGui::Checkbox("Forced Render Scaling", &GRAPHICS_WINDOW_BACKBUFFER_SCALE);
+2
View File
@@ -28,6 +28,8 @@ namespace rawinput {
int TOUCHSCREEN_RANGE_X = 0; int TOUCHSCREEN_RANGE_X = 0;
int TOUCHSCREEN_RANGE_Y = 0; int TOUCHSCREEN_RANGE_Y = 0;
bool DUMP_HID_DEVICES_TO_LOG = false; bool DUMP_HID_DEVICES_TO_LOG = false;
bool NAIVE_REQUIRE_FOCUS = true;
bool RAWINPUT_REQUIRE_FOCUS = false;
// set this to something slightly longer than 16.67ms (60Hz) so that I/O can pick it up // set this to something slightly longer than 16.67ms (60Hz) so that I/O can pick it up
// this may need to be adjusted for each game in the future if there is a game that polls less // this may need to be adjusted for each game in the future if there is a game that polls less
+3
View File
@@ -28,6 +28,9 @@ namespace rawinput {
extern uint32_t MIDI_NOTE_SUSTAIN; extern uint32_t MIDI_NOTE_SUSTAIN;
extern bool NAIVE_REQUIRE_FOCUS;
extern bool RAWINPUT_REQUIRE_FOCUS;
struct DeviceCallback { struct DeviceCallback {
void *data; void *data;
std::function<void(void*, Device*)> f; std::function<void(void*, Device*)> f;
+88
View File
@@ -0,0 +1,88 @@
#include <cstdint>
#define EXPORT __declspec(dllexport)
enum {
Error_NoError = 0,
Error_DeviceNotFound = 100,
Error_Busy,
Error_Printing_Busy,
Error_Printing_Ready,
Error_PortBusy,
Error_GetCPDIDinfo,
Error_PrinterBusy,
Error_FuncParamError = 201,
Error_MemAllocError,
Error_Timeout,
};
extern "C" int32_t EXPORT __stdcall CPU9CheckPaperRemain(void *p1, void *p2) {
return Error_FuncParamError;
}
extern "C" int32_t EXPORT __stdcall CPU9CheckPrinter(void *p1, void *p2) {
return Error_FuncParamError;
}
extern "C" int32_t EXPORT __stdcall CPU9CheckPrintEnd(uint32_t p1, void *p2, void *p3) {
return Error_FuncParamError;
}
extern "C" int32_t EXPORT __stdcall CPU9GetFWInfo(void *p1, void *p2) {
return Error_FuncParamError;
}
extern "C" int32_t EXPORT __stdcall CPU9GetMediaType(void *p1, void *p2) {
return Error_FuncParamError;
}
extern "C" int32_t EXPORT __stdcall CPU9GetTempInfo(void *p1, void *p2) {
return Error_FuncParamError;
}
extern "C" int32_t EXPORT __stdcall CPU9GetTotalPrintCount(void *p1, void *p2) {
return Error_FuncParamError;
}
extern "C" int32_t EXPORT __stdcall CPU9PreHeat(void *p1) {
return Error_FuncParamError;
}
extern "C" int32_t EXPORT __stdcall CPU9PrintJobCancel(void *p1) {
return Error_FuncParamError;
}
extern "C" int32_t EXPORT __stdcall CPU9PrintOut(void *p1, void *p2) {
return Error_FuncParamError;
}
extern "C" int32_t EXPORT __stdcall CPU9SetPrintParameter2(void *p1, void *p2) {
return Error_FuncParamError;
}
extern "C" int32_t EXPORT __stdcall CPUASendImage(void *p1, void *p2, void *p3, void *p4) {
return Error_FuncParamError;
}
extern "C" int32_t EXPORT __stdcall CPUASendImagePrint(
void *p1, void *p2, void *p3, bool p4, void *p5, int32_t p6, void *p7) {
return Error_FuncParamError;
}
extern "C" int32_t EXPORT __stdcall CPUASendImagePrint2(
void *p1, void *p2, void *p3, bool p4, bool p5, void *p6, int32_t p7, void *p8) {
return Error_FuncParamError;
}
extern "C" int32_t EXPORT __stdcall CPUASetPrintParameter(void *p1, void *p2, bool p3, void *p4, void *p5) {
return Error_FuncParamError;
}
extern "C" int32_t EXPORT __stdcall CPUXSearchPrinters(void *p1, void *p2, int32_t p3, void *p4) {
return Error_FuncParamError;
}
extern "C" void EXPORT __stdcall CPUXInit() {
return;
}
+18
View File
@@ -0,0 +1,18 @@
EXPORTS
CPU9CheckPaperRemain
CPU9CheckPrinter
CPU9CheckPrintEnd
CPU9GetFWInfo
CPU9GetMediaType
CPU9GetTempInfo
CPU9GetTotalPrintCount
CPU9PreHeat
CPU9PrintJobCancel
CPU9PrintOut
CPU9SetPrintParameter2
CPUASendImage
CPUASendImagePrint
CPUASendImagePrint2
CPUASetPrintParameter
CPUXInit
CPUXSearchPrinters
+12
View File
@@ -356,3 +356,15 @@ void libutils::check_duplicate_dlls() {
} }
} }
} }
void libutils::warn_if_dll_exists(const std::string &file_name) {
if (fileutils::file_exists(MODULE_PATH / file_name)) {
log_info("libutils", "found user-supplied {} in modules directory", file_name);
return;
}
const auto &spice_bin_path = libutils::module_file_name(nullptr).parent_path();
if (fileutils::file_exists(spice_bin_path / file_name)) {
log_info("libutils", "found user-supplied {} next to spice executable path", file_name);
return;
}
}
+1
View File
@@ -25,6 +25,7 @@ namespace libutils {
} }
void check_duplicate_dlls(); void check_duplicate_dlls();
void warn_if_dll_exists(const std::string &file_name);
// get module handle helpers // get module handle helpers
HMODULE get_module(const char *module_name); HMODULE get_module(const char *module_name);
+26 -7
View File
@@ -215,23 +215,42 @@ namespace sysutils {
} }
std::string prefix("device"); std::string prefix("device");
if (is_monitor) { if (is_monitor) {
prefix = " adapter"; prefix = " adapter";
}
log_misc("gpuinfo", "{} {} device name : {}", prefix.c_str(), index, adapter->DeviceName);
log_misc("gpuinfo", "{} {} device string : {}", prefix.c_str(), index, adapter->DeviceString);
log_dbug("gpuinfo", "{} {} flags : 0x{:x}", prefix.c_str(), index, adapter->StateFlags);
if (!is_monitor) {
DEVMODEA devmode = {};
devmode.dmSize = sizeof(devmode);
if (EnumDisplaySettingsA(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &devmode)) {
log_misc(
"gpuinfo",
"{} {} resolution : {}px * {}px @ {}Hz",
prefix.c_str(),
index,
devmode.dmPelsWidth, devmode.dmPelsHeight,
devmode.dmDisplayFrequency);
} else {
log_misc("gpuinfo", "EnumDisplaySettingsA failed");
}
} }
log_misc("gpuinfo", "{} {} name : {}", prefix.c_str(), index, adapter->DeviceName);
log_misc("gpuinfo", "{} {} string : {}", prefix.c_str(), index, adapter->DeviceString);
log_dbug("gpuinfo", "{} {} flags : 0x{:x}", prefix.c_str(), index, adapter->StateFlags);
} }
void print_gpus() { void print_gpus() {
DWORD device_index = 0; DWORD device_index = 0;
DISPLAY_DEVICEA device; DISPLAY_DEVICEA device = {};
device.cb = sizeof(device); device.cb = sizeof(device);
log_misc("smbios", "dumping GPU/monitor information..."); log_misc(
"gpuinfo",
"dumping GPU/monitor information... "
"(note: these are current values **before** launching the game)");
while (EnumDisplayDevicesA(nullptr, device_index, &device, 0)) { while (EnumDisplayDevicesA(nullptr, device_index, &device, 0)) {
print_adapter(device_index, &device, false); print_adapter(device_index, &device, false);
DWORD monitor_index = 0; DWORD monitor_index = 0;
DISPLAY_DEVICEA monitor; DISPLAY_DEVICEA monitor = {};
monitor.cb = sizeof(monitor); monitor.cb = sizeof(monitor);
while (EnumDisplayDevicesA((PCHAR)device.DeviceName, monitor_index, &monitor, 0)) { while (EnumDisplayDevicesA((PCHAR)device.DeviceName, monitor_index, &monitor, 0)) {
print_adapter(monitor_index, &monitor, true); print_adapter(monitor_index, &monitor, true);
+23
View File
@@ -227,6 +227,7 @@ static inline std::vector<HWND> find_windows_beginning_with(const std::string &t
return windows; return windows;
} }
// exists for compat only; prefer to use FindProcessWindowBeginsWith instead
static inline HWND FindWindowBeginsWith(std::string title) { static inline HWND FindWindowBeginsWith(std::string title) {
// get all windows // get all windows
@@ -248,6 +249,28 @@ static inline HWND FindWindowBeginsWith(std::string title) {
return nullptr; return nullptr;
} }
static inline HWND FindProcessWindowBeginsWith(const std::string &title) {
// try foreground window first
HWND fg_win = GetForegroundWindow();
if (string_begins_with(get_window_title(fg_win), title)) {
DWORD fg_pid;
GetWindowThreadProcessId(fg_win, &fg_pid);
if (fg_pid == GetCurrentProcessId()) {
return fg_win;
}
}
// try different windows
for (const auto window : find_windows_beginning_with(title)) {
DWORD fg_pid;
GetWindowThreadProcessId(window, &fg_pid);
if (fg_pid == GetCurrentProcessId()) {
return window;
}
}
return nullptr;
}
static inline std::string get_last_error_string() { static inline std::string get_last_error_string() {
// get error // get error