mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-02 14:50:41 -07:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4bc849ccdb | |||
| 58481a035b | |||
| 66b3de8e5c | |||
| 257afe0cd3 | |||
| aafea3b1c1 | |||
| 346a159157 | |||
| 54028d8cbb | |||
| 621b702ed4 | |||
| 26ea8a1b92 | |||
| 6e7bf99af8 | |||
| df9d9d8673 | |||
| b3cee1dfcc | |||
| f40c382903 | |||
| e4cfb808bf | |||
| 61d14a16e8 | |||
| 521e87ea41 | |||
| 2b735ffd55 | |||
| 4f72b0a98b | |||
| 474f5f5203 | |||
| 205cf29aff | |||
| bf9074a76e | |||
| 44e1edb50c | |||
| 41d0dce6e9 | |||
| 598422b701 | |||
| b58a1ccfcc | |||
| 60efd643d1 | |||
| 0596eeb6fe | |||
| 3d5c64feb7 |
@@ -13,6 +13,11 @@ jobs:
|
||||
working-directory: ./src/spice2x
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set ccache environment variables
|
||||
run: |
|
||||
echo "CCACHE_DIR=${{ github.workspace }}/src/spice2x/.ccache" >> $GITHUB_ENV
|
||||
- name: Install ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
- name: Calculate commit SHA
|
||||
id: vars
|
||||
run: |
|
||||
|
||||
+8
-4
@@ -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.
|
||||
* 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.
|
||||
* 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.
|
||||
* 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.
|
||||
* 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
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
> * Please do note that this is a GPL v3.0 open source project.
|
||||
> * Please read the [CONTRIBUTING](https://github.com/spice2x/spice2x.github.io/blob/main/CONTRIBUTING.md) guide.
|
||||
> * Maintainers reserve the right to reject or modify your submission without reason.
|
||||
> * No new compiler warnings must be introduced. Check the CI build results.
|
||||
>
|
||||
> Feel free to remove this section after you have read it.
|
||||
|
||||
@@ -12,8 +13,5 @@
|
||||
## Description of change
|
||||
*what does this code do?*
|
||||
|
||||
## Compiling
|
||||
*you must be able to compile with the included Docker script with zero compiler warnings*
|
||||
|
||||
## Testing
|
||||
*how was the code tested?*
|
||||
|
||||
@@ -2,3 +2,4 @@ bin/**
|
||||
dist/**
|
||||
docker/**
|
||||
cmake-build*
|
||||
.ccache/**
|
||||
@@ -16,4 +16,6 @@ build_docker.local.sh
|
||||
bin/*
|
||||
dist/*
|
||||
|
||||
external/cv2pdb/*
|
||||
external/cv2pdb/*
|
||||
|
||||
.ccache/*
|
||||
@@ -124,8 +124,8 @@ else()
|
||||
# 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,
|
||||
# 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_SHARED_LINKER_FLAGS "${CMAKE_SHARED_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,--enable-stdcall-fixup")
|
||||
|
||||
# set visibility to 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")
|
||||
|
||||
# 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_SHARED_LINKER_FLAGS "${CMAKE_SHARED_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,--enable-stdcall-fixup")
|
||||
|
||||
# set visibility to hidden
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -fvisibility=hidden")
|
||||
@@ -184,8 +184,8 @@ else()
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-gdwarf")
|
||||
|
||||
# linker fix to load below 4GB
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--disable-dynamicbase,--image-base=0x400000")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_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,--enable-stdcall-fixup")
|
||||
|
||||
# enable debug symbols on level 3 and keep frame pointers
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g3 -fno-omit-frame-pointer")
|
||||
@@ -607,6 +607,22 @@ IF(NOT MSVC)
|
||||
set_target_properties(spicetools_spice PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
||||
endif()
|
||||
|
||||
# spice_laa.exe
|
||||
###########
|
||||
|
||||
set(RESOURCE_FILES build/manifest.manifest build/manifest.rc build/icon.rc cfg/Win32D.rc)
|
||||
add_executable(spicetools_spice_laa ${SOURCE_FILES} ${RESOURCE_FILES})
|
||||
target_link_libraries(spicetools_spice_laa
|
||||
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard winhttp
|
||||
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
|
||||
set_target_properties(spicetools_spice_laa PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_spice_laa PROPERTIES OUTPUT_NAME "spice_laa")
|
||||
target_compile_definitions(spicetools_spice_laa PRIVATE SPICE32_LARGE_ADDRESS_AWARE=1)
|
||||
|
||||
IF(NOT MSVC)
|
||||
set_target_properties(spicetools_spice_laa PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32 -Wl,--large-address-aware")
|
||||
endif()
|
||||
|
||||
# spice64.exe
|
||||
#############
|
||||
|
||||
@@ -626,7 +642,6 @@ IF(NOT MSVC)
|
||||
set_target_properties(spicetools_spice64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
|
||||
endif()
|
||||
|
||||
|
||||
# spicecfg.exe
|
||||
##############
|
||||
|
||||
@@ -723,6 +738,16 @@ if(NOT MSVC)
|
||||
set_target_properties(spicetools_stubs_nvEncodeAPI64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
|
||||
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
|
||||
####################
|
||||
|
||||
@@ -732,7 +757,7 @@ set_target_properties(spicetools_cfg
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools")
|
||||
|
||||
# output 32bit
|
||||
set_target_properties(spicetools_spice spicetools_stubs_kbt spicetools_stubs_kld
|
||||
set_target_properties(spicetools_spice spicetools_spice_laa spicetools_stubs_kbt spicetools_stubs_kld spicetools_stubs_cpusbxpkm
|
||||
PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive32"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32"
|
||||
|
||||
@@ -4,4 +4,4 @@ WORKDIR /src
|
||||
COPY --from=gitroot . /src/.git
|
||||
COPY . /src/src/spice2x
|
||||
WORKDIR /src/src/spice2x
|
||||
CMD ["./build_all.sh"]
|
||||
ENTRYPOINT ["./build_all.sh"]
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace acio2emu {
|
||||
void forward_packet_(const Packet &packet);
|
||||
|
||||
public:
|
||||
IOBHandle(std::wstring device);
|
||||
IOBHandle(LPCWSTR device);
|
||||
|
||||
bool register_node(std::unique_ptr<Node> node);
|
||||
int number_of_nodes() const;
|
||||
|
||||
@@ -9,6 +9,9 @@ using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
std::optional<uint32_t> CAPTURE_QUALITY;
|
||||
std::optional<uint32_t> CAPTURE_DIVIDE;
|
||||
|
||||
static thread_local std::vector<uint8_t> CAPTURE_BUFFER;
|
||||
|
||||
Capture::Capture() : Module("capture") {
|
||||
@@ -44,12 +47,21 @@ namespace api::modules {
|
||||
int screen = 0;
|
||||
int quality = 70;
|
||||
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();
|
||||
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();
|
||||
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();
|
||||
}
|
||||
|
||||
// receive JPEG data
|
||||
uint64_t timestamp = 0;
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
extern std::optional<uint32_t> CAPTURE_QUALITY;
|
||||
extern std::optional<uint32_t> CAPTURE_DIVIDE;
|
||||
|
||||
class Capture : public Module {
|
||||
public:
|
||||
Capture();
|
||||
|
||||
@@ -76,18 +76,23 @@ namespace avs {
|
||||
const auto dll_path_s = dll_path.string();
|
||||
log_info("avs-game", "DLL path: {}", dll_path_s.c_str());
|
||||
|
||||
if (MAX_PATH <= (dll_path_s.length() + 64)) {
|
||||
// MAX_PATH is 260
|
||||
if (130 <= dll_path_s.length()) {
|
||||
log_warning(
|
||||
"avs-game",
|
||||
"PATH TOO LONG WARNING\n\n\n"
|
||||
"-------------------------------------------------------------------\n"
|
||||
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
|
||||
"WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING\n"
|
||||
" PATH TOO LONG \n"
|
||||
"WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING\n"
|
||||
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
|
||||
"'{}' has a length of {}\n"
|
||||
"Most of these games will behave unexpectedly when the path is too long,\n"
|
||||
"often resulting in random crashes. Move the game contents to a directory\n"
|
||||
"with shorter path.\n"
|
||||
"-------------------------------------------------------------------\n\n\n",
|
||||
"The path '{}'\n"
|
||||
" has a length of {}\n"
|
||||
"Most of these games may behave unexpectedly when the path is too\n"
|
||||
"long, often resulting in random crashes. Move the game contents to\n"
|
||||
"a directory with shorter path.\n"
|
||||
"-------------------------------------------------------------------\n\n",
|
||||
dll_path_s, dll_path_s.length());
|
||||
}
|
||||
if (!fileutils::file_exists(dll_path)) {
|
||||
|
||||
@@ -19,6 +19,25 @@ function trap_error_exit {
|
||||
trap trap_error_dbg DEBUG
|
||||
trap trap_error_exit EXIT
|
||||
|
||||
IGNORE_CACHE=0
|
||||
|
||||
# Parse options
|
||||
while getopts "ih" opt; do
|
||||
case $opt in
|
||||
i) IGNORE_CACHE=1 ;;
|
||||
h)
|
||||
echo "Usage: $0 [-i]"
|
||||
echo " -i: Ignore build cache"
|
||||
echo " -h: Show this help"
|
||||
exit 0
|
||||
;;
|
||||
\?)
|
||||
echo "Invalid option: -$OPTARG" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# settings
|
||||
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2> /dev/null || echo "none")
|
||||
GIT_HEAD=$(git rev-parse HEAD || echo "none")
|
||||
@@ -41,7 +60,7 @@ DIST_ENABLE=1
|
||||
DIST_FOLDER="./dist"
|
||||
DIST_NAME="spice2x-$(date +%y)-$(date +%m)-$(date +%d).zip"
|
||||
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"
|
||||
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"
|
||||
|
||||
# determine build type
|
||||
@@ -74,6 +93,15 @@ echo "Build Type: $BUILD_TYPE"
|
||||
echo "Cores: $CORES"
|
||||
echo ""
|
||||
|
||||
if ((IGNORE_CACHE > 0))
|
||||
then
|
||||
echo "Ignoring build cache..."
|
||||
else
|
||||
export CCACHE_DIR="$(pwd)/.ccache"
|
||||
export CMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
export CMAKE_C_COMPILER_LAUNCHER=ccache
|
||||
fi
|
||||
|
||||
time (
|
||||
# 32 bit
|
||||
echo "Building 32bit targets..."
|
||||
@@ -153,8 +181,9 @@ fi
|
||||
echo "Copy files to output directory..."
|
||||
rm -rf ${OUTDIR}
|
||||
mkdir -p ${OUTDIR}
|
||||
#mkdir -p ${OUTDIR}/stubs/32
|
||||
mkdir -p ${OUTDIR}/stubs/32
|
||||
mkdir -p ${OUTDIR}/stubs/64
|
||||
mkdir -p ${OUTDIR}/extras/largeaddressaware
|
||||
if false # ((DEBUG > 0))
|
||||
then
|
||||
# debug files
|
||||
@@ -171,10 +200,12 @@ then
|
||||
#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/nvcuvid.dll ${OUTDIR}/stubs/64 2>/dev/null
|
||||
#cp ${BUILDDIR_32}/spicetools/32/cpusbxpkm.dll ${OUTDIR}/stubs/32 2>/dev/null
|
||||
else
|
||||
# release files
|
||||
cp ${BUILDDIR_32}/spicetools/spicecfg.exe ${OUTDIR} 2>/dev/null
|
||||
cp ${BUILDDIR_32}/spicetools/32/spice.exe ${OUTDIR} 2>/dev/null
|
||||
cp ${BUILDDIR_32}/spicetools/32/spice_laa.exe ${OUTDIR}/extras/largeaddressaware/spice.exe 2>/dev/null
|
||||
#cp ${BUILDDIR_32}/spicetools/32/kbt.dll ${OUTDIR}/stubs/32 2>/dev/null
|
||||
#cp ${BUILDDIR_32}/spicetools/32/kld.dll ${OUTDIR}/stubs/32 2>/dev/null
|
||||
cp ${BUILDDIR_64}/spicetools/64/spice64.exe ${OUTDIR} 2>/dev/null
|
||||
@@ -183,6 +214,7 @@ else
|
||||
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/nvcuvid.dll ${OUTDIR}/stubs/64 2>/dev/null
|
||||
cp ${BUILDDIR_32}/spicetools/32/cpusbxpkm.dll ${OUTDIR}/stubs/32 2>/dev/null
|
||||
fi
|
||||
|
||||
# pack source files to output directory
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
docker build --pull external/docker -t spicetools/deps
|
||||
docker build --build-context gitroot=%cd%/../../.git . -t spicetools/spice --no-cache
|
||||
docker run --rm -it -v %cd%/dist:/src/src/spice2x/dist -v %cd%/bin:/src/src/spice2x/bin spicetools/spice
|
||||
docker build --build-context gitroot=%cd%/../../.git . -t spicetools/spice
|
||||
docker run --rm -it -v %cd%/dist:/src/src/spice2x/dist -v %cd%/bin:/src/src/spice2x/bin -v %cd%/.ccache:/src/src/spice2x/.ccache spicetools/spice %*
|
||||
@REM to generate PDBs, set DEBUG to 1 in build_all.sh, place cv2pdb in external\cv2pdb, and run below
|
||||
@REM external\cv2pdb\cv2pdb.exe bin\spice2x\spicecfg.exe bin\spice2x\spicecfg-pdb.exe bin\spice2x\spicecfg-pdb.pdb
|
||||
@REM external\cv2pdb\cv2pdb.exe bin\spice2x\spice.exe bin\spice2x\spice-pdb.exe bin\spice2x\spice-pdb.pdb
|
||||
|
||||
@@ -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 --no-cache
|
||||
docker run --rm -v $PWD/dist:/src/src/spice2x/dist -v $PWD/bin:/src/src/spice2x/bin spicetools/spice
|
||||
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 "$@"
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
del /s /q .ccache
|
||||
call build_docker.bat
|
||||
+34
-4
@@ -1,5 +1,8 @@
|
||||
#include "api.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "launcher/superexit.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "rawinput/piuio.h"
|
||||
#include "util/time.h"
|
||||
@@ -64,18 +67,29 @@ GameAPI::Buttons::State GameAPI::Buttons::getState(rawinput::RawInputManager *ma
|
||||
auto current_button = &_button;
|
||||
auto alternatives = check_alts ? ¤t_button->getAlternatives() : nullptr;
|
||||
unsigned int button_count = 0;
|
||||
std::optional<bool> window_has_focus;
|
||||
while (true) {
|
||||
|
||||
// naive behavior
|
||||
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
|
||||
auto vkey = current_button->getVKey();
|
||||
GameAPI::Buttons::State state;
|
||||
if (vkey == 0xFF) {
|
||||
if (vkey == INVALID_VKEY) {
|
||||
state = BUTTON_NOT_PRESSED;
|
||||
} else {
|
||||
state = (GetAsyncKeyState(current_button->getVKey()) & 0x8000) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
||||
state = (GetAsyncKeyState(vkey) & 0x8000) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
||||
}
|
||||
|
||||
// invert
|
||||
@@ -106,6 +120,16 @@ GameAPI::Buttons::State GameAPI::Buttons::getState(rawinput::RawInputManager *ma
|
||||
auto &devid = current_button->getDeviceIdentifier();
|
||||
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
|
||||
GameAPI::Buttons::State state = current_button->getLastState();
|
||||
double *last_up = nullptr;
|
||||
@@ -571,6 +595,12 @@ float GameAPI::Analogs::getState(rawinput::Device *device, Analog &analog) {
|
||||
switch (device->type) {
|
||||
case rawinput::MOUSE: {
|
||||
|
||||
// check for focus
|
||||
if (rawinput::NAIVE_REQUIRE_FOCUS && !superexit::has_focus()) {
|
||||
value = analog.getLastState();
|
||||
break;
|
||||
}
|
||||
|
||||
// get mouse position
|
||||
auto mouse = device->mouseInfo;
|
||||
long pos;
|
||||
|
||||
@@ -284,7 +284,7 @@ std::string Button::getDisplayString(rawinput::RawInputManager* manager) {
|
||||
std::string vKeyString = fmt::format("{:#x}", vKey);
|
||||
|
||||
// device must be existing
|
||||
if (this->device_identifier.empty() && vKey == 0xFF) {
|
||||
if (this->device_identifier.empty() && vKey == INVALID_VKEY) {
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
@@ -33,12 +33,14 @@ enum ButtonAnalogType {
|
||||
|
||||
extern const char *ButtonAnalogTypeStr[];
|
||||
|
||||
constexpr unsigned short INVALID_VKEY = UINT16_C(0xFF);
|
||||
|
||||
class Button {
|
||||
private:
|
||||
std::vector<Button> alternatives;
|
||||
std::string name;
|
||||
std::string device_identifier = "";
|
||||
unsigned short vKey = 0xFF;
|
||||
unsigned short vKey = INVALID_VKEY;
|
||||
ButtonAnalogType analog_type = BAT_NONE;
|
||||
double debounce_up = 0.0;
|
||||
double debounce_down = 0.0;
|
||||
@@ -68,12 +70,12 @@ public:
|
||||
if (this->override_enabled) {
|
||||
return true;
|
||||
}
|
||||
if (this->vKey != 0xFF) {
|
||||
if (this->vKey != INVALID_VKEY) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto &alternative : this->alternatives) {
|
||||
if (alternative.vKey != 0xFF) {
|
||||
if (alternative.vKey != INVALID_VKEY) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -82,7 +84,7 @@ public:
|
||||
}
|
||||
|
||||
inline void clearBindings() {
|
||||
vKey = 0xFF;
|
||||
vKey = INVALID_VKEY;
|
||||
alternatives.clear();
|
||||
device_identifier = "";
|
||||
analog_type = BAT_NONE;
|
||||
|
||||
@@ -15,12 +15,18 @@ namespace cfg {
|
||||
std::optional<std::string> SCREEN_RESIZE_CFG_PATH_OVERRIDE;
|
||||
|
||||
ScreenResize::ScreenResize() {
|
||||
bool file_exists = false;
|
||||
if (SCREEN_RESIZE_CFG_PATH_OVERRIDE.has_value()) {
|
||||
this->config_path = SCREEN_RESIZE_CFG_PATH_OVERRIDE.value();
|
||||
if (fileutils::file_exists(this->config_path)) {
|
||||
log_info("ScreenResize", "loading config from: {}", this->config_path.string());
|
||||
file_exists = true;
|
||||
}
|
||||
} else {
|
||||
this->config_path = std::filesystem::path(_wgetenv(L"APPDATA")) / L"spicetools_screen_resize.json";
|
||||
this->config_path =
|
||||
fileutils::get_config_file_path("ScreenResize", "spicetools_screen_resize.json", &file_exists);
|
||||
}
|
||||
if (fileutils::file_exists(this->config_path)) {
|
||||
if (file_exists) {
|
||||
this->config_load();
|
||||
}
|
||||
}
|
||||
@@ -29,12 +35,6 @@ namespace cfg {
|
||||
}
|
||||
|
||||
void ScreenResize::config_load() {
|
||||
if (SCREEN_RESIZE_CFG_PATH_OVERRIDE.has_value()) {
|
||||
log_info("ScreenResize", "loading custom config: {}", this->config_path.string());
|
||||
} else {
|
||||
log_info("ScreenResize", "loading global config from APPDATA");
|
||||
}
|
||||
|
||||
std::string config = fileutils::text_read(this->config_path);
|
||||
if (config.empty()) {
|
||||
log_info("ScreenResize", "config is empty");
|
||||
@@ -170,8 +170,6 @@ namespace cfg {
|
||||
}
|
||||
|
||||
void ScreenResize::config_save() {
|
||||
log_info("ScreenResize", "saving config: {}", this->config_path.string());
|
||||
|
||||
rapidjson::Document doc;
|
||||
std::string config = fileutils::text_read(this->config_path);
|
||||
if (!config.empty()) {
|
||||
@@ -221,10 +219,10 @@ namespace cfg {
|
||||
doc.Accept(writer);
|
||||
|
||||
// save to file
|
||||
if (fileutils::text_write(this->config_path, buffer.GetString())) {
|
||||
if (fileutils::write_config_file("ScreenResize", this->config_path, buffer.GetString())) {
|
||||
// this->config_dirty = false;
|
||||
} else {
|
||||
log_warning("ScreenResize", "unable to save config file to {}", this->config_path.string());
|
||||
log_warning("ScreenResize", "unable to save config file");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
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]
|
||||
LargeAddressAware variant of spice.exe for Metal Gear
|
||||
Save new JSON config files to %appdata%/spice2x
|
||||
Various bug fixes
|
||||
|
||||
04/08/2025 [spice2x]
|
||||
Ensure rawinput touch is default for all games (-wintouch to override)
|
||||
Various overlay UI fixes and improvements, update ImGui library
|
||||
|
||||
+2
-1
@@ -11,6 +11,7 @@ RUN pacman --noconfirm -Syu git \
|
||||
mingw-w64-winpthreads \
|
||||
mingw-w64-gcc \
|
||||
mingw-w64-headers \
|
||||
mingw-w64-binutils
|
||||
mingw-w64-binutils \
|
||||
ccache
|
||||
RUN useradd user -m && echo "ALL ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
||||
RUN su user -c "cd /tmp/ && git clone https://aur.archlinux.org/yay-bin.git --depth=1 && cd yay-bin && makepkg --noconfirm -si && yay --noconfirm -S mingw-w64-cmake"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.9)
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
project(imgui CXX)
|
||||
|
||||
set(HASH_LIBRARY_HEADERS
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.9)
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
cmake_policy(SET CMP0069 NEW)
|
||||
project(minhook)
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace games::iidx {
|
||||
static IIDXLocalCamera *front_camera = nullptr; // camera id #1
|
||||
std::vector<IIDXLocalCamera*> LOCAL_CAMERA_LIST = {};
|
||||
static IDirect3DDeviceManager9 *s_pD3DManager = nullptr;
|
||||
std::filesystem::path CAMERA_CONFIG_PATH = std::filesystem::path(_wgetenv(L"APPDATA")) / L"spicetools_camera_control.json";
|
||||
std::filesystem::path CAMERA_CONFIG_PATH;
|
||||
bool CAMERA_READY = false;
|
||||
|
||||
bool parse_cmd_params() {
|
||||
@@ -493,7 +493,8 @@ namespace games::iidx {
|
||||
}
|
||||
|
||||
bool camera_config_load() {
|
||||
log_info("iidx:camhook", "loading config");
|
||||
CAMERA_CONFIG_PATH =
|
||||
fileutils::get_config_file_path("iidx::camhook", "spicetools_camera_control.json");
|
||||
|
||||
try {
|
||||
// read config file
|
||||
@@ -651,9 +652,9 @@ namespace games::iidx {
|
||||
doc.Accept(writer);
|
||||
|
||||
// save to file
|
||||
if (fileutils::text_write(CAMERA_CONFIG_PATH, buffer.GetString())) {
|
||||
if (fileutils::write_config_file("iidx::camhook", CAMERA_CONFIG_PATH, buffer.GetString())) {
|
||||
} else {
|
||||
log_warning("iidx:camhook", "unable to save config file to {}", CAMERA_CONFIG_PATH.string());
|
||||
log_warning("iidx:camhook", "unable to save config file");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -266,8 +266,8 @@ namespace games::iidx::poke {
|
||||
y *= rawinput::TOUCHSCREEN_RANGE_Y;
|
||||
} else if (GRAPHICS_IIDX_WSUB) {
|
||||
// Scale to windowed subscreen
|
||||
x *= GRAPHICS_IIDX_WSUB_WIDTH;
|
||||
y *= GRAPHICS_IIDX_WSUB_HEIGHT;
|
||||
x *= GRAPHICS_WSUB_WIDTH;
|
||||
y *= GRAPHICS_WSUB_HEIGHT;
|
||||
} else if (GENERIC_SUB_WINDOW_FULLSIZE || !overlay::OVERLAY->get_active()) {
|
||||
// Overlay is not present, scale to main screen
|
||||
if (GRAPHICS_WINDOWED) {
|
||||
|
||||
@@ -13,10 +13,14 @@
|
||||
|
||||
namespace games::loveplus {
|
||||
|
||||
bool CAMERA_ENABLE = false;
|
||||
|
||||
// touch stuff
|
||||
static bool TOUCH_ENABLE = false;
|
||||
static bool TOUCH_ATTACHED = false;
|
||||
|
||||
static std::string lp_args = "-noWatchDog -noIOError -noIrda -notarget";
|
||||
|
||||
void touch_update() {
|
||||
|
||||
// check if touch enabled
|
||||
@@ -28,16 +32,15 @@ namespace games::loveplus {
|
||||
if (!TOUCH_ATTACHED) {
|
||||
|
||||
// Find the game window.
|
||||
// We check the foreground window first, then fall back to searching for the window title
|
||||
// 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);
|
||||
}
|
||||
HWND wnd = FindProcessWindowBeginsWith("LovePlusAC");
|
||||
|
||||
// attach touch hook
|
||||
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);
|
||||
} else {
|
||||
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 std::string lp_args = "-win -noCamera -noWatchDog -noIOError -noIrda -notarget";
|
||||
|
||||
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") {
|
||||
logger::hook_add(lp_spam_remover, nullptr);
|
||||
}
|
||||
|
||||
LovePlusGame::~LovePlusGame() {
|
||||
logger::hook_remove(lp_spam_remover, this);
|
||||
}
|
||||
|
||||
void LovePlusGame::attach() {
|
||||
@@ -157,9 +174,17 @@ namespace games::loveplus {
|
||||
HMODULE seteq = libutils::try_library("ad_hd_seteq_dll.dll");
|
||||
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
|
||||
HMODULE lpac = libutils::try_library("lpac.dll");
|
||||
detour::iat("GetCommandLineA", GetCommandLineA_hook, lpac);
|
||||
|
||||
}
|
||||
|
||||
void LovePlusGame::detach() {
|
||||
|
||||
@@ -4,12 +4,15 @@
|
||||
|
||||
namespace games::loveplus {
|
||||
|
||||
extern bool CAMERA_ENABLE;
|
||||
|
||||
// touch stuff
|
||||
void touch_update();
|
||||
|
||||
class LovePlusGame : public games::Game {
|
||||
public:
|
||||
LovePlusGame();
|
||||
virtual ~LovePlusGame() override;
|
||||
virtual void attach() override;
|
||||
virtual void detach() override;
|
||||
};
|
||||
|
||||
@@ -294,7 +294,7 @@ namespace games::shared {
|
||||
|
||||
// error
|
||||
if (!clear) {
|
||||
log_fatal("sdvx", "could not find path");
|
||||
log_fatal("printer", "could not find path");
|
||||
}
|
||||
|
||||
return "DUMMY";
|
||||
@@ -326,7 +326,7 @@ namespace games::shared {
|
||||
memcpy(image_data, pBandImage->baseAddr, (size_t) (image_width * image_height * 3));
|
||||
|
||||
// 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++) {
|
||||
int index = pixel * 3;
|
||||
uint8_t tmp = image_data[index];
|
||||
@@ -334,25 +334,45 @@ namespace games::shared {
|
||||
image_data[index + 2] = tmp;
|
||||
}
|
||||
|
||||
// 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;
|
||||
if (avs::game::is_model({"KLP", "KFC"})) {
|
||||
// rotate image clockwise
|
||||
log_misc("printer", "rotate clockwise...");
|
||||
auto rotated = new uint8_t[image_width * image_height * 3];
|
||||
for (int x = 0; x < image_width; x++) {
|
||||
for (int y = 0; y < image_height; y++) {
|
||||
int index1 = (y * image_width + x) * 3;
|
||||
int index2 = (x * image_height + y) * 3;
|
||||
rotated[index2 + 0] = image_data[index1 + 0];
|
||||
rotated[index2 + 1] = image_data[index1 + 1];
|
||||
rotated[index2 + 2] = image_data[index1 + 2];
|
||||
}
|
||||
}
|
||||
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
|
||||
log_info("printer", "writing files...");
|
||||
log_misc("printer", "writing files...");
|
||||
for (const auto &path : PRINTER_PATH) {
|
||||
for (const auto &format : PRINTER_FORMAT) {
|
||||
|
||||
|
||||
@@ -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) {
|
||||
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
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
X = GRAPHICS_IIDX_WSUB_X;
|
||||
Y = GRAPHICS_IIDX_WSUB_Y;
|
||||
X = GRAPHICS_WSUB_X;
|
||||
Y = GRAPHICS_WSUB_Y;
|
||||
|
||||
nWidth = rect.right - rect.left;
|
||||
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));
|
||||
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) {
|
||||
|
||||
@@ -50,12 +50,14 @@ extern bool GRAPHICS_WINDOW_ALWAYS_ON_TOP;
|
||||
extern bool GRAPHICS_WINDOW_BACKBUFFER_SCALE;
|
||||
|
||||
extern bool GRAPHICS_IIDX_WSUB;
|
||||
extern std::optional<std::string> GRAPHICS_IIDX_WSUB_SIZE;
|
||||
extern std::optional<std::string> GRAPHICS_IIDX_WSUB_POS;
|
||||
extern int GRAPHICS_IIDX_WSUB_WIDTH;
|
||||
extern int GRAPHICS_IIDX_WSUB_HEIGHT;
|
||||
extern int GRAPHICS_IIDX_WSUB_X;
|
||||
extern int GRAPHICS_IIDX_WSUB_Y;
|
||||
extern std::optional<std::string> GRAPHICS_WSUB_SIZE;
|
||||
extern std::optional<std::string> GRAPHICS_WSUB_POS;
|
||||
extern int GRAPHICS_WSUB_WIDTH;
|
||||
extern int GRAPHICS_WSUB_HEIGHT;
|
||||
extern int GRAPHICS_WSUB_X;
|
||||
extern int GRAPHICS_WSUB_Y;
|
||||
extern bool GRAPHICS_WSUB_BORDERLESS;
|
||||
extern bool GRAPHICS_WSUB_ALWAYS_ON_TOP;
|
||||
extern HWND TDJ_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_capture_initial_window(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);
|
||||
bool graphics_window_change_crashes_game();
|
||||
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
|
||||
bool GRAPHICS_IIDX_WSUB = false;
|
||||
std::optional<std::string> GRAPHICS_IIDX_WSUB_SIZE;
|
||||
std::optional<std::string> GRAPHICS_IIDX_WSUB_POS;
|
||||
int GRAPHICS_IIDX_WSUB_WIDTH = 1280;
|
||||
int GRAPHICS_IIDX_WSUB_HEIGHT = 720;
|
||||
int GRAPHICS_IIDX_WSUB_X = 0;
|
||||
int GRAPHICS_IIDX_WSUB_Y = 0;
|
||||
std::optional<std::string> GRAPHICS_WSUB_SIZE;
|
||||
std::optional<std::string> GRAPHICS_WSUB_POS;
|
||||
int GRAPHICS_WSUB_WIDTH = 1280;
|
||||
int GRAPHICS_WSUB_HEIGHT = 720;
|
||||
int GRAPHICS_WSUB_X = 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
|
||||
// (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) {
|
||||
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
|
||||
@@ -169,29 +171,29 @@ void graphics_load_windowed_subscreen_parameters() {
|
||||
|
||||
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(
|
||||
"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;
|
||||
if (parse_width_height(GRAPHICS_IIDX_WSUB_SIZE.value(), result)) {
|
||||
GRAPHICS_IIDX_WSUB_WIDTH = result.first;
|
||||
GRAPHICS_IIDX_WSUB_HEIGHT = result.second;
|
||||
if (parse_width_height(GRAPHICS_WSUB_SIZE.value(), result)) {
|
||||
GRAPHICS_WSUB_WIDTH = result.first;
|
||||
GRAPHICS_WSUB_HEIGHT = result.second;
|
||||
} else {
|
||||
log_warning("graphics-windowed", "failed to parse -wsubsize");
|
||||
}
|
||||
}
|
||||
|
||||
if (GRAPHICS_IIDX_WSUB_POS.has_value()) {
|
||||
if (GRAPHICS_WSUB_POS.has_value()) {
|
||||
log_debug(
|
||||
"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;
|
||||
if (parse_width_height(GRAPHICS_IIDX_WSUB_POS.value(), result)) {
|
||||
GRAPHICS_IIDX_WSUB_X = result.first;
|
||||
GRAPHICS_IIDX_WSUB_Y = result.second;
|
||||
if (parse_width_height(GRAPHICS_WSUB_POS.value(), result)) {
|
||||
GRAPHICS_WSUB_X = result.first;
|
||||
GRAPHICS_WSUB_Y = result.second;
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
|
||||
void graphics_update_z_order(HWND hWnd) {
|
||||
void graphics_update_z_order(HWND hWnd, bool always_on_top) {
|
||||
if (!GRAPHICS_WINDOWED) {
|
||||
return;
|
||||
}
|
||||
@@ -406,7 +408,7 @@ void graphics_update_z_order(HWND hWnd) {
|
||||
log_debug("graphics-windowed", "graphics_update_z_order called");
|
||||
|
||||
HWND insert_after = nullptr;
|
||||
if (cfg::SCREENRESIZE->window_always_on_top) {
|
||||
if (always_on_top) {
|
||||
insert_after = HWND_TOPMOST;
|
||||
} else {
|
||||
insert_after = HWND_NOTOPMOST;
|
||||
|
||||
@@ -179,7 +179,7 @@ void networkhook_init() {
|
||||
strncpy(s_prefix, inet_ntoa(prefix), 16);
|
||||
|
||||
// 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
|
||||
auto orig_addr = detour::iat_try(
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <shlwapi.h>
|
||||
#include <cfg/configurator.h>
|
||||
|
||||
#include "api/modules/capture.h"
|
||||
#include "acio/acio.h"
|
||||
#include "acio/icca/icca.h"
|
||||
#include "api/controller.h"
|
||||
@@ -661,21 +662,42 @@ int main_implementation(int argc, char *argv[]) {
|
||||
if (options[launcher::Options::SDVXPrinterEmulation].value_bool()) {
|
||||
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;
|
||||
}
|
||||
|
||||
// printer output path
|
||||
for (auto &path : options[launcher::Options::SDVXPrinterOutputPath].values_text()) {
|
||||
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()) {
|
||||
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()) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (options[launcher::Options::HTTP11].is_active()) {
|
||||
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()) {
|
||||
NETWORK_SUBNET = options[launcher::Options::AdapterSubnet].value_text();
|
||||
}
|
||||
|
||||
if (options[launcher::Options::APITCPPort].is_active()) {
|
||||
api_enable = true;
|
||||
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()) {
|
||||
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()) {
|
||||
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_BACKBUFFER_SCALE = options[launcher::Options::WindowForceScaling].value_bool();
|
||||
|
||||
// IIDX Windowed Subscreen
|
||||
// IIDX/SDVX Windowed Subscreen
|
||||
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()) {
|
||||
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()) {
|
||||
@@ -1056,6 +1098,17 @@ int main_implementation(int argc, char *argv[]) {
|
||||
if (options[launcher::Options::spice2x_EnableSMXDedicab].value_bool()) {
|
||||
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].value_text() == "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();
|
||||
}
|
||||
|
||||
if (options[launcher::Options::LovePlusCamEnable].value_bool()) {
|
||||
games::loveplus::CAMERA_ENABLE = true;
|
||||
}
|
||||
|
||||
// API debugging
|
||||
if (api_debug && !cfg::CONFIGURATOR_STANDALONE) {
|
||||
API_CONTROLLER = std::make_unique<api::Controller>(api_port, api_pass, api_pretty);
|
||||
@@ -1118,6 +1175,8 @@ int main_implementation(int argc, char *argv[]) {
|
||||
// log
|
||||
#ifdef SPICE64
|
||||
log_info("launcher", "SpiceTools Bootstrap (x64) (spice2x)");
|
||||
#elif SPICE32_LARGE_ADDRESS_AWARE
|
||||
log_info("launcher", "SpiceTools Bootstrap (x32) (Large Address Aware) (spice2x)");
|
||||
#else
|
||||
log_info("launcher", "SpiceTools Bootstrap (x32) (spice2x)");
|
||||
#endif
|
||||
@@ -1170,13 +1229,19 @@ int main_implementation(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !GRAPHICS_WINDOWED) {
|
||||
#if SPICE64
|
||||
GRAPHICS_FS_ORIENTATION_SWAP = true;
|
||||
#else
|
||||
log_warning("launcher", "-sdvxlandscape is not supported in 32-bit SDVX, ignoring...");
|
||||
#endif
|
||||
// SDVXFullscreenLandscape disabled due to it messing with in-game camera angle
|
||||
// FullscreenOrientationFlip continues to live on, however.
|
||||
if (options[launcher::Options::SDVXFullscreenLandscape].value_bool()) {
|
||||
log_fatal("launcher", "-sdvxlandscape removed due to a camera bug in SDVX!");
|
||||
}
|
||||
// if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !GRAPHICS_WINDOWED) {
|
||||
// #if SPICE64
|
||||
// GRAPHICS_FS_ORIENTATION_SWAP = true;
|
||||
// #else
|
||||
// log_warning("launcher", "-sdvxlandscape is not supported in 32-bit SDVX, ignoring...");
|
||||
// #endif
|
||||
// }
|
||||
|
||||
if (options[launcher::Options::FullscreenOrientationFlip].value_bool() && !GRAPHICS_WINDOWED) {
|
||||
GRAPHICS_FS_ORIENTATION_SWAP = true;
|
||||
}
|
||||
@@ -1800,6 +1865,19 @@ int main_implementation(int argc, char *argv[]) {
|
||||
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
|
||||
libutils::check_duplicate_dlls();
|
||||
|
||||
// print cpu features
|
||||
if (!cfg::CONFIGURATOR_STANDALONE && dump_sysinfo) {
|
||||
cpuutils::print_cpu_features();
|
||||
|
||||
@@ -228,7 +228,8 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.desc =
|
||||
"Allows you to play portrait games in in landscape (and vice versa) by transposing resolution and applying image scaling.\n\n"
|
||||
"Works great for some games, but can COMPLETELY BREAK other games - YMMV!\n\n"
|
||||
"Strongly consider combining this with -forceres option to render at monitor native resolution",
|
||||
"Strongly consider combining this with -forceres option to render at monitor native resolution\n\n"
|
||||
"WARNING: for SDVX, this messes with camera angle for note lanes, causing it to be zoomed out!",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Graphics (Full Screen)"
|
||||
},
|
||||
@@ -305,6 +306,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
"Force the use of an adapter with the specified network. Must also provide -subnet",
|
||||
.type = OptionType::Text,
|
||||
.category = "Network Adapters",
|
||||
.sensitive = true,
|
||||
},
|
||||
{
|
||||
.title = "Preferred NetAdapter Subnet",
|
||||
@@ -692,7 +694,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
{
|
||||
.title = "SDVX Printer Output Format",
|
||||
.name = "printerformat",
|
||||
.desc = "Path to folder where images will be stored",
|
||||
.desc = "File format for printer output",
|
||||
.type = OptionType::Text,
|
||||
.setting_name = "(png/bmp/tga/jpg)",
|
||||
.game_name = "Sound Voltex",
|
||||
@@ -1087,7 +1089,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.title = "Screen Resize Config Path",
|
||||
.name = "resizecfgpath",
|
||||
.desc = "Sets a custom file path for screen resize config file. "
|
||||
"If left empty, %appdata%\\spicetools_screen_resize.json will be used",
|
||||
"If left empty, %appdata%\\spice2x\\spicetools_screen_resize.json will be used",
|
||||
.type = OptionType::Text,
|
||||
.category = "Paths",
|
||||
},
|
||||
@@ -1096,7 +1098,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.title = "Patch Manager Config Path",
|
||||
.name = "patchcfgpath",
|
||||
.desc = "Sets a custom file path for patch manager config file. Can be used to manage 'profiles' for auto-patches. "
|
||||
"If left empty, %appdata%\\spicetools_patch_manager.json will be used",
|
||||
"If left empty, %appdata%\\spice2x\\spicetools_patch_manager.json will be used",
|
||||
.type = OptionType::Text,
|
||||
.category = "Paths",
|
||||
},
|
||||
@@ -1192,6 +1194,28 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.type = OptionType::Bool,
|
||||
.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",
|
||||
.name = "io",
|
||||
@@ -1841,6 +1865,24 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.game_name = "Beatmania IIDX",
|
||||
.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
|
||||
.title = "JB Legacy Touch Targets",
|
||||
@@ -2049,15 +2091,17 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
},
|
||||
{
|
||||
// SDVXFullscreenLandscape
|
||||
.title = "SDVX Landscape Mode (SDVX5+)",
|
||||
.title = "(DISABLED) SDVX Landscape Mode (SDVX5+)",
|
||||
.name = "sdvxlandscape",
|
||||
.desc =
|
||||
"Option HIDDEN and DISABLED due to it affecting in-game camera angle for displaying lanes.\n\n"
|
||||
"Allows you to play in landscape by transposing resolution and applying image scaling.\n\n"
|
||||
"Works only for SDVX5 and above! This is identical to -forceresswap.\n\n"
|
||||
"Will launch at 1080p by default; strongly consider combining this with -forceres option to render at monitor native resolution",
|
||||
.type = OptionType::Bool,
|
||||
.hidden = true,
|
||||
.game_name = "Sound Voltex",
|
||||
.category = "Game Options"
|
||||
.category = "Game Options",
|
||||
},
|
||||
{
|
||||
// spice2x_EnableSMXStage
|
||||
@@ -2126,6 +2170,23 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.setting_name = "20",
|
||||
.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
|
||||
.title = "Nost Screen Poke",
|
||||
@@ -2150,7 +2211,96 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
{"2", "double 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) {
|
||||
|
||||
@@ -136,6 +136,8 @@ namespace launcher {
|
||||
APISerialBaud,
|
||||
APIPretty,
|
||||
APIDebugMode,
|
||||
APIScreenMirrorQuality,
|
||||
APIScreenMirrorDivide,
|
||||
EnableAllIOModules,
|
||||
EnableACIOModule,
|
||||
EnableICCAModule,
|
||||
@@ -207,6 +209,8 @@ namespace launcher {
|
||||
WindowForceScaling,
|
||||
spice2x_IIDXWindowedSubscreenSize,
|
||||
spice2x_IIDXWindowedSubscreenPosition,
|
||||
IIDXWindowedSubscreenBorderless,
|
||||
IIDXWindowedSubscreenAlwaysOnTop,
|
||||
spice2x_JubeatLegacyTouch,
|
||||
spice2x_RBTouchScale,
|
||||
spice2x_AsioForceUnload,
|
||||
@@ -229,8 +233,19 @@ namespace launcher {
|
||||
IIDXRecQuality,
|
||||
MidiAlgoVer,
|
||||
MidiNoteSustain,
|
||||
InputRequiresFocus,
|
||||
NostalgiaPoke,
|
||||
ForceBackBufferCount,
|
||||
SDVXWindowedSubscreenSize,
|
||||
SDVXWindowedSubscreenPosition,
|
||||
SDVXWindowedSubscreenBorderless,
|
||||
SDVXWindowedSubscreenAlwaysOnTop,
|
||||
LovePlusCamEnable,
|
||||
LovePlusPrinterOutputPath,
|
||||
LovePlusPrinterOutputClear,
|
||||
LovePlusPrinterOutputOverwrite,
|
||||
LovePlusPrinterOutputFormat,
|
||||
LovePlusPrinterJPGQuality,
|
||||
};
|
||||
|
||||
enum class OptionsCategory {
|
||||
|
||||
@@ -29,8 +29,11 @@ namespace overlay::windows {
|
||||
}
|
||||
|
||||
this->toggle_button = games::OverlayButtons::ToggleCardManager;
|
||||
this->config_path = std::filesystem::path(_wgetenv(L"APPDATA")) / L"spicetools_card_manager.json";
|
||||
if (fileutils::file_exists(this->config_path)) {
|
||||
|
||||
bool file_exists = false;
|
||||
this->config_path =
|
||||
fileutils::get_config_file_path("cardmanager", "spicetools_card_manager.json", &file_exists);
|
||||
if (file_exists) {
|
||||
this->config_load();
|
||||
}
|
||||
|
||||
@@ -434,8 +437,6 @@ namespace overlay::windows {
|
||||
}
|
||||
|
||||
void CardManager::config_load() {
|
||||
log_info("cardmanager", "loading config");
|
||||
|
||||
// clear cards
|
||||
this->cards.clear();
|
||||
|
||||
@@ -566,10 +567,10 @@ namespace overlay::windows {
|
||||
doc.Accept(writer);
|
||||
|
||||
// save to file
|
||||
if (fileutils::text_write(this->config_path, buffer.GetString())) {
|
||||
if (fileutils::write_config_file("cardmanager", this->config_path, buffer.GetString())) {
|
||||
this->config_dirty = false;
|
||||
} else {
|
||||
log_warning("cardmanager", "unable to save config file to {}", this->config_path.string());
|
||||
log_warning("cardmanager", "unable to save config file");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -219,7 +219,8 @@ namespace overlay::windows {
|
||||
this->config_path = PATCH_MANAGER_CFG_PATH_OVERRIDE.value();
|
||||
log_info("patchmanager", "using custom config file path: {}", this->config_path.string().c_str());
|
||||
} else {
|
||||
this->config_path = std::filesystem::path(_wgetenv(L"APPDATA")) / L"spicetools_patch_manager.json";
|
||||
this->config_path =
|
||||
fileutils::get_config_file_path("patchmanager", "spicetools_patch_manager.json");
|
||||
}
|
||||
|
||||
if (!ldr_registered) {
|
||||
@@ -1127,7 +1128,7 @@ namespace overlay::windows {
|
||||
doc.Accept(writer);
|
||||
|
||||
// save to file
|
||||
if (fileutils::text_write(config_path, buffer.GetString())) {
|
||||
if (fileutils::write_config_file("patchmanager", config_path, buffer.GetString())) {
|
||||
config_dirty = false;
|
||||
} else {
|
||||
log_warning("patchmanager", "unable to save config file");
|
||||
@@ -1644,6 +1645,8 @@ namespace overlay::windows {
|
||||
std::string first_id = get_game_identifier(MODULE_PATH / firstDll);
|
||||
std::filesystem::path firstPath = fmt::format("patches/{}.json", first_id);
|
||||
|
||||
log_misc("patchmanager", "patch identifier of {}: {}", firstDll, first_id);
|
||||
|
||||
auto extraDlls = getExtraDlls(firstDll);
|
||||
std::erase_if(extraDlls, [](const std::string& 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, "
|
||||
"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) ) {
|
||||
graphics_update_z_order(window);
|
||||
if (ImGui::Checkbox("Always On Top", &cfg::SCREENRESIZE->window_always_on_top) ) {
|
||||
graphics_update_z_order(window, cfg::SCREENRESIZE->window_always_on_top);
|
||||
}
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::Checkbox("Forced Render Scaling", &GRAPHICS_WINDOW_BACKBUFFER_SCALE);
|
||||
|
||||
@@ -28,6 +28,8 @@ namespace rawinput {
|
||||
int TOUCHSCREEN_RANGE_X = 0;
|
||||
int TOUCHSCREEN_RANGE_Y = 0;
|
||||
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
|
||||
// this may need to be adjusted for each game in the future if there is a game that polls less
|
||||
@@ -494,7 +496,7 @@ void rawinput::RawInputManager::devices_scan_rawinput(RAWINPUTDEVICELIST *device
|
||||
// get string index
|
||||
ULONG string_index = 0;
|
||||
if (button_caps.IsStringRange && button_caps.Range.StringMin != 0) {
|
||||
string_index = button_caps.Range.StringMin + static_cast<ULONG>(button_output_caps_list.size()) - 1;
|
||||
string_index = button_caps.Range.StringMin + static_cast<ULONG>(button_output_caps_names.size());
|
||||
}
|
||||
else if (!button_caps.IsStringRange && button_caps.NotRange.StringIndex != 0) {
|
||||
string_index = button_caps.NotRange.StringIndex;
|
||||
|
||||
@@ -28,6 +28,9 @@ namespace rawinput {
|
||||
|
||||
extern uint32_t MIDI_NOTE_SUSTAIN;
|
||||
|
||||
extern bool NAIVE_REQUIRE_FOCUS;
|
||||
extern bool RAWINPUT_REQUIRE_FOCUS;
|
||||
|
||||
struct DeviceCallback {
|
||||
void *data;
|
||||
std::function<void(void*, Device*)> f;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
EXPORTS
|
||||
CPU9CheckPaperRemain
|
||||
CPU9CheckPrinter
|
||||
CPU9CheckPrintEnd
|
||||
CPU9GetFWInfo
|
||||
CPU9GetMediaType
|
||||
CPU9GetTempInfo
|
||||
CPU9GetTotalPrintCount
|
||||
CPU9PreHeat
|
||||
CPU9PrintJobCancel
|
||||
CPU9PrintOut
|
||||
CPU9SetPrintParameter2
|
||||
CPUASendImage
|
||||
CPUASendImagePrint
|
||||
CPUASendImagePrint2
|
||||
CPUASetPrintParameter
|
||||
CPUXInit
|
||||
CPUXSearchPrinters
|
||||
@@ -264,3 +264,53 @@ std::vector<uint8_t> *fileutils::bin_read(const std::filesystem::path &path) {
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
std::filesystem::path fileutils::get_config_file_path(const std::string module, const std::string filename, bool* file_exists) {
|
||||
// try %appdata%\spice2x path first, if it exists
|
||||
const auto appdata_spice2x = std::filesystem::path(_wgetenv(L"APPDATA")) / "spice2x" / filename;
|
||||
if (fileutils::file_exists(appdata_spice2x)) {
|
||||
log_info(module, "loading config from %appdata%\\spice2x\\{}", filename);
|
||||
if (file_exists) {
|
||||
*file_exists = true;
|
||||
}
|
||||
return appdata_spice2x;
|
||||
}
|
||||
|
||||
// fallback to older %appdata% path (older spice2x or mainline spicetools), if it exists
|
||||
const auto appdata = std::filesystem::path(_wgetenv(L"APPDATA")) / filename;
|
||||
if (fileutils::file_exists(appdata)) {
|
||||
log_info(module, "loading config from %appdata%\\{}", filename);
|
||||
if (file_exists) {
|
||||
*file_exists = true;
|
||||
}
|
||||
return appdata;
|
||||
}
|
||||
|
||||
// prefer new path if no existing file found
|
||||
if (file_exists) {
|
||||
*file_exists = false;
|
||||
}
|
||||
return appdata_spice2x;
|
||||
}
|
||||
|
||||
bool fileutils::write_config_file(const std::string_view &module, const std::filesystem::path path, std::string text) {
|
||||
// attempt to undo %appdata% expansion to hide user name
|
||||
const auto appdata = std::filesystem::path(_wgetenv(L"APPDATA")).string();
|
||||
auto censored = path.string();
|
||||
const auto substr_offset = censored.find(appdata);
|
||||
if (substr_offset != std::string::npos) {
|
||||
censored.replace(substr_offset, appdata.length(), "%appdata%");
|
||||
}
|
||||
|
||||
// create directory path up to where the config file lives
|
||||
if (!path.parent_path().empty() && !std::filesystem::exists(path.parent_path())) {
|
||||
log_misc(module, "creating directory path to config file: {}", censored);
|
||||
if (!fileutils::dir_create_recursive(path.parent_path())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// save file
|
||||
log_info(module, "saving config file: {}", censored);
|
||||
return fileutils::text_write(path, text);
|
||||
}
|
||||
@@ -33,4 +33,7 @@ namespace fileutils {
|
||||
std::string text_read(const std::filesystem::path &file_path);
|
||||
bool bin_write(const std::filesystem::path &path, uint8_t *data, size_t len);
|
||||
std::vector<uint8_t> *bin_read(const std::filesystem::path &path);
|
||||
|
||||
std::filesystem::path get_config_file_path(const std::string module, const std::string filename, bool* file_exists=nullptr);
|
||||
bool write_config_file(const std::string_view &module, const std::filesystem::path path, std::string text);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "logging.h"
|
||||
#include "utils.h"
|
||||
#include "peb.h"
|
||||
#include "util/fileutils.h"
|
||||
|
||||
std::filesystem::path libutils::module_file_name(HMODULE module) {
|
||||
std::wstring buf;
|
||||
@@ -323,3 +324,47 @@ intptr_t libutils::offset2rva(const std::filesystem::path &path, intptr_t offset
|
||||
|
||||
return rva;
|
||||
}
|
||||
|
||||
void libutils::check_duplicate_dlls() {
|
||||
const auto &spice_bin_path = libutils::module_file_name(nullptr).parent_path();
|
||||
if (MODULE_PATH == spice_bin_path) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &file : std::filesystem::directory_iterator(MODULE_PATH)) {
|
||||
const auto &filename = file.path().filename();
|
||||
const auto extension = strtolower(filename.extension().string());
|
||||
|
||||
if (extension == ".dll" &&
|
||||
fileutils::file_exists(spice_bin_path / filename)) {
|
||||
log_warning(
|
||||
"libutils",
|
||||
"DLL CONFLICT WARNING\n\n\n"
|
||||
"-------------------------------------------------------------------\n"
|
||||
"WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING\n"
|
||||
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
|
||||
"{} exists in BOTH of these directories:\n\n"
|
||||
" 1. {}\n"
|
||||
" 2. {}\n\n"
|
||||
"due to Windows DLL load order rules, #1 will load instead of #2.\n"
|
||||
"this has unintended consequences and may crash your game!\n"
|
||||
"resolve the conflict by deleting the stale copy of the DLL\n"
|
||||
"-------------------------------------------------------------------\n\n\n",
|
||||
filename.string(),
|
||||
(spice_bin_path / filename).string(),
|
||||
(MODULE_PATH / filename).string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,9 @@ namespace libutils {
|
||||
return try_library(module_name.c_str());
|
||||
}
|
||||
|
||||
void check_duplicate_dlls();
|
||||
void warn_if_dll_exists(const std::string &file_name);
|
||||
|
||||
// get module handle helpers
|
||||
HMODULE get_module(const char *module_name);
|
||||
HMODULE try_module(const char *module_name);
|
||||
|
||||
@@ -215,23 +215,42 @@ namespace sysutils {
|
||||
}
|
||||
std::string prefix("device");
|
||||
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() {
|
||||
DWORD device_index = 0;
|
||||
DISPLAY_DEVICEA device;
|
||||
DISPLAY_DEVICEA 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)) {
|
||||
print_adapter(device_index, &device, false);
|
||||
|
||||
DWORD monitor_index = 0;
|
||||
DISPLAY_DEVICEA monitor;
|
||||
DISPLAY_DEVICEA monitor = {};
|
||||
monitor.cb = sizeof(monitor);
|
||||
while (EnumDisplayDevicesA((PCHAR)device.DeviceName, monitor_index, &monitor, 0)) {
|
||||
print_adapter(monitor_index, &monitor, true);
|
||||
|
||||
@@ -227,6 +227,7 @@ static inline std::vector<HWND> find_windows_beginning_with(const std::string &t
|
||||
return windows;
|
||||
}
|
||||
|
||||
// exists for compat only; prefer to use FindProcessWindowBeginsWith instead
|
||||
static inline HWND FindWindowBeginsWith(std::string title) {
|
||||
|
||||
// get all windows
|
||||
@@ -248,6 +249,28 @@ static inline HWND FindWindowBeginsWith(std::string title) {
|
||||
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() {
|
||||
|
||||
// get error
|
||||
|
||||
Reference in New Issue
Block a user