mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-02 06:40:42 -07:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 205cf29aff | |||
| bf9074a76e | |||
| 44e1edb50c | |||
| 41d0dce6e9 | |||
| 598422b701 | |||
| b58a1ccfcc | |||
| 60efd643d1 | |||
| 0596eeb6fe | |||
| 3d5c64feb7 | |||
| 610984f40e | |||
| 1e271b7352 | |||
| 07c78caf01 | |||
| d153209d8a | |||
| 48186245fe | |||
| f7c274591a | |||
| d1a20e58b6 | |||
| 1e02c31524 | |||
| 66fab48a5b | |||
| ae06652148 | |||
| bd6c8d3f3c | |||
| e88abec2a6 | |||
| 6de8b529d2 | |||
| 7301cdaa6c | |||
| 6969dfcdd1 | |||
| edaf5a386f | |||
| 302f08dd57 | |||
| 68f5a614ef | |||
| 5db43031f2 | |||
| 873ade757a | |||
| 3c8dc9de32 | |||
| 4694b59cec | |||
| 4f1ada7a2f | |||
| 104a9cbffd | |||
| 45a52cff90 | |||
| bf79b5d2aa | |||
| e74c60f61d | |||
| 89921e5ec2 | |||
| 1b7ebd6fc2 | |||
| 165cc6028f | |||
| 8dc835bf52 | |||
| 8241641502 | |||
| 6de122c9f8 | |||
| b267ad09ac | |||
| e0dd371d61 | |||
| f8f45a0cbd | |||
| bf8ba050ce |
@@ -0,0 +1,32 @@
|
||||
on: [push, pull_request]
|
||||
name: Continuous Integration
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
fw-ci:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
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: |
|
||||
calculatedSha=$(git rev-parse --short ${{ github.sha }})
|
||||
echo "COMMIT_SHORT_SHA=$calculatedSha" >> $GITHUB_ENV
|
||||
- name: Compile
|
||||
run: ./build_docker.sh
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: spice2x-ci-${{ env.COMMIT_SHORT_SHA }}
|
||||
path: src/spice2x/bin
|
||||
if-no-files-found: error
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
.vscode/*
|
||||
|
||||
# JetBrains IDEs
|
||||
.idea
|
||||
.idea_modules
|
||||
*.iws
|
||||
|
||||
# CMake
|
||||
cmake-build*
|
||||
|
||||
# user config
|
||||
build_all.local.sh
|
||||
build_docker.local.sh
|
||||
@@ -55,3 +55,6 @@ We explicitly do **NOT** have a Discord server for dicussions - we try to do eve
|
||||
|
||||
Please see [CONTRIBUTING page](https://github.com/spice2x/spice2x.github.io/blob/main/CONTRIBUTING.md) for a full list of guidelines when submitting code.
|
||||
|
||||
## Additional information
|
||||
|
||||
Please read [README.md](https://github.com/spice2x/spice2x.github.io/blob/main/src/spice2x/README.md) inside `src/spice2x`.
|
||||
|
||||
@@ -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/*
|
||||
@@ -211,14 +211,12 @@ add_compile_definitions(
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DACIOEMU_LOG")
|
||||
|
||||
# add project directory to include path so we can comfortably import
|
||||
include_directories(${spicetools_SOURCE_DIR})
|
||||
include_directories(${spicetools_SOURCE_DIR} ${spicetools_SOURCE_DIR}/external/imgui)
|
||||
|
||||
# add external libraries
|
||||
add_subdirectory(external/fmt EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(external/discord-rpc EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(external/hash-library EXCLUDE_FROM_ALL)
|
||||
add_compile_definitions(IMGUI_DISABLE_DEMO_WINDOWS)
|
||||
add_compile_definitions(IMGUI_DISABLE_DEBUG_TOOLS)
|
||||
add_subdirectory(external/imgui EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(external/minhook EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(external/cpu_features EXCLUDE_FROM_ALL)
|
||||
@@ -302,6 +300,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
api/modules/drs.cpp
|
||||
api/modules/lcd.cpp
|
||||
api/modules/ddr.cpp
|
||||
api/modules/resize.cpp
|
||||
|
||||
# avs
|
||||
avs/core.cpp
|
||||
@@ -441,6 +440,9 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
games/qks/qks.cpp
|
||||
games/qks/io.cpp
|
||||
games/qks/bi2x_hook.cpp
|
||||
games/mfg/mfg.cpp
|
||||
games/mfg/io.cpp
|
||||
games/mfg/bi2a_hook.cpp
|
||||
|
||||
# hooks
|
||||
hooks/audio/audio.cpp
|
||||
@@ -512,7 +514,6 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
overlay/overlay.cpp
|
||||
overlay/window.cpp
|
||||
overlay/imgui/extensions.cpp
|
||||
overlay/imgui/impl_dx9.cpp
|
||||
overlay/imgui/impl_spice.cpp
|
||||
overlay/imgui/impl_sw.cpp
|
||||
overlay/windows/acio_status_buffers.cpp
|
||||
@@ -586,6 +587,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
util/sysutils.cpp
|
||||
util/lz77.cpp
|
||||
util/tapeled.cpp
|
||||
util/execexe.cpp
|
||||
)
|
||||
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "Source Files" FILES ${SOURCE_FILES})
|
||||
@@ -605,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
|
||||
#############
|
||||
|
||||
@@ -624,7 +642,6 @@ IF(NOT MSVC)
|
||||
set_target_properties(spicetools_spice64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
|
||||
endif()
|
||||
|
||||
|
||||
# spicecfg.exe
|
||||
##############
|
||||
|
||||
@@ -730,7 +747,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
|
||||
PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive32"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32"
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
FROM spicetools/deps
|
||||
WORKDIR /src
|
||||
RUN chown user:user /src
|
||||
USER user
|
||||
COPY --chown=user:user --from=gitroot . /src/.git
|
||||
COPY --chown=user:user . /src/src/spice2x
|
||||
|
||||
COPY --from=gitroot . /src/.git
|
||||
COPY . /src/src/spice2x
|
||||
WORKDIR /src/src/spice2x
|
||||
CMD ["./build_all.sh"]
|
||||
ENTRYPOINT ["./build_all.sh"]
|
||||
|
||||
@@ -181,6 +181,12 @@ All of those three modules have equally named methods for you to call.
|
||||
- removes the override value from the objects specified by name
|
||||
- if no names were passed, all overrides will be removed
|
||||
|
||||
##### Additional API for lights
|
||||
- read(name: string, ...)
|
||||
- same as read(), but you can specify light names
|
||||
- write_reset(name: str, ...)
|
||||
- same as write_reset(), but it accepts a flat list of strings
|
||||
|
||||
#### Touch
|
||||
- read()
|
||||
- returns an array of state objects containing id, x and y
|
||||
@@ -246,6 +252,18 @@ which also means that your hex edits are applicable directly.
|
||||
- info()
|
||||
- returns information about the serial LCD controller some games use
|
||||
|
||||
#### Resize
|
||||
- image_resize_enable(enable: bool)
|
||||
- enables or disables image resize state
|
||||
- image_resize_set_scene(scene: int)
|
||||
- sets the active scene for image resize state; set to 0 to disable resize
|
||||
|
||||
## Native wrapper libraries
|
||||
Spicetools provides wrapper libraries in: Arduino, C++, Dart, and Python.
|
||||
Python is the only one that is fully spec compliant.
|
||||
Other libraries may be missing features and contain bugs; please feel free to
|
||||
contribute code to fill the gaps if you work on a project using these libraries.
|
||||
|
||||
## License
|
||||
Unless otherwise noted, all files are licensed under the GPLv3.
|
||||
See the LICENSE file for the full license text.
|
||||
|
||||
@@ -33,6 +33,11 @@ static int __cdecl ac_io_bmpu_control_1p_start_led_off() {
|
||||
if (avs::game::is_model("KDM")) {
|
||||
auto &lights = games::dea::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P1Start), 0.f);
|
||||
|
||||
// MUSECA
|
||||
} else if (avs::game::is_model("PIX")) {
|
||||
auto &lights = games::museca::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights.at(games::museca::Lights::Start), 0.f);
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -44,6 +49,11 @@ static int __cdecl ac_io_bmpu_control_1p_start_led_on() {
|
||||
if (avs::game::is_model("KDM")) {
|
||||
auto &lights = games::dea::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P1Start), 1.f);
|
||||
|
||||
// MUSECA
|
||||
} else if (avs::game::is_model("PIX")) {
|
||||
auto &lights = games::museca::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights.at(games::museca::Lights::Start), 1.f);
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -55,6 +65,11 @@ static int __cdecl ac_io_bmpu_control_2p_start_led_off() {
|
||||
if (avs::game::is_model("KDM")) {
|
||||
auto &lights = games::dea::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P2Start), 0.f);
|
||||
|
||||
// MUSECA
|
||||
} else if (avs::game::is_model("PIX")) {
|
||||
auto &lights = games::museca::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights.at(games::museca::Lights::Keypad), 0.f);
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -66,6 +81,11 @@ static int __cdecl ac_io_bmpu_control_2p_start_led_on() {
|
||||
if (avs::game::is_model("KDM")) {
|
||||
auto &lights = games::dea::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P2Start), 1.f);
|
||||
|
||||
// MUSECA
|
||||
} else if (avs::game::is_model("PIX")) {
|
||||
auto &lights = games::museca::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights.at(games::museca::Lights::Keypad), 1.f);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
acioemu::ACIOHandle::ACIOHandle(LPCWSTR lpCOMPort) {
|
||||
acioemu::ACIOHandle::ACIOHandle(LPCWSTR lpCOMPort, uint8_t iccaNodeCount) {
|
||||
this->com_port = lpCOMPort;
|
||||
this->icca_node_count = iccaNodeCount;
|
||||
}
|
||||
|
||||
bool acioemu::ACIOHandle::open(LPCWSTR lpFileName) {
|
||||
@@ -16,7 +17,7 @@ bool acioemu::ACIOHandle::open(LPCWSTR lpFileName) {
|
||||
log_info("acioemu", "Opened {} (ACIO)", ws2s(com_port));
|
||||
|
||||
// ACIO device
|
||||
acio_emu.add_device(new acioemu::ICCADevice(false, true, 2));
|
||||
acio_emu.add_device(new acioemu::ICCADevice(false, true, icca_node_count));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -9,10 +9,12 @@ namespace acioemu {
|
||||
private:
|
||||
LPCWSTR com_port;
|
||||
|
||||
uint8_t icca_node_count;
|
||||
|
||||
acioemu::ACIOEmu acio_emu;
|
||||
|
||||
public:
|
||||
ACIOHandle(LPCWSTR lpCOMPort);
|
||||
ACIOHandle(LPCWSTR lpCOMPort, uint8_t iccaNodeCount = 2);
|
||||
|
||||
bool open(LPCWSTR lpFileName) override;
|
||||
|
||||
|
||||
@@ -107,6 +107,8 @@ bool ICCADevice::parse_msg(MessageData *msg_in,
|
||||
(avs::game::is_model("KFC") && (avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H'))
|
||||
) {
|
||||
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCA");
|
||||
} else if (avs::game::is_model({"VFG"})) {
|
||||
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCB");
|
||||
} else {
|
||||
this->set_version(msg, 0x3, 0, 1, 6, 0, "ICCA");
|
||||
}
|
||||
@@ -333,6 +335,7 @@ bool ICCADevice::parse_msg(MessageData *msg_in,
|
||||
case ACIO_CMD_CLEAR:
|
||||
case 0x30: // GetBoardProductNumber
|
||||
case 0x31: // GetMicomInfo
|
||||
case 0x3A: // ???
|
||||
case 0x0116: // ???
|
||||
case 0x0120: // ???
|
||||
case 0xFF: // BROADCAST
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "modules/lights.h"
|
||||
#include "modules/memory.h"
|
||||
#include "modules/touch.h"
|
||||
#include "modules/resize.h"
|
||||
#include "request.h"
|
||||
#include "response.h"
|
||||
|
||||
@@ -403,6 +404,7 @@ void Controller::init_state(api::ClientState *state) {
|
||||
state->modules.push_back(new modules::Lights());
|
||||
state->modules.push_back(new modules::Memory());
|
||||
state->modules.push_back(new modules::Touch());
|
||||
state->modules.push_back(new modules::Resize());
|
||||
}
|
||||
|
||||
void Controller::free_state(api::ClientState *state) {
|
||||
|
||||
@@ -19,11 +19,16 @@ namespace api::modules {
|
||||
functions["read"] = std::bind(&Lights::read, this, _1, _2);
|
||||
functions["write"] = std::bind(&Lights::write, this, _1, _2);
|
||||
functions["write_reset"] = std::bind(&Lights::write_reset, this, _1, _2);
|
||||
lights = games::get_lights(eamuse_get_game());
|
||||
|
||||
this->lights = games::get_lights(eamuse_get_game());
|
||||
for (auto &light : *this->lights) {
|
||||
this->lights_by_names.emplace(light.getName(), light);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* read()
|
||||
* read(name: str, ...)
|
||||
*/
|
||||
void Lights::read(api::Request &req, Response &res) {
|
||||
|
||||
@@ -32,17 +37,39 @@ namespace api::modules {
|
||||
return;
|
||||
}
|
||||
|
||||
// add state for each light
|
||||
for (auto &light : *this->lights) {
|
||||
Value state(kArrayType);
|
||||
Value light_name(light.getName().c_str(), res.doc()->GetAllocator());
|
||||
Value light_state(GameAPI::Lights::readLight(RI_MGR, light));
|
||||
Value light_enabled(light.override_enabled);
|
||||
state.PushBack(light_name, res.doc()->GetAllocator());
|
||||
state.PushBack(light_state, res.doc()->GetAllocator());
|
||||
state.PushBack(light_enabled, res.doc()->GetAllocator());
|
||||
res.add_data(state);
|
||||
// all lights for this game
|
||||
if (req.params.Size() == 0) {
|
||||
// add state for each light
|
||||
for (auto &light : *this->lights) {
|
||||
get_light(light, res);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// specified light names
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
// check params
|
||||
if (!param.IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
return;
|
||||
}
|
||||
const auto name = param.GetString();
|
||||
if (this->lights_by_names.contains(name)) {
|
||||
get_light(this->lights_by_names.at(name).get(), res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Lights::get_light(Light &light, Response &res) {
|
||||
Value state(kArrayType);
|
||||
Value light_name(light.getName().c_str(), res.doc()->GetAllocator());
|
||||
Value light_state(GameAPI::Lights::readLight(RI_MGR, light));
|
||||
Value light_enabled(light.override_enabled);
|
||||
state.PushBack(light_name, res.doc()->GetAllocator());
|
||||
state.PushBack(light_state, res.doc()->GetAllocator());
|
||||
state.PushBack(light_enabled, res.doc()->GetAllocator());
|
||||
res.add_data(state);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,6 +117,7 @@ namespace api::modules {
|
||||
|
||||
/**
|
||||
* write_reset()
|
||||
* write_reset(name: str, ...)
|
||||
* write_reset([name: str], ...)
|
||||
*/
|
||||
void Lights::write_reset(Request &req, Response &res) {
|
||||
@@ -119,26 +147,28 @@ namespace api::modules {
|
||||
|
||||
// loop parameters
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
const char* light_name = nullptr;
|
||||
|
||||
// check params
|
||||
if (!param.IsArray()) {
|
||||
error(res, "parameters must be arrays");
|
||||
return;
|
||||
if (param.IsArray()) {
|
||||
if (param.Size() < 1) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
// get params
|
||||
light_name = param[0].GetString();
|
||||
} else if (param.IsString()) {
|
||||
light_name = param.GetString();
|
||||
} else {
|
||||
error(res, "parameters must be arrays or strings");
|
||||
}
|
||||
if (param.Size() < 1) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto light_name = param[0].GetString();
|
||||
|
||||
// write analog state
|
||||
if (!this->write_light_reset(light_name)) {
|
||||
if (light_name && !this->write_light_reset(light_name)) {
|
||||
error_unknown(res, "analog", light_name);
|
||||
continue;
|
||||
}
|
||||
@@ -153,21 +183,20 @@ namespace api::modules {
|
||||
}
|
||||
|
||||
// find light
|
||||
for (auto &light : *this->lights) {
|
||||
if (light.getName() == name) {
|
||||
light.override_state = CLAMP(state, 0.f, 1.f);
|
||||
light.override_enabled = true;
|
||||
if (this->lights_by_names.contains(name)) {
|
||||
auto &light = this->lights_by_names.at(name).get();
|
||||
light.override_state = CLAMP(state, 0.f, 1.f);
|
||||
light.override_enabled = true;
|
||||
|
||||
if (cfg::CONFIGURATOR_STANDALONE) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, light, state);
|
||||
}
|
||||
|
||||
return true;
|
||||
if (cfg::CONFIGURATOR_STANDALONE) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, light, state);
|
||||
}
|
||||
}
|
||||
|
||||
// unknown light
|
||||
return false;
|
||||
return true;
|
||||
} else {
|
||||
// unknown light
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Lights::write_light_reset(std::string name) {
|
||||
@@ -178,14 +207,13 @@ namespace api::modules {
|
||||
}
|
||||
|
||||
// find light
|
||||
for (auto &light : *this->lights) {
|
||||
if (light.getName() == name) {
|
||||
light.override_enabled = false;
|
||||
return true;
|
||||
}
|
||||
if (this->lights_by_names.contains(name)) {
|
||||
auto &light = this->lights_by_names.at(name).get();
|
||||
light.override_enabled = false;
|
||||
return true;
|
||||
} else {
|
||||
// unknown light
|
||||
return false;
|
||||
}
|
||||
|
||||
// unknown light
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <external/robin_hood.h>
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
#include "cfg/api.h"
|
||||
@@ -15,6 +17,7 @@ namespace api::modules {
|
||||
|
||||
// state
|
||||
std::vector<Light> *lights;
|
||||
robin_hood::unordered_map<std::string, std::reference_wrapper<Light>> lights_by_names;
|
||||
|
||||
// function definitions
|
||||
void read(Request &req, Response &res);
|
||||
@@ -22,6 +25,7 @@ namespace api::modules {
|
||||
void write_reset(Request &req, Response &res);
|
||||
|
||||
// helper
|
||||
void get_light(Light &light, Response &res);
|
||||
bool write_light(std::string name, float state);
|
||||
bool write_light_reset(std::string name);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
#include "resize.h"
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "cfg/screen_resize.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
static thread_local std::vector<uint8_t> CAPTURE_BUFFER;
|
||||
|
||||
Resize::Resize() : Module("resize") {
|
||||
functions["image_resize_enable"] = std::bind(&Resize::image_resize_enable, this, _1, _2);
|
||||
functions["image_resize_set_scene"] = std::bind(&Resize::image_resize_set_scene, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* image_resize_enable(enable: bool)
|
||||
*/
|
||||
void Resize::image_resize_enable(Request &req, Response &res) {
|
||||
if (req.params.Size() < 1) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsBool()) {
|
||||
return error_type(res, "enable", "bool");
|
||||
}
|
||||
|
||||
cfg::SCREENRESIZE->enable_screen_resize = req.params[0].GetBool();
|
||||
}
|
||||
|
||||
/**
|
||||
* image_resize_set_scene(scene: int)
|
||||
*/
|
||||
void Resize::image_resize_set_scene(Request &req, Response &res) {
|
||||
if (req.params.Size() < 1) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsInt()) {
|
||||
return error_type(res, "scene", "int");
|
||||
}
|
||||
|
||||
const auto scene = req.params[0].GetInt();
|
||||
if (scene < 0 || (int)std::size(cfg::SCREENRESIZE->scene_settings) < scene) {
|
||||
return error(res, "invalid scene number");
|
||||
}
|
||||
if (scene == 0) {
|
||||
cfg::SCREENRESIZE->enable_screen_resize = false;
|
||||
} else {
|
||||
cfg::SCREENRESIZE->enable_screen_resize = true;
|
||||
cfg::SCREENRESIZE->screen_resize_current_scene = scene - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Resize : public Module {
|
||||
public:
|
||||
Resize();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void image_resize_enable(Request &req, Response &res);
|
||||
void image_resize_set_scene(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
@@ -12,3 +12,4 @@ from .keypads import *
|
||||
from .lights import *
|
||||
from .memory import *
|
||||
from .touch import *
|
||||
from .resize import *
|
||||
@@ -2,8 +2,14 @@ from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def lights_read(con: Connection):
|
||||
res = con.request(Request("lights", "read"))
|
||||
def lights_read(con: Connection, light_names=None):
|
||||
req = Request("lights", "read")
|
||||
|
||||
if light_names:
|
||||
for light_name in light_names:
|
||||
req.add_param(light_name)
|
||||
|
||||
res = con.request(req)
|
||||
return res.get_data()
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
def image_resize_enable(con: Connection, enable: bool):
|
||||
req = Request("resize", "image_resize_enable")
|
||||
req.add_param(enable)
|
||||
con.request(req)
|
||||
|
||||
def image_resize_set_scene(con: Connection, scene: int):
|
||||
req = Request("resize", "image_resize_set_scene")
|
||||
req.add_param(scene)
|
||||
con.request(req)
|
||||
@@ -397,6 +397,65 @@ class LightsTab(ttk.Frame):
|
||||
# set text
|
||||
self.txt_lights.set_text(txt)
|
||||
|
||||
class ResizeTab(ttk.Frame):
|
||||
"""Resize tab."""
|
||||
|
||||
def __init__(self, app, parent, **kwargs):
|
||||
|
||||
# init frame
|
||||
ttk.Frame.__init__(self, parent, **kwargs)
|
||||
self.app = app
|
||||
self.parent = parent
|
||||
|
||||
# scale grid
|
||||
self.columnconfigure(0, weight=1)
|
||||
|
||||
# image resize
|
||||
self.resize = ttk.Frame(self, padding=(8, 8, 8, 8))
|
||||
self.resize.grid(row=0, column=0, sticky=tk.E+tk.W)
|
||||
self.resize.columnconfigure(0, weight=1)
|
||||
self.resize.columnconfigure(1, weight=1)
|
||||
self.resize_lbl = ttk.Label(self.resize, text="Image Resize")
|
||||
self.resize_lbl.grid(row=0, columnspan=2)
|
||||
|
||||
self.resize_off = ttk.Button(self.resize, text="Disable", command=self.action_resize_false)
|
||||
self.resize_off.grid(row=2, column=0, sticky=NSEW, padx=2, pady=2)
|
||||
|
||||
self.resize_on = ttk.Button(self.resize, text="Enable", command=self.action_resize_on)
|
||||
self.resize_on.grid(row=2, column=1, sticky=NSEW, padx=2, pady=2)
|
||||
|
||||
self.resize_scene_1 = ttk.Button(self.resize, text="Scene 1", command=self.action_resize_scene_1)
|
||||
self.resize_scene_1.grid(row=3, column=0, sticky=NSEW, padx=2, pady=2)
|
||||
self.resize_scene_2 = ttk.Button(self.resize, text="Scene 2", command=self.action_resize_scene_2)
|
||||
self.resize_scene_2.grid(row=3, column=1, sticky=NSEW, padx=2, pady=2)
|
||||
self.resize_scene_3 = ttk.Button(self.resize, text="Scene 3", command=self.action_resize_scene_3)
|
||||
self.resize_scene_3.grid(row=4, column=0, sticky=NSEW, padx=2, pady=2)
|
||||
self.resize_scene_4 = ttk.Button(self.resize, text="Scene 4", command=self.action_resize_scene_4)
|
||||
self.resize_scene_4.grid(row=4, column=1, sticky=NSEW, padx=2, pady=2)
|
||||
|
||||
@api_action
|
||||
def action_resize_on(self):
|
||||
spiceapi.image_resize_enable(self.app.connection, True)
|
||||
|
||||
@api_action
|
||||
def action_resize_false(self):
|
||||
spiceapi.image_resize_enable(self.app.connection, False)
|
||||
|
||||
@api_action
|
||||
def action_resize_scene_1(self):
|
||||
spiceapi.image_resize_set_scene(self.app.connection, 1)
|
||||
|
||||
@api_action
|
||||
def action_resize_scene_2(self):
|
||||
spiceapi.image_resize_set_scene(self.app.connection, 2)
|
||||
|
||||
@api_action
|
||||
def action_resize_scene_3(self):
|
||||
spiceapi.image_resize_set_scene(self.app.connection, 3)
|
||||
|
||||
@api_action
|
||||
def action_resize_scene_4(self):
|
||||
spiceapi.image_resize_set_scene(self.app.connection, 4)
|
||||
|
||||
class MainApp(ttk.Frame):
|
||||
"""The main application frame."""
|
||||
@@ -419,6 +478,8 @@ class MainApp(ttk.Frame):
|
||||
self.tabs.add(self.tab_analogs, text="Analogs")
|
||||
self.tab_lights = LightsTab(self, self.tabs)
|
||||
self.tabs.add(self.tab_lights, text="Lights")
|
||||
self.tab_resize = ResizeTab(self, self.tabs)
|
||||
self.tabs.add(self.tab_resize, text="Resize")
|
||||
self.tab_manual = ManualTab(self, self.tabs)
|
||||
self.tabs.add(self.tab_manual, text="Manual")
|
||||
self.tabs.pack(expand=True, fill=tk.BOTH)
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
Regular → Executable
+58
-26
@@ -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"
|
||||
TARGETS_64="spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvEncodeAPI64 spicetools_stubs_nvcuvid spicetools_stubs_nvcuda spicetools_spice64"
|
||||
|
||||
# determine build type
|
||||
@@ -57,7 +76,7 @@ then
|
||||
fi
|
||||
|
||||
# determine number of cores
|
||||
CORES=$(awk '/^processor\t/ {cores[$NF]++} END {print length(cores)}' /proc/cpuinfo)
|
||||
CORES=$(nproc)
|
||||
|
||||
# print information
|
||||
echo ""
|
||||
@@ -74,34 +93,45 @@ echo "Build Type: $BUILD_TYPE"
|
||||
echo "Cores: $CORES"
|
||||
echo ""
|
||||
|
||||
# 32 bit
|
||||
echo "Building 32bit targets..."
|
||||
echo "========================="
|
||||
if ((CLEAN_BUILD > 0))
|
||||
if ((IGNORE_CACHE > 0))
|
||||
then
|
||||
rm -rf ${BUILDDIR_32}
|
||||
echo "Ignoring build cache..."
|
||||
else
|
||||
export CCACHE_DIR="$(pwd)/.ccache"
|
||||
export CMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
export CMAKE_C_COMPILER_LAUNCHER=ccache
|
||||
fi
|
||||
mkdir -p ${BUILDDIR_32}
|
||||
pushd ${BUILDDIR_32} > /dev/null
|
||||
cmake -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_32} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && make -j ${CORES} ${TARGETS_32}
|
||||
popd > /dev/null
|
||||
|
||||
# 64 bit
|
||||
echo ""
|
||||
echo "Building 64bit targets..."
|
||||
echo "========================="
|
||||
if ((CLEAN_BUILD > 0))
|
||||
then
|
||||
rm -rf ${BUILDDIR_64}
|
||||
fi
|
||||
mkdir -p ${BUILDDIR_64}
|
||||
pushd ${BUILDDIR_64} > /dev/null
|
||||
cmake -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && make -j ${CORES} ${TARGETS_64}
|
||||
popd > /dev/null
|
||||
time (
|
||||
# 32 bit
|
||||
echo "Building 32bit targets..."
|
||||
echo "========================="
|
||||
if ((CLEAN_BUILD > 0))
|
||||
then
|
||||
rm -rf ${BUILDDIR_32}
|
||||
fi
|
||||
mkdir -p ${BUILDDIR_32}
|
||||
pushd ${BUILDDIR_32} > /dev/null
|
||||
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_32} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && ninja ${TARGETS_32}
|
||||
popd > /dev/null
|
||||
|
||||
echo ""
|
||||
echo "Compilation process done :)"
|
||||
echo "==========================="
|
||||
# 64 bit
|
||||
echo ""
|
||||
echo "Building 64bit targets..."
|
||||
echo "========================="
|
||||
if ((CLEAN_BUILD > 0))
|
||||
then
|
||||
rm -rf ${BUILDDIR_64}
|
||||
fi
|
||||
mkdir -p ${BUILDDIR_64}
|
||||
pushd ${BUILDDIR_64} > /dev/null
|
||||
cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && ninja ${TARGETS_64}
|
||||
popd > /dev/null
|
||||
|
||||
echo ""
|
||||
echo "Compilation process done :)"
|
||||
echo "==========================="
|
||||
)
|
||||
|
||||
# generate PDBs
|
||||
if false # ((DEBUG > 0))
|
||||
@@ -153,6 +183,7 @@ rm -rf ${OUTDIR}
|
||||
mkdir -p ${OUTDIR}
|
||||
#mkdir -p ${OUTDIR}/stubs/32
|
||||
mkdir -p ${OUTDIR}/stubs/64
|
||||
mkdir -p ${OUTDIR}/extras/largeaddressaware
|
||||
if false # ((DEBUG > 0))
|
||||
then
|
||||
# debug files
|
||||
@@ -173,6 +204,7 @@ 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
|
||||
|
||||
@@ -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
|
||||
|
||||
Regular → Executable
+2
-4
@@ -1,6 +1,4 @@
|
||||
#!/bin/bash
|
||||
export DOCKER_BUILDKIT=0
|
||||
|
||||
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 -it -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
|
||||
@@ -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");
|
||||
@@ -76,14 +76,18 @@ namespace cfg {
|
||||
eamuse_get_game(),
|
||||
use_game_setting,
|
||||
root);
|
||||
load_int_value(doc, root + "offset_x", this->offset_x);
|
||||
load_int_value(doc, root + "offset_y", this->offset_y);
|
||||
load_float_value(doc, root + "scale_x", this->scale_x);
|
||||
load_float_value(doc, root + "scale_y", this->scale_y);
|
||||
|
||||
load_bool_value(doc, root + "enable_screen_resize", this->enable_screen_resize);
|
||||
load_bool_value(doc, root + "enable_linear_filter", this->enable_linear_filter);
|
||||
load_bool_value(doc, root + "keep_aspect_ratio", this->keep_aspect_ratio);
|
||||
load_bool_value(doc, root + "centered", this->centered);
|
||||
for (size_t i = 0; i < std::size(this->scene_settings); i++) {
|
||||
auto& scene = this->scene_settings[i];
|
||||
const std::string prefix = fmt::format("scenes/{}/", i);
|
||||
load_int_value(doc, root + prefix + "offset_x", scene.offset_x);
|
||||
load_int_value(doc, root + prefix + "offset_y", scene.offset_y);
|
||||
load_float_value(doc, root + prefix + "scale_x", scene.scale_x);
|
||||
load_float_value(doc, root + prefix + "scale_y", scene.scale_y);
|
||||
load_bool_value(doc, root + prefix + "keep_aspect_ratio", scene.keep_aspect_ratio);
|
||||
}
|
||||
|
||||
// windowed settings are always under game settings
|
||||
root = "/sp2x_games/" + eamuse_get_game() + "/";
|
||||
@@ -166,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()) {
|
||||
@@ -189,14 +191,17 @@ namespace cfg {
|
||||
root);
|
||||
|
||||
// full screen image settings
|
||||
rapidjson::Pointer(root + "offset_x").Set(doc, this->offset_x);
|
||||
rapidjson::Pointer(root + "offset_y").Set(doc, this->offset_y);
|
||||
rapidjson::Pointer(root + "scale_x").Set(doc, this->scale_x);
|
||||
rapidjson::Pointer(root + "scale_y").Set(doc, this->scale_y);
|
||||
rapidjson::Pointer(root + "enable_screen_resize").Set(doc, this->enable_screen_resize);
|
||||
rapidjson::Pointer(root + "enable_linear_filter").Set(doc, this->enable_linear_filter);
|
||||
rapidjson::Pointer(root + "keep_aspect_ratio").Set(doc, this->keep_aspect_ratio);
|
||||
rapidjson::Pointer(root + "centered").Set(doc, this->centered);
|
||||
for (size_t i = 0; i < std::size(this->scene_settings); i++) {
|
||||
auto& scene = this->scene_settings[i];
|
||||
const std::string prefix = fmt::format("scenes/{}/", i);
|
||||
rapidjson::Pointer(root + prefix + "offset_x").Set(doc, scene.offset_x);
|
||||
rapidjson::Pointer(root + prefix + "offset_y").Set(doc, scene.offset_y);
|
||||
rapidjson::Pointer(root + prefix + "scale_x").Set(doc, scene.scale_x);
|
||||
rapidjson::Pointer(root + prefix + "scale_y").Set(doc, scene.scale_y);
|
||||
rapidjson::Pointer(root + prefix + "keep_aspect_ratio").Set(doc, scene.keep_aspect_ratio);
|
||||
}
|
||||
|
||||
// windowed mode settings
|
||||
rapidjson::Pointer(root + "w_always_on_top").Set(doc, this->window_always_on_top);
|
||||
@@ -214,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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,14 @@ namespace cfg {
|
||||
ResizableFrame = 2
|
||||
};
|
||||
|
||||
struct fullscreen_setting {
|
||||
int offset_x = 0;
|
||||
int offset_y = 0;
|
||||
float scale_x = 1.0;
|
||||
float scale_y = 1.0;
|
||||
bool keep_aspect_ratio = true;
|
||||
};
|
||||
|
||||
extern std::optional<std::string> SCREEN_RESIZE_CFG_PATH_OVERRIDE;
|
||||
|
||||
class ScreenResize {
|
||||
@@ -31,14 +39,10 @@ namespace cfg {
|
||||
~ScreenResize();
|
||||
|
||||
// full screen (directx) image settings
|
||||
int offset_x = 0;
|
||||
int offset_y = 0;
|
||||
float scale_x = 1.0;
|
||||
float scale_y = 1.0;
|
||||
bool enable_screen_resize = false;
|
||||
int8_t screen_resize_current_scene = 0;
|
||||
bool enable_linear_filter = true;
|
||||
bool keep_aspect_ratio = true;
|
||||
bool centered = true;
|
||||
fullscreen_setting scene_settings[4];
|
||||
|
||||
// windowed mode sizing
|
||||
// Windows terminology:
|
||||
|
||||
@@ -1,3 +1,28 @@
|
||||
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
|
||||
Add missing lights in Museca
|
||||
|
||||
03/29/2025 [spice2x]
|
||||
Add SDVX landscape mode, -forceresswap option
|
||||
Fix bugs in image scaler
|
||||
Configurator UI tweaks
|
||||
|
||||
03/25/2025 [spice2x]
|
||||
Add Mahjong Fight Girl support
|
||||
Add option for custom full screen resolution (-forceres)
|
||||
|
||||
03/24/2025 [spice2x]
|
||||
Auto PIN entry
|
||||
Multiple scenes for image resize
|
||||
API: add resize function, lights.read can now filter lights by name
|
||||
Fix DLL hooks not receiving command-line arguments
|
||||
|
||||
03/16/2025 [spice2x]
|
||||
Fix clipboard copy function truncating last character of string
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required (VERSION 3.2.0)
|
||||
cmake_minimum_required (VERSION 3.12)
|
||||
project (DiscordRPC)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
+6
-14
@@ -1,25 +1,17 @@
|
||||
FROM archlinux:base
|
||||
FROM ghcr.io/archlinux/archlinux:base-devel
|
||||
|
||||
RUN pacman --noconfirm -Syu gettext \
|
||||
base-devel \
|
||||
git \
|
||||
RUN pacman --noconfirm -Syu git \
|
||||
bash \
|
||||
zip \
|
||||
upx \
|
||||
sudo \
|
||||
binutils \
|
||||
file \
|
||||
make \
|
||||
gcc \
|
||||
fakeroot \
|
||||
diffutils \
|
||||
ninja \
|
||||
cmake \
|
||||
awk \
|
||||
unzip \
|
||||
mingw-w64-crt \
|
||||
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.git --depth=1 && cd yay && makepkg --noconfirm -si && yay --noconfirm -S mingw-w64-cmake"
|
||||
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
|
||||
|
||||
+5
-3
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.9)
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
project(imgui CXX)
|
||||
|
||||
# dear imgui uses the C++ virtual interfaces for DirectX
|
||||
@@ -8,7 +8,8 @@ set(IMGUI_HEADERS
|
||||
imconfig.h
|
||||
imgui.h
|
||||
imgui_internal.h
|
||||
imgui_stdlib.h
|
||||
backends/imgui_impl_dx9.h
|
||||
misc/cpp/imgui_stdlib.h
|
||||
)
|
||||
set(IMGUI_SOURCES
|
||||
imgui.cpp
|
||||
@@ -16,7 +17,8 @@ set(IMGUI_SOURCES
|
||||
imgui_tables.cpp
|
||||
imgui_widgets.cpp
|
||||
imgui_demo.cpp
|
||||
imgui_stdlib.cpp
|
||||
backends/imgui_impl_dx9.cpp
|
||||
misc/cpp/imgui_stdlib.cpp
|
||||
)
|
||||
|
||||
add_library(imgui STATIC ${IMGUI_HEADERS} ${IMGUI_SOURCES})
|
||||
|
||||
Vendored
+3
-3
@@ -22,14 +22,14 @@
|
||||
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX9_RenderDrawData() in the .h file so you can call it yourself.
|
||||
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
|
||||
|
||||
#include "impl_dx9.h"
|
||||
#include "imgui_impl_dx9.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
// DirectX
|
||||
#include <d3d9.h>
|
||||
|
||||
#include "external/imgui/imgui.h"
|
||||
#include "imgui.h"
|
||||
|
||||
// allow std::min use
|
||||
#ifdef min
|
||||
@@ -327,7 +327,7 @@ void ImGui_ImplDX9_InvalidateDeviceObjects() {
|
||||
if (g_FontTexture) {
|
||||
g_FontTexture->Release();
|
||||
g_FontTexture = NULL;
|
||||
ImGui::GetIO().Fonts->TexID = NULL;
|
||||
ImGui::GetIO().Fonts->TexID = 0;
|
||||
} // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -11,7 +11,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "external/imgui/imgui.h"
|
||||
#include "imgui.h"
|
||||
|
||||
struct IDirect3DDevice9;
|
||||
|
||||
+33
-16
@@ -1,5 +1,5 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
|
||||
// DEAR IMGUI COMPILE-TIME OPTIONS
|
||||
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
|
||||
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -9,7 +9,7 @@
|
||||
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
|
||||
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
|
||||
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
|
||||
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
|
||||
// Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
@@ -21,27 +21,29 @@
|
||||
|
||||
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
|
||||
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
|
||||
// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
|
||||
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
|
||||
//#define IMGUI_API __declspec( dllexport )
|
||||
//#define IMGUI_API __declspec( dllimport )
|
||||
// - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
|
||||
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
|
||||
//#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export
|
||||
//#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import
|
||||
//#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden
|
||||
|
||||
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
|
||||
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
|
||||
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions.
|
||||
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
|
||||
|
||||
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
||||
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
|
||||
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
|
||||
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
|
||||
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowStackToolWindow() will be empty (this was called IMGUI_DISABLE_METRICS_WINDOW before 1.88).
|
||||
#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
|
||||
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty.
|
||||
|
||||
//---- Don't implement some functions to reduce linkage requirements.
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
|
||||
#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
|
||||
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
|
||||
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
|
||||
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
||||
//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
|
||||
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
|
||||
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
|
||||
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
|
||||
@@ -49,8 +51,13 @@
|
||||
#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
||||
|
||||
//---- Enable Test Engine / Automation features.
|
||||
//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details.
|
||||
|
||||
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
|
||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
|
||||
|
||||
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
|
||||
#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||
@@ -58,16 +65,17 @@
|
||||
//---- Performance optimization for DX9
|
||||
#define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; float z; ImU32 col; ImVec2 uv; }
|
||||
|
||||
//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||
//#define IMGUI_USE_WCHAR32
|
||||
|
||||
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
|
||||
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
|
||||
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
|
||||
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
|
||||
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if enabled
|
||||
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined.
|
||||
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
|
||||
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
||||
//#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined.
|
||||
|
||||
//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
|
||||
// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
|
||||
@@ -78,6 +86,15 @@
|
||||
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
|
||||
//#define IMGUI_ENABLE_FREETYPE
|
||||
|
||||
//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
|
||||
// Only works in combination with IMGUI_ENABLE_FREETYPE.
|
||||
// - lunasvg is currently easier to acquire/install, as e.g. it is part of vcpkg.
|
||||
// - plutosvg will support more fonts and may load them faster. It currently requires to be built manually but it is fairly easy. See misc/freetype/README for instructions.
|
||||
// - Both require headers to be available in the include path + program to be linked with the library code (not provided).
|
||||
// - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
|
||||
//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG
|
||||
//#define IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||
|
||||
//---- Use stb_truetype to build and rasterize the font atlas (default)
|
||||
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
|
||||
//#define IMGUI_ENABLE_STB_TRUETYPE
|
||||
@@ -108,7 +125,7 @@
|
||||
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
|
||||
//#define ImDrawCallback MyImDrawCallback
|
||||
|
||||
//---- Debug Tools: Macro to break in Debugger
|
||||
//---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase)
|
||||
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
|
||||
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||
//#define IM_DEBUG_BREAK __debugbreak()
|
||||
@@ -116,10 +133,10 @@
|
||||
//---- Debug Tools: Enable slower asserts
|
||||
//#define IMGUI_DEBUG_PARANOID
|
||||
|
||||
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
|
||||
//---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files)
|
||||
/*
|
||||
namespace ImGui
|
||||
{
|
||||
void MyFunction(const char* name, const MyMatrix44& v);
|
||||
void MyFunction(const char* name, MyMatrix44* mtx);
|
||||
}
|
||||
*/
|
||||
|
||||
Vendored
+4651
-2301
File diff suppressed because it is too large
Load Diff
Vendored
+1497
-902
File diff suppressed because it is too large
Load Diff
+3015
-663
File diff suppressed because it is too large
Load Diff
+621
-129
File diff suppressed because it is too large
Load Diff
+938
-658
File diff suppressed because it is too large
Load Diff
+584
-235
File diff suppressed because it is too large
Load Diff
+2536
-870
File diff suppressed because it is too large
Load Diff
+139
-107
@@ -2,8 +2,11 @@
|
||||
// This is a slightly modified version of stb_textedit.h 1.14.
|
||||
// Those changes would need to be pushed into nothings/stb:
|
||||
// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
|
||||
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000)
|
||||
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
|
||||
// - Added name to struct or it may be forward declared in our code.
|
||||
// - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925)
|
||||
// Grep for [DEAR IMGUI] to find the changes.
|
||||
// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
|
||||
|
||||
// stb_textedit.h - v1.14 - public domain - Sean Barrett
|
||||
// Development of this library was sponsored by RAD Game Tools
|
||||
@@ -30,7 +33,7 @@
|
||||
// DEPENDENCIES
|
||||
//
|
||||
// Uses the C runtime function 'memmove', which you can override
|
||||
// by defining STB_TEXTEDIT_memmove before the implementation.
|
||||
// by defining IMSTB_TEXTEDIT_memmove before the implementation.
|
||||
// Uses no other functions. Performs no runtime allocations.
|
||||
//
|
||||
//
|
||||
@@ -40,7 +43,7 @@
|
||||
// 1.13 (2019-02-07) fix bug in undo size management
|
||||
// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
|
||||
// 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield
|
||||
// 1.10 (2016-10-25) supress warnings about casting away const with -Wcast-qual
|
||||
// 1.10 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
|
||||
// 1.9 (2016-08-27) customizable move-by-word
|
||||
// 1.8 (2016-04-02) better keyboard handling when mouse button is down
|
||||
// 1.7 (2015-09-13) change y range handling in case baseline is non-0
|
||||
@@ -208,6 +211,7 @@
|
||||
// int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
// int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
|
||||
// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key)
|
||||
// void stb_textedit_text(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int text_len)
|
||||
//
|
||||
// Each of these functions potentially updates the string and updates the
|
||||
// state.
|
||||
@@ -242,7 +246,12 @@
|
||||
// various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit
|
||||
// set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is
|
||||
// clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to
|
||||
// anything other type you wante before including.
|
||||
// anything other type you want before including.
|
||||
// if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are
|
||||
// transformed into text and stb_textedit_text() is automatically called.
|
||||
//
|
||||
// text: [DEAR IMGUI] added 2024-09
|
||||
// call this to text inputs sent to the textfield.
|
||||
//
|
||||
//
|
||||
// When rendering, you can read the cursor position and selection state from
|
||||
@@ -274,8 +283,8 @@
|
||||
////
|
||||
////
|
||||
|
||||
#ifndef INCLUDE_STB_TEXTEDIT_H
|
||||
#define INCLUDE_STB_TEXTEDIT_H
|
||||
#ifndef INCLUDE_IMSTB_TEXTEDIT_H
|
||||
#define INCLUDE_IMSTB_TEXTEDIT_H
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -286,38 +295,38 @@
|
||||
// and undo state.
|
||||
//
|
||||
|
||||
#ifndef STB_TEXTEDIT_UNDOSTATECOUNT
|
||||
#define STB_TEXTEDIT_UNDOSTATECOUNT 99
|
||||
#ifndef IMSTB_TEXTEDIT_UNDOSTATECOUNT
|
||||
#define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99
|
||||
#endif
|
||||
#ifndef STB_TEXTEDIT_UNDOCHARCOUNT
|
||||
#define STB_TEXTEDIT_UNDOCHARCOUNT 999
|
||||
#ifndef IMSTB_TEXTEDIT_UNDOCHARCOUNT
|
||||
#define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999
|
||||
#endif
|
||||
#ifndef STB_TEXTEDIT_CHARTYPE
|
||||
#define STB_TEXTEDIT_CHARTYPE int
|
||||
#ifndef IMSTB_TEXTEDIT_CHARTYPE
|
||||
#define IMSTB_TEXTEDIT_CHARTYPE int
|
||||
#endif
|
||||
#ifndef STB_TEXTEDIT_POSITIONTYPE
|
||||
#define STB_TEXTEDIT_POSITIONTYPE int
|
||||
#ifndef IMSTB_TEXTEDIT_POSITIONTYPE
|
||||
#define IMSTB_TEXTEDIT_POSITIONTYPE int
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// private data
|
||||
STB_TEXTEDIT_POSITIONTYPE where;
|
||||
STB_TEXTEDIT_POSITIONTYPE insert_length;
|
||||
STB_TEXTEDIT_POSITIONTYPE delete_length;
|
||||
IMSTB_TEXTEDIT_POSITIONTYPE where;
|
||||
IMSTB_TEXTEDIT_POSITIONTYPE insert_length;
|
||||
IMSTB_TEXTEDIT_POSITIONTYPE delete_length;
|
||||
int char_storage;
|
||||
} StbUndoRecord;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// private data
|
||||
StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT];
|
||||
STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT];
|
||||
StbUndoRecord undo_rec [IMSTB_TEXTEDIT_UNDOSTATECOUNT];
|
||||
IMSTB_TEXTEDIT_CHARTYPE undo_char[IMSTB_TEXTEDIT_UNDOCHARCOUNT];
|
||||
short undo_point, redo_point;
|
||||
int undo_char_point, redo_char_point;
|
||||
} StbUndoState;
|
||||
|
||||
typedef struct
|
||||
typedef struct STB_TexteditState
|
||||
{
|
||||
/////////////////////
|
||||
//
|
||||
@@ -371,7 +380,7 @@ typedef struct
|
||||
float ymin,ymax; // height of row above and below baseline
|
||||
int num_chars;
|
||||
} StbTexteditRow;
|
||||
#endif //INCLUDE_STB_TEXTEDIT_H
|
||||
#endif //INCLUDE_IMSTB_TEXTEDIT_H
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
@@ -384,11 +393,11 @@ typedef struct
|
||||
|
||||
// implementation isn't include-guarded, since it might have indirectly
|
||||
// included just the "header" portion
|
||||
#ifdef STB_TEXTEDIT_IMPLEMENTATION
|
||||
#ifdef IMSTB_TEXTEDIT_IMPLEMENTATION
|
||||
|
||||
#ifndef STB_TEXTEDIT_memmove
|
||||
#ifndef IMSTB_TEXTEDIT_memmove
|
||||
#include <string.h>
|
||||
#define STB_TEXTEDIT_memmove memmove
|
||||
#define IMSTB_TEXTEDIT_memmove memmove
|
||||
#endif
|
||||
|
||||
|
||||
@@ -398,7 +407,7 @@ typedef struct
|
||||
//
|
||||
|
||||
// traverse the layout to locate the nearest character to a display position
|
||||
static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
|
||||
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
||||
{
|
||||
StbTexteditRow r;
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
@@ -437,13 +446,13 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
|
||||
if (x < r.x1) {
|
||||
// search characters in row for one that straddles 'x'
|
||||
prev_x = r.x0;
|
||||
for (k=0; k < r.num_chars; ++k) {
|
||||
for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) {
|
||||
float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
|
||||
if (x < prev_x+w) {
|
||||
if (x < prev_x+w/2)
|
||||
return k+i;
|
||||
else
|
||||
return k+i+1;
|
||||
return IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k);
|
||||
}
|
||||
prev_x += w;
|
||||
}
|
||||
@@ -458,7 +467,7 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
|
||||
}
|
||||
|
||||
// API click: on mouse down, move the cursor to the clicked location, and reset the selection
|
||||
static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
||||
static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
||||
{
|
||||
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
|
||||
// goes off the top or bottom of the text
|
||||
@@ -476,7 +485,7 @@ static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat
|
||||
}
|
||||
|
||||
// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
|
||||
static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
||||
static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
||||
{
|
||||
int p = 0;
|
||||
|
||||
@@ -502,11 +511,11 @@ static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state
|
||||
//
|
||||
|
||||
// forward declarations
|
||||
static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state);
|
||||
static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state);
|
||||
static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length);
|
||||
static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
|
||||
static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
|
||||
static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length);
|
||||
static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length);
|
||||
static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length);
|
||||
static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -518,7 +527,7 @@ typedef struct
|
||||
|
||||
// find the x/y location of a character, and remember info about the previous row in
|
||||
// case we get a move-up event (for page up, we'll have to rescan)
|
||||
static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line)
|
||||
static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING *str, int n, int single_line)
|
||||
{
|
||||
StbTexteditRow r;
|
||||
int prev_start = 0;
|
||||
@@ -549,7 +558,10 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
|
||||
i += r.num_chars;
|
||||
find->y += r.baseline_y_delta;
|
||||
if (i == z) // [DEAR IMGUI]
|
||||
{
|
||||
r.num_chars = 0; // [DEAR IMGUI]
|
||||
break; // [DEAR IMGUI]
|
||||
}
|
||||
}
|
||||
|
||||
find->first_char = first = i;
|
||||
@@ -559,14 +571,14 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
|
||||
|
||||
// now scan to find xpos
|
||||
find->x = r.x0;
|
||||
for (i=0; first+i < n; ++i)
|
||||
for (i=0; first+i < n; i = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, first + i) - first)
|
||||
find->x += STB_TEXTEDIT_GETWIDTH(str, first, i);
|
||||
}
|
||||
|
||||
#define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end)
|
||||
|
||||
// make the selection/cursor state valid if client altered the string
|
||||
static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
static void stb_textedit_clamp(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
{
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
if (STB_TEXT_HAS_SELECTION(state)) {
|
||||
@@ -580,7 +592,7 @@ static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat
|
||||
}
|
||||
|
||||
// delete characters while updating undo
|
||||
static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len)
|
||||
static void stb_textedit_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len)
|
||||
{
|
||||
stb_text_makeundo_delete(str, state, where, len);
|
||||
STB_TEXTEDIT_DELETECHARS(str, where, len);
|
||||
@@ -588,7 +600,7 @@ static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *sta
|
||||
}
|
||||
|
||||
// delete the section
|
||||
static void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
static void stb_textedit_delete_selection(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
{
|
||||
stb_textedit_clamp(str, state);
|
||||
if (STB_TEXT_HAS_SELECTION(state)) {
|
||||
@@ -625,7 +637,7 @@ static void stb_textedit_move_to_first(STB_TexteditState *state)
|
||||
}
|
||||
|
||||
// move cursor to last character of selection
|
||||
static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
{
|
||||
if (STB_TEXT_HAS_SELECTION(state)) {
|
||||
stb_textedit_sortselection(state);
|
||||
@@ -636,14 +648,25 @@ static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditStat
|
||||
}
|
||||
}
|
||||
|
||||
// [DEAR IMGUI]
|
||||
// Functions must be implemented for UTF8 support
|
||||
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
|
||||
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
|
||||
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
|
||||
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx) (idx - 1)
|
||||
#endif
|
||||
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
|
||||
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx) (idx + 1)
|
||||
#endif
|
||||
|
||||
#ifdef STB_TEXTEDIT_IS_SPACE
|
||||
static int is_word_boundary( STB_TEXTEDIT_STRING *str, int idx )
|
||||
static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
|
||||
{
|
||||
return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1;
|
||||
}
|
||||
|
||||
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
|
||||
static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c )
|
||||
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
|
||||
{
|
||||
--c; // always move at least one character
|
||||
while( c >= 0 && !is_word_boundary( str, c ) )
|
||||
@@ -658,7 +681,7 @@ static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c )
|
||||
#endif
|
||||
|
||||
#ifndef STB_TEXTEDIT_MOVEWORDRIGHT
|
||||
static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *str, int c )
|
||||
static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
|
||||
{
|
||||
const int len = STB_TEXTEDIT_STRINGLEN(str);
|
||||
++c; // always move at least one character
|
||||
@@ -685,7 +708,7 @@ static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state)
|
||||
}
|
||||
|
||||
// API cut: delete selection
|
||||
static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
static int stb_textedit_cut(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
{
|
||||
if (STB_TEXT_HAS_SELECTION(state)) {
|
||||
stb_textedit_delete_selection(str,state); // implicitly clamps
|
||||
@@ -696,7 +719,7 @@ static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
}
|
||||
|
||||
// API paste: replace existing selection with passed-in text
|
||||
static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
|
||||
static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE *text, int len)
|
||||
{
|
||||
// if there's a selection, the paste should delete it
|
||||
stb_textedit_clamp(str, state);
|
||||
@@ -716,36 +739,44 @@ static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditSta
|
||||
#define STB_TEXTEDIT_KEYTYPE int
|
||||
#endif
|
||||
|
||||
// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
|
||||
static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
|
||||
{
|
||||
// can't add newline in single-line mode
|
||||
if (text[0] == '\n' && state->single_line)
|
||||
return;
|
||||
|
||||
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
|
||||
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
|
||||
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
|
||||
state->cursor += text_len;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
stb_textedit_delete_selection(str, state); // implicitly clamps
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
|
||||
stb_text_makeundo_insert(state, state->cursor, text_len);
|
||||
state->cursor += text_len;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// API key: process a keyboard input
|
||||
static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
|
||||
static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
|
||||
{
|
||||
retry:
|
||||
switch (key) {
|
||||
default: {
|
||||
#ifdef STB_TEXTEDIT_KEYTOTEXT
|
||||
int c = STB_TEXTEDIT_KEYTOTEXT(key);
|
||||
if (c > 0) {
|
||||
STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c;
|
||||
|
||||
// can't add newline in single-line mode
|
||||
if (c == '\n' && state->single_line)
|
||||
break;
|
||||
|
||||
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
|
||||
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
|
||||
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
|
||||
++state->cursor;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
} else {
|
||||
stb_textedit_delete_selection(str,state); // implicitly clamps
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
|
||||
stb_text_makeundo_insert(state, state->cursor, 1);
|
||||
++state->cursor;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
}
|
||||
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
|
||||
stb_textedit_text(str, state, &ch, 1);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -771,7 +802,7 @@ retry:
|
||||
stb_textedit_move_to_first(state);
|
||||
else
|
||||
if (state->cursor > 0)
|
||||
--state->cursor;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
|
||||
@@ -780,7 +811,7 @@ retry:
|
||||
if (STB_TEXT_HAS_SELECTION(state))
|
||||
stb_textedit_move_to_last(str, state);
|
||||
else
|
||||
++state->cursor;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
stb_textedit_clamp(str, state);
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
@@ -790,7 +821,7 @@ retry:
|
||||
stb_textedit_prep_selection_at_cursor(state);
|
||||
// move selection left
|
||||
if (state->select_end > 0)
|
||||
--state->select_end;
|
||||
state->select_end = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->select_end);
|
||||
state->cursor = state->select_end;
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
@@ -840,7 +871,7 @@ retry:
|
||||
case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT:
|
||||
stb_textedit_prep_selection_at_cursor(state);
|
||||
// move selection right
|
||||
++state->select_end;
|
||||
state->select_end = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->select_end);
|
||||
stb_textedit_clamp(str, state);
|
||||
state->cursor = state->select_end;
|
||||
state->has_preferred_x = 0;
|
||||
@@ -889,14 +920,14 @@ retry:
|
||||
x = row.x0;
|
||||
for (i=0; i < row.num_chars; ++i) {
|
||||
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
|
||||
#ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
break;
|
||||
#endif
|
||||
x += dx;
|
||||
if (x > goal_x)
|
||||
break;
|
||||
++state->cursor;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
}
|
||||
stb_textedit_clamp(str, state);
|
||||
|
||||
@@ -951,14 +982,14 @@ retry:
|
||||
x = row.x0;
|
||||
for (i=0; i < row.num_chars; ++i) {
|
||||
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
|
||||
#ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
break;
|
||||
#endif
|
||||
x += dx;
|
||||
if (x > goal_x)
|
||||
break;
|
||||
++state->cursor;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
}
|
||||
stb_textedit_clamp(str, state);
|
||||
|
||||
@@ -986,7 +1017,7 @@ retry:
|
||||
else {
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
if (state->cursor < n)
|
||||
stb_textedit_delete(str, state, state->cursor, 1);
|
||||
stb_textedit_delete(str, state, state->cursor, IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor) - state->cursor);
|
||||
}
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
@@ -998,8 +1029,9 @@ retry:
|
||||
else {
|
||||
stb_textedit_clamp(str, state);
|
||||
if (state->cursor > 0) {
|
||||
stb_textedit_delete(str, state, state->cursor-1, 1);
|
||||
--state->cursor;
|
||||
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
|
||||
stb_textedit_delete(str, state, prev, state->cursor - prev);
|
||||
state->cursor = prev;
|
||||
}
|
||||
}
|
||||
state->has_preferred_x = 0;
|
||||
@@ -1109,8 +1141,8 @@ retry:
|
||||
|
||||
static void stb_textedit_flush_redo(StbUndoState *state)
|
||||
{
|
||||
state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT;
|
||||
state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT;
|
||||
state->redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
|
||||
state->redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
|
||||
}
|
||||
|
||||
// discard the oldest entry in the undo list
|
||||
@@ -1122,13 +1154,13 @@ static void stb_textedit_discard_undo(StbUndoState *state)
|
||||
int n = state->undo_rec[0].insert_length, i;
|
||||
// delete n characters from all other records
|
||||
state->undo_char_point -= n;
|
||||
STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE)));
|
||||
IMSTB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));
|
||||
for (i=0; i < state->undo_point; ++i)
|
||||
if (state->undo_rec[i].char_storage >= 0)
|
||||
state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it
|
||||
}
|
||||
--state->undo_point;
|
||||
STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));
|
||||
IMSTB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1138,7 +1170,7 @@ static void stb_textedit_discard_undo(StbUndoState *state)
|
||||
// fill up even though the undo buffer didn't
|
||||
static void stb_textedit_discard_redo(StbUndoState *state)
|
||||
{
|
||||
int k = STB_TEXTEDIT_UNDOSTATECOUNT-1;
|
||||
int k = IMSTB_TEXTEDIT_UNDOSTATECOUNT-1;
|
||||
|
||||
if (state->redo_point <= k) {
|
||||
// if the k'th undo state has characters, clean those up
|
||||
@@ -1146,7 +1178,7 @@ static void stb_textedit_discard_redo(StbUndoState *state)
|
||||
int n = state->undo_rec[k].insert_length, i;
|
||||
// move the remaining redo character data to the end of the buffer
|
||||
state->redo_char_point += n;
|
||||
STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE)));
|
||||
IMSTB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((IMSTB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));
|
||||
// adjust the position of all the other records to account for above memmove
|
||||
for (i=state->redo_point; i < k; ++i)
|
||||
if (state->undo_rec[i].char_storage >= 0)
|
||||
@@ -1154,12 +1186,12 @@ static void stb_textedit_discard_redo(StbUndoState *state)
|
||||
}
|
||||
// now move all the redo records towards the end of the buffer; the first one is at 'redo_point'
|
||||
// [DEAR IMGUI]
|
||||
size_t move_size = (size_t)((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));
|
||||
size_t move_size = (size_t)((IMSTB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));
|
||||
const char* buf_begin = (char*)state->undo_rec; (void)buf_begin;
|
||||
const char* buf_end = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end;
|
||||
IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin);
|
||||
IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end);
|
||||
STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size);
|
||||
IMSTB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size);
|
||||
|
||||
// now move redo_point to point to the new one
|
||||
++state->redo_point;
|
||||
@@ -1173,32 +1205,32 @@ static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numch
|
||||
|
||||
// if we have no free records, we have to make room, by sliding the
|
||||
// existing records down
|
||||
if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
if (state->undo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
stb_textedit_discard_undo(state);
|
||||
|
||||
// if the characters to store won't possibly fit in the buffer, we can't undo
|
||||
if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) {
|
||||
if (numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT) {
|
||||
state->undo_point = 0;
|
||||
state->undo_char_point = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// if we don't have enough free characters in the buffer, we have to make room
|
||||
while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT)
|
||||
while (state->undo_char_point + numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT)
|
||||
stb_textedit_discard_undo(state);
|
||||
|
||||
return &state->undo_rec[state->undo_point++];
|
||||
}
|
||||
|
||||
static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len)
|
||||
static IMSTB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len)
|
||||
{
|
||||
StbUndoRecord *r = stb_text_create_undo_record(state, insert_len);
|
||||
if (r == NULL)
|
||||
return NULL;
|
||||
|
||||
r->where = pos;
|
||||
r->insert_length = (STB_TEXTEDIT_POSITIONTYPE) insert_len;
|
||||
r->delete_length = (STB_TEXTEDIT_POSITIONTYPE) delete_len;
|
||||
r->insert_length = (IMSTB_TEXTEDIT_POSITIONTYPE) insert_len;
|
||||
r->delete_length = (IMSTB_TEXTEDIT_POSITIONTYPE) delete_len;
|
||||
|
||||
if (insert_len == 0) {
|
||||
r->char_storage = -1;
|
||||
@@ -1210,7 +1242,7 @@ static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos,
|
||||
}
|
||||
}
|
||||
|
||||
static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
{
|
||||
StbUndoState *s = &state->undostate;
|
||||
StbUndoRecord u, *r;
|
||||
@@ -1237,7 +1269,7 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
// characters stored for *undoing* don't leave room for redo
|
||||
// if the last is true, we have to bail
|
||||
|
||||
if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) {
|
||||
if (s->undo_char_point + u.delete_length >= IMSTB_TEXTEDIT_UNDOCHARCOUNT) {
|
||||
// the undo records take up too much character space; there's no space to store the redo characters
|
||||
r->insert_length = 0;
|
||||
} else {
|
||||
@@ -1246,7 +1278,7 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
// there's definitely room to store the characters eventually
|
||||
while (s->undo_char_point + u.delete_length > s->redo_char_point) {
|
||||
// should never happen:
|
||||
if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
return;
|
||||
// there's currently not enough room, so discard a redo record
|
||||
stb_textedit_discard_redo(s);
|
||||
@@ -1278,11 +1310,11 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
s->redo_point--;
|
||||
}
|
||||
|
||||
static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
{
|
||||
StbUndoState *s = &state->undostate;
|
||||
StbUndoRecord *u, r;
|
||||
if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
return;
|
||||
|
||||
// we need to do two things: apply the redo record, and create an undo record
|
||||
@@ -1334,20 +1366,20 @@ static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int le
|
||||
stb_text_createundo(&state->undostate, where, 0, length);
|
||||
}
|
||||
|
||||
static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length)
|
||||
static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length)
|
||||
{
|
||||
int i;
|
||||
STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);
|
||||
IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);
|
||||
if (p) {
|
||||
for (i=0; i < length; ++i)
|
||||
p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
|
||||
}
|
||||
}
|
||||
|
||||
static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length)
|
||||
static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length)
|
||||
{
|
||||
int i;
|
||||
STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);
|
||||
IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);
|
||||
if (p) {
|
||||
for (i=0; i < old_length; ++i)
|
||||
p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
|
||||
@@ -1359,8 +1391,8 @@ static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_lin
|
||||
{
|
||||
state->undostate.undo_point = 0;
|
||||
state->undostate.undo_char_point = 0;
|
||||
state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT;
|
||||
state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT;
|
||||
state->undostate.redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
|
||||
state->undostate.redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
|
||||
state->select_end = state->select_start = 0;
|
||||
state->cursor = 0;
|
||||
state->has_preferred_x = 0;
|
||||
@@ -1383,16 +1415,16 @@ static void stb_textedit_initialize_state(STB_TexteditState *state, int is_singl
|
||||
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||
#endif
|
||||
|
||||
static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len)
|
||||
static int stb_textedit_paste(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE const *ctext, int len)
|
||||
{
|
||||
return stb_textedit_paste_internal(str, state, (STB_TEXTEDIT_CHARTYPE *) ctext, len);
|
||||
return stb_textedit_paste_internal(str, state, (IMSTB_TEXTEDIT_CHARTYPE *) ctext, len);
|
||||
}
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif//STB_TEXTEDIT_IMPLEMENTATION
|
||||
#endif//IMSTB_TEXTEDIT_IMPLEMENTATION
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
+1
-1
@@ -656,7 +656,7 @@ STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h
|
||||
STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip);
|
||||
// If skip != 0, this tells stb_truetype to skip any codepoints for which
|
||||
// there is no corresponding glyph. If skip=0, which is the default, then
|
||||
// codepoints without a glyph recived the font's "missing character" glyph,
|
||||
// codepoints without a glyph received the font's "missing character" glyph,
|
||||
// typically an empty box by convention.
|
||||
|
||||
STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above
|
||||
|
||||
Vendored
Vendored
+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;
|
||||
|
||||
@@ -100,9 +100,25 @@ namespace games::iidx {
|
||||
*phkResult = DEVICE_ASIO_REG_HANDLE;
|
||||
|
||||
log_info("iidx::asio", "replacing '{}' with '{}'", lpSubKey, ASIO_DRIVER.value());
|
||||
|
||||
return RegOpenKeyExA_orig(real_asio_reg_handle, ASIO_DRIVER.value().c_str(), ulOptions, samDesired,
|
||||
const auto result = RegOpenKeyExA_orig(
|
||||
real_asio_reg_handle,
|
||||
ASIO_DRIVER.value().c_str(),
|
||||
ulOptions,
|
||||
samDesired,
|
||||
&real_asio_device_reg_handle);
|
||||
|
||||
if (result != ERROR_SUCCESS) {
|
||||
log_warning(
|
||||
"iidx::asio",
|
||||
"failed to open registry subkey '{}', error=0x{:x}",
|
||||
ASIO_DRIVER.value().c_str(), result);
|
||||
log_warning(
|
||||
"iidx::asio",
|
||||
"due to improper ASIO setting, game will likely fail to launch; double check -iidxasio and the registry",
|
||||
ASIO_DRIVER.value().c_str(), result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+20
-12
@@ -35,6 +35,7 @@
|
||||
#include "bc/io.h"
|
||||
#include "ccj/io.h"
|
||||
#include "qks/io.h"
|
||||
#include "mfg/io.h"
|
||||
|
||||
namespace games {
|
||||
|
||||
@@ -305,6 +306,13 @@ namespace games {
|
||||
buttons.insert({ qks, qks::get_buttons() });
|
||||
buttons_help.insert({ qks, qks::get_buttons_help() });
|
||||
file_hints[qks].emplace_back("game/uks.exe");
|
||||
|
||||
// Mahjong Fight Girl
|
||||
const std::string mfg("Mahjong Fight Girl");
|
||||
games.push_back(mfg);
|
||||
buttons.insert({ mfg, mfg::get_buttons() });
|
||||
buttons_help.insert({ mfg, mfg::get_buttons_help() });
|
||||
file_hints[mfg].emplace_back("game/MFGClient_Data");
|
||||
}
|
||||
|
||||
const std::vector<std::string> &get_games() {
|
||||
@@ -437,22 +445,22 @@ namespace games {
|
||||
vkey_defaults.push_back(VK_F12);
|
||||
names.emplace_back("Toggle Camera Control");
|
||||
vkey_defaults.push_back(0xFF);
|
||||
names.emplace_back("Player 1 PIN Macro");
|
||||
vkey_defaults.push_back(0xFF);
|
||||
names.emplace_back("Player 2 PIN Macro");
|
||||
vkey_defaults.push_back(0xFF);
|
||||
names.emplace_back("Screen Resize");
|
||||
vkey_defaults.push_back(0xFF);
|
||||
names.emplace_back("Screen Resize Scene 1");
|
||||
vkey_defaults.push_back(0xFF);
|
||||
names.emplace_back("Screen Resize Scene 2");
|
||||
vkey_defaults.push_back(0xFF);
|
||||
names.emplace_back("Screen Resize Scene 3");
|
||||
vkey_defaults.push_back(0xFF);
|
||||
names.emplace_back("Screen Resize Scene 4");
|
||||
vkey_defaults.push_back(0xFF);
|
||||
names.emplace_back("Force Exit Game");
|
||||
vkey_defaults.push_back(0xFF);
|
||||
names.emplace_back("Navigator Activate");
|
||||
vkey_defaults.push_back(0xFF);
|
||||
names.emplace_back("Navigator Cancel");
|
||||
vkey_defaults.push_back(0xFF);
|
||||
names.emplace_back("Navigator Up");
|
||||
vkey_defaults.push_back(0xFF);
|
||||
names.emplace_back("Navigator Down");
|
||||
vkey_defaults.push_back(0xFF);
|
||||
names.emplace_back("Navigator Left");
|
||||
vkey_defaults.push_back(0xFF);
|
||||
names.emplace_back("Navigator Right");
|
||||
vkey_defaults.push_back(0xFF);
|
||||
names.emplace_back("Hotkey Enable 1");
|
||||
vkey_defaults.push_back(0xFF);
|
||||
names.emplace_back("Hotkey Enable 2");
|
||||
|
||||
@@ -22,14 +22,14 @@ namespace games {
|
||||
ToggleScreenResize,
|
||||
ToggleOverlay,
|
||||
ToggleCameraControl,
|
||||
TriggerPinMacroP1,
|
||||
TriggerPinMacroP2,
|
||||
ScreenResize,
|
||||
ScreenResizeScene1,
|
||||
ScreenResizeScene2,
|
||||
ScreenResizeScene3,
|
||||
ScreenResizeScene4,
|
||||
SuperExit,
|
||||
NavigatorActivate,
|
||||
NavigatorCancel,
|
||||
NavigatorUp,
|
||||
NavigatorDown,
|
||||
NavigatorLeft,
|
||||
NavigatorRight,
|
||||
HotkeyEnable1,
|
||||
HotkeyEnable2,
|
||||
HotkeyToggle,
|
||||
|
||||
@@ -0,0 +1,385 @@
|
||||
#include "bi2a_hook.h"
|
||||
|
||||
#include "util/execexe.h"
|
||||
#include "util/logging.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "games/io.h"
|
||||
#include "io.h"
|
||||
#include "util/tapeled.h"
|
||||
|
||||
namespace games::mfg {
|
||||
|
||||
/*
|
||||
* class definitions
|
||||
*/
|
||||
|
||||
struct AIO_SCI_COMM {
|
||||
};
|
||||
|
||||
struct AIO_NMGR_IOB {
|
||||
};
|
||||
|
||||
struct AIO_IOB_BI2A_VFG {
|
||||
};
|
||||
|
||||
struct AIO_IOB_BI2A_VFG__INPUTDATA {
|
||||
uint8_t data[16];
|
||||
};
|
||||
|
||||
struct AIO_IOB_BI2A_VFG__OUTPUTDATA {
|
||||
uint8_t data[40];
|
||||
};
|
||||
|
||||
struct AIO_IOB_BI2A_VFG__INPUT
|
||||
{
|
||||
uint8_t bPcPowerCheck;
|
||||
uint8_t CoinCount;
|
||||
uint8_t bTest;
|
||||
uint8_t bService;
|
||||
uint8_t bCoinSw;
|
||||
uint8_t bCoinJam;
|
||||
uint8_t bCabinet;
|
||||
uint8_t bHPDetect;
|
||||
uint8_t bRecDetect;
|
||||
uint8_t bStart;
|
||||
};
|
||||
|
||||
struct AIO_IOB_BI2A_VFG__DEVSTATUS
|
||||
{
|
||||
uint8_t MechType;
|
||||
uint8_t bIsBi2a;
|
||||
uint8_t IoCounter;
|
||||
AIO_IOB_BI2A_VFG__INPUT Input;
|
||||
AIO_IOB_BI2A_VFG__INPUTDATA InputData;
|
||||
AIO_IOB_BI2A_VFG__OUTPUTDATA OutputData;
|
||||
};
|
||||
|
||||
/*
|
||||
* typedefs
|
||||
*/
|
||||
|
||||
// libaio-iob_video.dll
|
||||
typedef AIO_SCI_COMM* (__fastcall *aioIobBi2a_OpenSciUsbCdc_t)(uint32_t i_SerialNumber);
|
||||
typedef AIO_IOB_BI2A_VFG* (__fastcall *aioIobBi2aVFG_Create_t)(AIO_NMGR_IOB *i_pNodeMgr, uint32_t i_DevId);
|
||||
typedef void (__fastcall *aioIobBi2aVFG_SetMechType_t)(AIO_IOB_BI2A_VFG *i_pNodeCtl, uint32_t i_MechType);
|
||||
typedef void (__fastcall *aioIobBi2aVFG_GetDeviceStatus_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl,
|
||||
AIO_IOB_BI2A_VFG__DEVSTATUS *o_pDevStatus,
|
||||
uint32_t i_cbDevStatus);
|
||||
typedef void (__fastcall *aioIobBi2aVFG_SetWatchDogTimer_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint16_t i_Count);
|
||||
typedef void (__fastcall *aioIobBi2aVFG_ControlCoinBlocker_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Slot,
|
||||
bool i_bOpen);
|
||||
typedef void (__fastcall *aioIobBi2aVFG_AddCounter_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Counter,
|
||||
uint32_t i_Count);
|
||||
typedef void (__fastcall *aioIobBi2aVFG_SetAmpVolume_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Amp,
|
||||
uint32_t i_Volume);
|
||||
typedef void (__fastcall *aioIobBi2aVFG_EnableUsbCharger_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, bool i_bEnable);
|
||||
typedef void (__fastcall *aioIobBi2aVFG_SetLamp_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Lamp, uint8_t i_Bright);
|
||||
typedef void (__fastcall *aioIobBi2aVFG_SetIccrLed_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_RGB);
|
||||
typedef void (__fastcall *aioIobBi2aVFG_SetLedData_t)(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint8_t *i_pData,
|
||||
uint32_t i_cbData);
|
||||
|
||||
// libaio-iob.dll
|
||||
typedef AIO_NMGR_IOB* (__fastcall *aioNMgrIob_Create_t)(AIO_SCI_COMM* i_pSci, uint32_t i_bfMode);
|
||||
|
||||
// libaio.dll
|
||||
typedef void (__fastcall *aioNodeMgr_Destroy_t)(AIO_NMGR_IOB *i_pNodeMgr);
|
||||
typedef int32_t (__fastcall *aioNodeMgr_GetState_t)(AIO_NMGR_IOB *i_pNodeMgr);
|
||||
typedef bool (__fastcall *aioNodeMgr_IsReady_t)(AIO_NMGR_IOB *i_pNodeMgr, int32_t i_State);
|
||||
typedef bool (__fastcall *aioNodeMgr_IsError_t)(AIO_NMGR_IOB *i_pNodeMgr, int32_t i_State);
|
||||
typedef void (__fastcall *aioNodeCtl_Destroy_t)(AIO_IOB_BI2A_VFG *i_pNodeCtl);
|
||||
typedef int32_t (__fastcall *aioNodeCtl_GetState_t)(AIO_IOB_BI2A_VFG *i_pNodeCtl);
|
||||
typedef bool (__fastcall *aioNodeCtl_IsReady_t)(AIO_IOB_BI2A_VFG *i_pNodeCtl, int32_t i_State);
|
||||
typedef bool (__fastcall *aioNodeCtl_IsError_t)(AIO_IOB_BI2A_VFG *i_pNodeCtl, int32_t i_State);
|
||||
|
||||
/*
|
||||
* function pointers
|
||||
*/
|
||||
|
||||
// libaio-iob_video.dll
|
||||
static aioIobBi2a_OpenSciUsbCdc_t aioIobBi2a_OpenSciUsbCdc_orig = nullptr;
|
||||
static aioIobBi2aVFG_Create_t aioIobBi2aVFG_Create_orig = nullptr;
|
||||
static aioIobBi2aVFG_SetMechType_t aioIobBi2aVFG_SetMechType_orig = nullptr;
|
||||
static aioIobBi2aVFG_GetDeviceStatus_t aioIobBi2aVFG_GetDeviceStatus_orig = nullptr;
|
||||
static aioIobBi2aVFG_SetWatchDogTimer_t aioIobBi2aVFG_SetWatchDogTimer_orig = nullptr;
|
||||
static aioIobBi2aVFG_ControlCoinBlocker_t aioIobBi2aVFG_ControlCoinBlocker_orig = nullptr;
|
||||
static aioIobBi2aVFG_AddCounter_t aioIobBi2aVFG_AddCounter_orig = nullptr;
|
||||
static aioIobBi2aVFG_SetAmpVolume_t aioIobBi2aVFG_SetAmpVolume_orig = nullptr;
|
||||
static aioIobBi2aVFG_EnableUsbCharger_t aioIobBi2aVFG_EnableUsbCharger_orig = nullptr;
|
||||
static aioIobBi2aVFG_SetLamp_t aioIobBi2aVFG_SetLamp_orig = nullptr;
|
||||
static aioIobBi2aVFG_SetIccrLed_t aioIobBi2aVFG_SetIccrLed_orig = nullptr;
|
||||
static aioIobBi2aVFG_SetLedData_t aioIobBi2aVFG_SetLedData_orig = nullptr;
|
||||
|
||||
// libaio-iob.dll
|
||||
static aioNMgrIob_Create_t aioNMgrIob_Create_orig = nullptr;
|
||||
|
||||
// libaio.dll
|
||||
static aioNodeMgr_Destroy_t aioNodeMgr_Destroy_orig = nullptr;
|
||||
static aioNodeMgr_GetState_t aioNodeMgr_GetState_orig = nullptr;
|
||||
static aioNodeMgr_IsReady_t aioNodeMgr_IsReady_orig = nullptr;
|
||||
static aioNodeMgr_IsError_t aioNodeMgr_IsError_orig = nullptr;
|
||||
static aioNodeCtl_Destroy_t aioNodeCtl_Destroy_orig = nullptr;
|
||||
static aioNodeCtl_GetState_t aioNodeCtl_GetState_orig = nullptr;
|
||||
static aioNodeCtl_IsReady_t aioNodeCtl_IsReady_orig = nullptr;
|
||||
static aioNodeCtl_IsError_t aioNodeCtl_IsError_orig = nullptr;
|
||||
|
||||
/*
|
||||
* variables
|
||||
*/
|
||||
|
||||
static AIO_SCI_COMM *aioSciComm;
|
||||
static AIO_NMGR_IOB *aioNmgrIob;
|
||||
static AIO_IOB_BI2A_VFG *aioIobBi2aVfg;
|
||||
static uint8_t mechType = 0;
|
||||
static uint8_t count = 0;
|
||||
|
||||
/*
|
||||
* implementations
|
||||
*/
|
||||
|
||||
static AIO_SCI_COMM* __fastcall aioIob2Bi2a_OpenSciUsbCdc(uint32_t i_SerialNumber) {
|
||||
log_info("bi2a_hook", "aioIob2Bi2a_OpenSciUsbCdc hook hit");
|
||||
aioSciComm = new AIO_SCI_COMM;
|
||||
return aioSciComm;
|
||||
}
|
||||
|
||||
static AIO_IOB_BI2A_VFG* __fastcall aioIobBi2aVFG_Create(AIO_NMGR_IOB *i_pNodeMgr, uint32_t i_DevId) {
|
||||
if (i_pNodeMgr == aioNmgrIob) {
|
||||
log_info("bi2a_hook", "node created");
|
||||
aioIobBi2aVfg = new AIO_IOB_BI2A_VFG;
|
||||
return aioIobBi2aVfg;
|
||||
} else {
|
||||
return aioIobBi2aVFG_Create_orig(i_pNodeMgr, i_DevId);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIobBi2aVFG_SetMechType(AIO_IOB_BI2A_VFG *i_pNodeCtl, uint32_t i_MechType) {
|
||||
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||
mechType = i_MechType;
|
||||
} else {
|
||||
return aioIobBi2aVFG_SetMechType_orig(i_pNodeCtl, i_MechType);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIobBi2aVFG_GetDeviceStatus(AIO_IOB_BI2A_VFG* i_pNodeCtl,
|
||||
AIO_IOB_BI2A_VFG__DEVSTATUS *o_pDevStatus,
|
||||
uint32_t i_cbDevStatus) {
|
||||
|
||||
RI_MGR->devices_flush_output();
|
||||
|
||||
if (i_pNodeCtl != aioIobBi2aVfg) {
|
||||
return aioIobBi2aVFG_GetDeviceStatus_orig(i_pNodeCtl, o_pDevStatus, i_cbDevStatus);
|
||||
}
|
||||
|
||||
memset(o_pDevStatus, 0x00, sizeof(AIO_IOB_BI2A_VFG__DEVSTATUS));
|
||||
|
||||
o_pDevStatus->MechType = mechType;
|
||||
o_pDevStatus->bIsBi2a = 1;
|
||||
o_pDevStatus->IoCounter = count++;
|
||||
|
||||
auto &buttons = get_buttons();
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Test]))
|
||||
o_pDevStatus->Input.bTest = 1;
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::Service]))
|
||||
o_pDevStatus->Input.bService = 1;
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::CoinMech]))
|
||||
o_pDevStatus->Input.bCoinSw = 1;
|
||||
|
||||
o_pDevStatus->Input.CoinCount = eamuse_coin_get_stock();
|
||||
}
|
||||
|
||||
void __fastcall aioIobBi2aVFG_SetWatchDogTimer(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint16_t i_Count) {
|
||||
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||
} else {
|
||||
return aioIobBi2aVFG_SetWatchDogTimer_orig(i_pNodeCtl, i_Count);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIobBi2aVFG_ControlCoinBlocker(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Slot, bool i_bOpen) {
|
||||
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||
} else {
|
||||
return aioIobBi2aVFG_ControlCoinBlocker_orig(i_pNodeCtl, i_Slot, i_bOpen);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIobBi2aVFG_AddCounter(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Counter, uint32_t i_Count) {
|
||||
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||
} else {
|
||||
return aioIobBi2aVFG_AddCounter_orig(i_pNodeCtl, i_Counter, i_Count);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIobBi2aVFG_SetAmpVolume(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Amp, uint32_t i_Volume) {
|
||||
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||
} else {
|
||||
return aioIobBi2aVFG_SetAmpVolume_orig(i_pNodeCtl, i_Amp, i_Volume);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIobBi2aVFG_EnableUsbCharger(AIO_IOB_BI2A_VFG* i_pNodeCtl, bool i_bEnable) {
|
||||
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||
} else {
|
||||
return aioIobBi2aVFG_EnableUsbCharger_orig(i_pNodeCtl, i_bEnable);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIobBi2aVFG_SetLamp(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_Lamp, uint8_t i_Bright) {
|
||||
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||
} else {
|
||||
return aioIobBi2aVFG_SetLamp_orig(i_pNodeCtl, i_Lamp, i_Bright);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIobBi2aVFG_SetIccrLed(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint32_t i_RGB) {
|
||||
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||
} else {
|
||||
return aioIobBi2aVFG_SetIccrLed_orig(i_pNodeCtl, i_RGB);
|
||||
}
|
||||
}
|
||||
|
||||
void __fastcall aioIobBi2aVFG_SetLedData(AIO_IOB_BI2A_VFG* i_pNodeCtl, uint8_t *i_pData, uint32_t i_cbData) {
|
||||
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||
} else {
|
||||
return aioIobBi2aVFG_SetLedData_orig(i_pNodeCtl, i_pData, i_cbData);
|
||||
}
|
||||
}
|
||||
|
||||
static AIO_NMGR_IOB* __fastcall aioNMgrIob_Create(AIO_SCI_COMM *i_pSci, uint32_t i_bfMode) {
|
||||
if (i_pSci == aioSciComm) {
|
||||
aioNmgrIob = new AIO_NMGR_IOB;
|
||||
return aioNmgrIob;
|
||||
} else {
|
||||
return aioNMgrIob_Create_orig(i_pSci, i_bfMode);
|
||||
}
|
||||
}
|
||||
|
||||
static void __fastcall aioNodeMgr_Destroy(AIO_NMGR_IOB *i_pNodeMgr) {
|
||||
if (i_pNodeMgr == aioNmgrIob) {
|
||||
delete aioNmgrIob;
|
||||
aioNmgrIob = nullptr;
|
||||
} else {
|
||||
return aioNodeMgr_Destroy_orig(i_pNodeMgr);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t __fastcall aioNodeMgr_GetState(AIO_NMGR_IOB *i_pNodeMgr) {
|
||||
if (i_pNodeMgr == aioNmgrIob) {
|
||||
return 1;
|
||||
} else {
|
||||
return aioNodeMgr_GetState_orig(i_pNodeMgr);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __fastcall aioNodeMgr_IsReady(AIO_NMGR_IOB *i_pNodeMgr, int32_t i_State) {
|
||||
if (i_pNodeMgr == aioNmgrIob) {
|
||||
return true;
|
||||
} else {
|
||||
return aioNodeMgr_IsReady_orig(i_pNodeMgr, i_State);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __fastcall aioNodeMgr_IsError(AIO_NMGR_IOB *i_pNodeMgr, int32_t i_State) {
|
||||
if (i_pNodeMgr == aioNmgrIob) {
|
||||
return false;
|
||||
} else {
|
||||
return aioNodeMgr_IsError_orig(i_pNodeMgr, i_State);
|
||||
}
|
||||
}
|
||||
|
||||
static void __fastcall aioNodeCtl_Destroy(AIO_IOB_BI2A_VFG *i_pNodeCtl) {
|
||||
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||
delete aioIobBi2aVfg;
|
||||
aioIobBi2aVfg = nullptr;
|
||||
} else {
|
||||
return aioNodeCtl_Destroy_orig(i_pNodeCtl);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t __fastcall aioNodeCtl_GetState(AIO_IOB_BI2A_VFG *i_pNodeCtl) {
|
||||
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||
return 1;
|
||||
} else {
|
||||
return aioNodeCtl_GetState_orig(i_pNodeCtl);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __fastcall aioNodeCtl_IsReady(AIO_IOB_BI2A_VFG *i_pNodeCtl, int32_t i_State) {
|
||||
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||
return true;
|
||||
} else {
|
||||
return aioNodeCtl_IsReady_orig(i_pNodeCtl, i_State);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __fastcall aioNodeCtl_IsError(AIO_IOB_BI2A_VFG *i_pNodeCtl, int32_t i_State) {
|
||||
if (i_pNodeCtl == aioIobBi2aVfg) {
|
||||
return false;
|
||||
} else {
|
||||
return aioNodeCtl_IsError_orig(i_pNodeCtl, i_State);
|
||||
}
|
||||
}
|
||||
|
||||
void bi2a_hook_init() {
|
||||
// avoid double init
|
||||
static bool initialized = false;
|
||||
if (initialized) {
|
||||
return;
|
||||
} else {
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
// announce
|
||||
log_info("bi2a_hook", "init");
|
||||
|
||||
// libaio-iob_video.dll
|
||||
const auto libaioIobVideoDll = "libaio-iob_video.dll";
|
||||
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2a_OpenSciUsbCdc",
|
||||
aioIob2Bi2a_OpenSciUsbCdc, &aioIobBi2a_OpenSciUsbCdc_orig);
|
||||
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_Create",
|
||||
aioIobBi2aVFG_Create, &aioIobBi2aVFG_Create_orig);
|
||||
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_SetMechType",
|
||||
aioIobBi2aVFG_SetMechType, &aioIobBi2aVFG_SetMechType_orig);
|
||||
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_GetDeviceStatus",
|
||||
aioIobBi2aVFG_GetDeviceStatus, &aioIobBi2aVFG_GetDeviceStatus_orig);
|
||||
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_SetWatchDogTimer",
|
||||
aioIobBi2aVFG_SetWatchDogTimer, &aioIobBi2aVFG_SetWatchDogTimer_orig);
|
||||
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_ControlCoinBlocker",
|
||||
aioIobBi2aVFG_ControlCoinBlocker, &aioIobBi2aVFG_ControlCoinBlocker_orig);
|
||||
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_AddCounter",
|
||||
aioIobBi2aVFG_AddCounter, &aioIobBi2aVFG_AddCounter_orig);
|
||||
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_SetAmpVolume",
|
||||
aioIobBi2aVFG_SetAmpVolume, &aioIobBi2aVFG_SetAmpVolume_orig);
|
||||
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_EnableUsbCharger",
|
||||
aioIobBi2aVFG_EnableUsbCharger, &aioIobBi2aVFG_EnableUsbCharger_orig);
|
||||
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_SetLamp",
|
||||
aioIobBi2aVFG_SetLamp, &aioIobBi2aVFG_SetLamp_orig);
|
||||
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_SetIccrLed",
|
||||
aioIobBi2aVFG_SetIccrLed, &aioIobBi2aVFG_SetIccrLed_orig);
|
||||
execexe::trampoline_try(libaioIobVideoDll, "aioIobBi2aVFG_SetLedData",
|
||||
aioIobBi2aVFG_SetLedData, &aioIobBi2aVFG_SetLedData_orig);
|
||||
|
||||
// libaio-iob.dll
|
||||
const auto libaioIobDll = "libaio-iob.dll";
|
||||
execexe::trampoline_try(libaioIobDll, "aioNMgrIob_Create",
|
||||
aioNMgrIob_Create, &aioNMgrIob_Create_orig);
|
||||
|
||||
// libaio.dll
|
||||
const auto libaioDll = "libaio.dll";
|
||||
execexe::trampoline_try(libaioDll, "aioNodeMgr_Destroy",
|
||||
aioNodeMgr_Destroy, &aioNodeMgr_Destroy_orig);
|
||||
execexe::trampoline_try(libaioDll, "aioNodeMgr_GetState",
|
||||
aioNodeMgr_GetState, &aioNodeMgr_GetState_orig);
|
||||
execexe::trampoline_try(libaioDll, "aioNodeMgr_IsReady",
|
||||
aioNodeMgr_IsReady, &aioNodeMgr_IsReady_orig);
|
||||
execexe::trampoline_try(libaioDll, "aioNodeMgr_IsError",
|
||||
aioNodeMgr_IsError, &aioNodeMgr_IsError_orig);
|
||||
execexe::trampoline_try(libaioDll, "aioNodeCtl_Destroy",
|
||||
aioNodeCtl_Destroy, &aioNodeCtl_Destroy_orig);
|
||||
execexe::trampoline_try(libaioDll, "aioNodeCtl_GetState",
|
||||
aioNodeCtl_GetState, &aioNodeCtl_GetState_orig);
|
||||
execexe::trampoline_try(libaioDll, "aioNodeCtl_IsReady",
|
||||
aioNodeCtl_IsReady, &aioNodeCtl_IsReady_orig);
|
||||
execexe::trampoline_try(libaioDll, "aioNodeCtl_IsError",
|
||||
aioNodeCtl_IsError, &aioNodeCtl_IsError_orig);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace games::mfg {
|
||||
void bi2a_hook_init();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "io.h"
|
||||
|
||||
std::vector<Button> &games::mfg::get_buttons() {
|
||||
static std::vector<Button> buttons;
|
||||
|
||||
if (buttons.empty()) {
|
||||
buttons = GameAPI::Buttons::getButtons("Mahjong Fight Girl");
|
||||
|
||||
GameAPI::Buttons::sortButtons(
|
||||
&buttons,
|
||||
"Service",
|
||||
"Test",
|
||||
"Coin Mech"
|
||||
);
|
||||
}
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
std::string games::mfg::get_buttons_help() {
|
||||
return "";
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "cfg/api.h"
|
||||
|
||||
namespace games::mfg {
|
||||
namespace Buttons {
|
||||
enum {
|
||||
Service,
|
||||
Test,
|
||||
CoinMech
|
||||
};
|
||||
}
|
||||
|
||||
// getters
|
||||
std::vector<Button> &get_buttons();
|
||||
std::string get_buttons_help();
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "mfg.h"
|
||||
|
||||
#include <format>
|
||||
#include "util/fileutils.h"
|
||||
#include "util/unity_player.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/execexe.h"
|
||||
#include "acioemu/handle.h"
|
||||
#include "misc/wintouchemu.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "bi2a_hook.h"
|
||||
|
||||
namespace games::mfg {
|
||||
std::string MFG_INJECT_ARGS = "";
|
||||
std::string MFG_CABINET_TYPE = "HG";
|
||||
bool MFG_NO_IO = false;
|
||||
|
||||
static acioemu::ACIOHandle *acioHandle = nullptr;
|
||||
static std::wstring portName;
|
||||
|
||||
void MFGGame::attach() {
|
||||
Game::attach();
|
||||
|
||||
// create required files
|
||||
fileutils::dir_create_recursive("dev/raw/log");
|
||||
fileutils::bin_write("dev/raw/log/output_log.txt", nullptr, 0);
|
||||
|
||||
SetEnvironmentVariableA("VFG_CABINET_TYPE", MFG_CABINET_TYPE.c_str());
|
||||
|
||||
// add card reader
|
||||
portName = MFG_CABINET_TYPE == "UKS" ? L"\\\\.\\COM1" : L"\\\\.\\COM3";
|
||||
acioHandle = new acioemu::ACIOHandle(portName.c_str(), 1);
|
||||
devicehook_init_trampoline();
|
||||
devicehook_add(acioHandle);
|
||||
|
||||
execexe::init();
|
||||
execexe::init_port_hook(portName, acioHandle);
|
||||
|
||||
if (GRAPHICS_SHOW_CURSOR) {
|
||||
unity_utils::force_show_cursor(true);
|
||||
}
|
||||
|
||||
unity_utils::set_args(
|
||||
std::format("{} {}{}",
|
||||
GetCommandLineA(),
|
||||
MFG_INJECT_ARGS,
|
||||
unity_utils::get_unity_player_args()));
|
||||
}
|
||||
|
||||
void MFGGame::post_attach() {
|
||||
Game::post_attach();
|
||||
|
||||
execexe::load_library("libaio.dll");
|
||||
execexe::load_library("libaio-iob.dll");
|
||||
execexe::load_library("libaio-iob_video.dll");
|
||||
execexe::load_library("libaio-iob2_video.dll");
|
||||
execexe::load_library("win10actlog.dll");
|
||||
|
||||
if (!MFG_NO_IO) {
|
||||
// insert BI2* hooks
|
||||
if (MFG_CABINET_TYPE == "UKS") {
|
||||
log_fatal("mfg", "UKS io is not supported");
|
||||
} else {
|
||||
bi2a_hook_init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MFGGame::detach() {
|
||||
Game::detach();
|
||||
|
||||
devicehook_dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "games/game.h"
|
||||
|
||||
namespace games::mfg {
|
||||
extern std::string MFG_INJECT_ARGS;
|
||||
extern std::string MFG_CABINET_TYPE;
|
||||
extern bool MFG_NO_IO;
|
||||
|
||||
class MFGGame : public games::Game {
|
||||
public:
|
||||
MFGGame() : Game("Mahjong Fight Girl") {}
|
||||
|
||||
virtual void attach() override;
|
||||
virtual void post_attach() override;
|
||||
virtual void detach() override;
|
||||
};
|
||||
}
|
||||
@@ -71,6 +71,8 @@ std::vector<Light> &games::museca::get_lights() {
|
||||
|
||||
GameAPI::Lights::sortLights(
|
||||
&lights,
|
||||
"Start",
|
||||
"Keypad",
|
||||
"Title R",
|
||||
"Title G",
|
||||
"Title B",
|
||||
|
||||
@@ -46,6 +46,8 @@ namespace games::museca {
|
||||
// all lights in correct order
|
||||
namespace Lights {
|
||||
enum {
|
||||
Start,
|
||||
Keypad,
|
||||
TitleR,
|
||||
TitleG,
|
||||
TitleB,
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace games::qma {
|
||||
touch_attach_dx_hook();
|
||||
|
||||
// cursor
|
||||
if (!is_touch_available()) {
|
||||
if (!is_touch_available("QMATouchDevice::open")) {
|
||||
ShowCursor(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ bool games::rb::ReflecBeatTouchDeviceHandle::open(LPCWSTR lpFileName) {
|
||||
}
|
||||
|
||||
// show cursor on window if mouse is used
|
||||
if (!is_touch_available()) {
|
||||
if (!is_touch_available("ReflecBeatTouchDeviceHandle::open")) {
|
||||
ShowCursor(TRUE);
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
touch_attach_dx_hook();
|
||||
|
||||
// cursor
|
||||
if (!is_touch_available()) {
|
||||
if (!is_touch_available("TwTouchDevice::open")) {
|
||||
ShowCursor(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,9 +79,25 @@ namespace games::sdvx {
|
||||
*phkResult = DEVICE_ASIO_REG_HANDLE;
|
||||
|
||||
log_info("sdvx::asio", "replacing '{}' with '{}'", lpSubKey, ASIO_DRIVER.value());
|
||||
|
||||
return RegOpenKeyExA_orig(real_asio_reg_handle, ASIO_DRIVER.value().c_str(), ulOptions, samDesired,
|
||||
const auto result = RegOpenKeyExA_orig(
|
||||
real_asio_reg_handle,
|
||||
ASIO_DRIVER.value().c_str(),
|
||||
ulOptions,
|
||||
samDesired,
|
||||
&real_asio_device_reg_handle);
|
||||
|
||||
if (result != ERROR_SUCCESS) {
|
||||
log_warning(
|
||||
"sdvx::asio",
|
||||
"failed to open registry subkey '{}', error=0x{:x}",
|
||||
ASIO_DRIVER.value().c_str(), result);
|
||||
log_warning(
|
||||
"sdvx::asio",
|
||||
"due to improper ASIO setting, game will likely fall back to WASAPI; double check -sdvxasio and the registry",
|
||||
ASIO_DRIVER.value().c_str(), result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,9 @@ namespace games::sdvx {
|
||||
enum SdvxOverlayPosition {
|
||||
SDVX_OVERLAY_TOP,
|
||||
SDVX_OVERLAY_MIDDLE,
|
||||
SDVX_OVERLAY_BOTTOM
|
||||
SDVX_OVERLAY_BOTTOM,
|
||||
SDVX_OVERLAY_BOTTOM_LEFT,
|
||||
SDVX_OVERLAY_BOTTOM_RIGHT
|
||||
};
|
||||
|
||||
// settings
|
||||
|
||||
@@ -680,7 +680,29 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDevice(
|
||||
|
||||
// dump presentation parameters
|
||||
for (size_t i = 0; i < num_adapters; i++) {
|
||||
const auto *params = &pPresentationParameters[i];
|
||||
auto *params = &pPresentationParameters[i];
|
||||
if (!GRAPHICS_WINDOWED && i == 0) {
|
||||
GRAPHICS_FS_ORIGINAL_WIDTH = params->BackBufferWidth;
|
||||
GRAPHICS_FS_ORIGINAL_HEIGHT = params->BackBufferHeight;
|
||||
log_misc("graphics::d3d9", "original resolution: {}x{}", GRAPHICS_FS_ORIGINAL_WIDTH, GRAPHICS_FS_ORIGINAL_HEIGHT);
|
||||
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
||||
log_misc(
|
||||
"graphics::d3d9",
|
||||
"use custom resolution {}x{} => {}x{}",
|
||||
params->BackBufferWidth, params->BackBufferHeight,
|
||||
GRAPHICS_FS_CUSTOM_RESOLUTION.value().first,
|
||||
GRAPHICS_FS_CUSTOM_RESOLUTION.value().second);
|
||||
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
||||
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
||||
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||
log_misc(
|
||||
"graphics::d3d9",
|
||||
"swap full orientation {}x{} => {}x{}",
|
||||
params->BackBufferWidth, params->BackBufferHeight,
|
||||
params->BackBufferHeight, params->BackBufferWidth);
|
||||
std::swap(params->BackBufferWidth, params->BackBufferHeight);
|
||||
}
|
||||
}
|
||||
|
||||
log_info("graphics::d3d9",
|
||||
"D3D9 presentation parameters for adapter {}: BackBufferWidth: {}, BackBufferHeight: {}, "
|
||||
@@ -846,7 +868,29 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_adapters; i++) {
|
||||
const auto *params = &pPresentationParameters[i];
|
||||
auto *params = &pPresentationParameters[i];
|
||||
if (!GRAPHICS_WINDOWED && i == 0) {
|
||||
GRAPHICS_FS_ORIGINAL_WIDTH = params->BackBufferWidth;
|
||||
GRAPHICS_FS_ORIGINAL_HEIGHT = params->BackBufferHeight;
|
||||
log_misc("graphics::d3d9", "original resolution: {}x{}", GRAPHICS_FS_ORIGINAL_WIDTH, GRAPHICS_FS_ORIGINAL_HEIGHT);
|
||||
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
||||
log_misc(
|
||||
"graphics::d3d9",
|
||||
"use custom resolution {}x{} => {}x{}",
|
||||
params->BackBufferWidth, params->BackBufferHeight,
|
||||
GRAPHICS_FS_CUSTOM_RESOLUTION.value().first,
|
||||
GRAPHICS_FS_CUSTOM_RESOLUTION.value().second);
|
||||
params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
||||
params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
||||
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||
log_misc(
|
||||
"graphics::d3d9",
|
||||
"swap full orientation {}x{} => {}x{}",
|
||||
params->BackBufferWidth, params->BackBufferHeight,
|
||||
params->BackBufferHeight, params->BackBufferWidth);
|
||||
std::swap(params->BackBufferWidth, params->BackBufferHeight);
|
||||
}
|
||||
}
|
||||
|
||||
log_info("graphics::d3d9",
|
||||
"D3D9Ex presentation parameters for adapter {}: BackBufferWidth: {}, BackBufferHeight: {}, "
|
||||
@@ -870,7 +914,16 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx(
|
||||
}
|
||||
if (pFullscreenDisplayMode) {
|
||||
for (size_t i = 0; i < num_adapters; i++) {
|
||||
const auto *fullscreen_display_mode = &pFullscreenDisplayMode[i];
|
||||
auto *fullscreen_display_mode = &pFullscreenDisplayMode[i];
|
||||
|
||||
if (!GRAPHICS_WINDOWED && i == 0) {
|
||||
if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) {
|
||||
fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first;
|
||||
fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second;
|
||||
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||
std::swap(fullscreen_display_mode->Width, fullscreen_display_mode->Height);
|
||||
}
|
||||
}
|
||||
|
||||
log_info("graphics::d3d9",
|
||||
"D3D9Ex fullscreen display mode for adapter {}: Width: {}, Height: {}, RefreshRate: {}, "
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "overlay/overlay.h"
|
||||
#include "util/flags_helper.h"
|
||||
#include "util/utils.h"
|
||||
#include "cfg/screen_resize.h"
|
||||
|
||||
#include "d3d9_backend.h"
|
||||
@@ -692,88 +693,181 @@ static IDirect3DSurface9 *backbuffer = nullptr;
|
||||
static LPDIRECT3DSWAPCHAIN9 mSwapChain = nullptr;
|
||||
static IDirect3DTexture9* tex;
|
||||
|
||||
static UINT topSurface_width = 0;
|
||||
static UINT topSurface_height = 0;
|
||||
static float topSurface_aspect_ratio = 1.f;
|
||||
|
||||
void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||
// log_misc("graphics::d3d9", "SurfaceHook called");
|
||||
D3DPRESENT_PARAMETERS param {};
|
||||
|
||||
pReal->GetSwapChain(0, &mSwapChain);
|
||||
mSwapChain->GetPresentParameters(¶m);
|
||||
|
||||
// phase 0 - create a surface that has the same aspect ratio as the original back buffer
|
||||
// but larger in size. this is needed to ensure that when the user zooms out, we have
|
||||
// sufficient buffer space (black border) around the original image
|
||||
//
|
||||
// (only done once)
|
||||
if (!topSurface) {
|
||||
if (pReal->CreateTexture(4096, 4096, 1,
|
||||
topSurface_width = param.BackBufferWidth * 3;
|
||||
topSurface_height = param.BackBufferHeight * 3;
|
||||
topSurface_aspect_ratio = (float)topSurface_width / (float)topSurface_height;
|
||||
if (pReal->CreateTexture(topSurface_width, topSurface_height, 1,
|
||||
D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8,
|
||||
D3DPOOL_DEFAULT, &tex, NULL) != D3D_OK) {
|
||||
log_misc("graphics::d3d9", "create texture failed");
|
||||
|
||||
log_warning("graphics::d3d9", "SurfaceHook - create texture failed");
|
||||
}
|
||||
log_misc("graphics::d3d9", "Backbuffer: {} {} {}",
|
||||
param.BackBufferWidth, param.BackBufferHeight, param.BackBufferCount);
|
||||
tex->GetSurfaceLevel(0, &topSurface);
|
||||
|
||||
log_misc(
|
||||
"graphics::d3d9", "SurfaceHook - Backbuffer: {} {} {}",
|
||||
param.BackBufferWidth, param.BackBufferHeight, param.BackBufferCount);
|
||||
|
||||
tex->GetSurfaceLevel(0, &topSurface);
|
||||
if (mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &backbuffer) != D3D_OK) {
|
||||
log_misc("graphics::d3d9", "GetBackBuffer failed");
|
||||
log_warning("graphics::d3d9", "SurfaceHook - GetBackBuffer failed");
|
||||
}
|
||||
}
|
||||
|
||||
const int rectLeft = 1024;
|
||||
const int rectTop = 576;
|
||||
// pre-calculate dimensions used for phase 1
|
||||
const int rectLeft = param.BackBufferWidth;
|
||||
const int rectTop = param.BackBufferHeight;
|
||||
const int w = param.BackBufferWidth;
|
||||
const int h = param.BackBufferHeight;
|
||||
|
||||
D3DLOCKED_RECT rect;
|
||||
HRESULT hr;
|
||||
topSurface->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
|
||||
|
||||
// phase 1 - copy the original back buffer onto the new surface.
|
||||
// we draw it 1:1 in the center of the surface.
|
||||
// if orientation is swapped, fix up the rect and draw it in the center.
|
||||
//
|
||||
// for this phase we always render with the same rect so that it's always overwritten
|
||||
// by a new frame on the same spot.
|
||||
RECT originRect {
|
||||
rectLeft,
|
||||
rectTop,
|
||||
(LONG)(rectLeft + w),
|
||||
(LONG)(rectTop + h),
|
||||
};
|
||||
if (GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||
originRect.left = (topSurface_width / 2) - (GRAPHICS_FS_ORIGINAL_WIDTH / 2);
|
||||
originRect.top = (topSurface_height / 2) - (GRAPHICS_FS_ORIGINAL_HEIGHT / 2);
|
||||
originRect.right = originRect.left + GRAPHICS_FS_ORIGINAL_WIDTH;
|
||||
originRect.bottom = originRect.top + GRAPHICS_FS_ORIGINAL_HEIGHT;
|
||||
}
|
||||
|
||||
// log_misc(
|
||||
// "graphics::d3d9", "originRect: {} {} {} {}",
|
||||
// originRect.left, originRect.top, originRect.right, originRect.bottom);
|
||||
|
||||
hr = pReal->StretchRect(
|
||||
backbuffer, nullptr,
|
||||
topSurface, &originRect,
|
||||
D3DTEXF_LINEAR);
|
||||
if (hr != D3D_OK) {
|
||||
log_warning(
|
||||
"graphics::d3d9",
|
||||
"SurfaceHook - StretchRect backbuffer failed, rect: {} {} {} {}",
|
||||
originRect.left, originRect.top, originRect.right, originRect.bottom);
|
||||
}
|
||||
topSurface->UnlockRect();
|
||||
|
||||
// phase 2 - viewport calculation - do the actual zoom / offset math
|
||||
// figure out what region of the surface to copy back to the back buffer.
|
||||
RECT targetRect {
|
||||
rectLeft,
|
||||
rectTop,
|
||||
(LONG)(rectLeft + w),
|
||||
(LONG)(rectTop + h),
|
||||
};
|
||||
if (cfg::SCREENRESIZE->enable_screen_resize) {
|
||||
auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene];
|
||||
auto w_new = w / scene.scale_x;
|
||||
auto h_new = h / scene.scale_y;
|
||||
|
||||
// stretch to add top/left offset to avoid going negative
|
||||
topSurface->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
|
||||
auto hr = pReal->StretchRect(
|
||||
backbuffer, nullptr,
|
||||
topSurface, &targetRect,
|
||||
D3DTEXF_LINEAR);
|
||||
// make sure the scaling is within bounds
|
||||
if (cfg::SCREENRESIZE->client_keep_aspect_ratio) {
|
||||
if (w_new > topSurface_width || h_new > topSurface_height) {
|
||||
w_new = topSurface_width;
|
||||
h_new = topSurface_height;
|
||||
}
|
||||
} else {
|
||||
w_new = MIN(w_new, topSurface_width);
|
||||
h_new = MIN(h_new, topSurface_height);
|
||||
}
|
||||
|
||||
if (hr != D3D_OK) {
|
||||
log_misc("graphics::d3d9", "StretchRect backbuffer failed");
|
||||
}
|
||||
topSurface->UnlockRect();
|
||||
targetRect.left = (topSurface_width / 2) - (w_new / 2) - scene.offset_x;
|
||||
targetRect.top = (topSurface_height / 2) - (h_new / 2) - scene.offset_y;
|
||||
targetRect.right = targetRect.left + w_new;
|
||||
targetRect.bottom = targetRect.top + h_new;
|
||||
|
||||
// do the actual zoom / offset math
|
||||
if (cfg::SCREENRESIZE->centered) {
|
||||
targetRect.right = (w + rectLeft) / cfg::SCREENRESIZE->scale_x;
|
||||
targetRect.bottom = (h + rectTop) / cfg::SCREENRESIZE->scale_y;
|
||||
const LONG deltaH = ((targetRect.bottom - targetRect.top) - h) / 2;
|
||||
const LONG deltaW = ((targetRect.right - targetRect.left) - w) / 2;
|
||||
targetRect.top -= deltaH;
|
||||
targetRect.bottom -= deltaH;
|
||||
targetRect.left -= deltaW;
|
||||
targetRect.right -= deltaW;
|
||||
} else {
|
||||
targetRect.left -= cfg::SCREENRESIZE->offset_x;
|
||||
targetRect.top += cfg::SCREENRESIZE->offset_y;
|
||||
targetRect.right = -cfg::SCREENRESIZE->offset_x;
|
||||
targetRect.right += (w + rectLeft) / cfg::SCREENRESIZE->scale_x;
|
||||
targetRect.bottom = cfg::SCREENRESIZE->offset_y;
|
||||
targetRect.bottom += (h + rectTop) / cfg::SCREENRESIZE->scale_y;
|
||||
// boundary check to prevent StretchRect failures
|
||||
if (targetRect.left < 0) {
|
||||
targetRect.left = 0;
|
||||
targetRect.right = w_new;
|
||||
} else if (targetRect.right > (LONG)topSurface_width) {
|
||||
targetRect.left = topSurface_width - w_new;
|
||||
targetRect.right = topSurface_width;
|
||||
}
|
||||
if (targetRect.top < 0) {
|
||||
targetRect.top = 0;
|
||||
targetRect.bottom = h_new;
|
||||
} else if (targetRect.bottom > (LONG)topSurface_height) {
|
||||
targetRect.top = topSurface_height - h_new;
|
||||
targetRect.bottom = topSurface_height;
|
||||
}
|
||||
|
||||
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||
// increase the viewport to fit the image on screen (effectively, zoom out a little)
|
||||
if (GRAPHICS_FS_ORIGINAL_WIDTH < GRAPHICS_FS_ORIGINAL_HEIGHT) {
|
||||
// portrait game on landscape display
|
||||
// height should match the original image
|
||||
targetRect.top = originRect.top;
|
||||
targetRect.bottom = originRect.bottom;
|
||||
|
||||
// width of viewport should be wider, effectively shrinking the image on x-axis when rendered
|
||||
const auto w_new = GRAPHICS_FS_ORIGINAL_HEIGHT * topSurface_aspect_ratio;
|
||||
targetRect.left = (topSurface_width / 2) - (w_new / 2);
|
||||
targetRect.right = targetRect.left + w_new;
|
||||
} else {
|
||||
// landscape game on portrait display
|
||||
targetRect.left = originRect.left;
|
||||
targetRect.right = originRect.right;
|
||||
|
||||
// height of viewport should be taller, effectively shrinking the image on y-axis when rendered
|
||||
const auto h_new = GRAPHICS_FS_ORIGINAL_WIDTH / topSurface_aspect_ratio;
|
||||
targetRect.top = (topSurface_height / 2) - (h_new / 2);
|
||||
targetRect.bottom = targetRect.top + h_new;
|
||||
}
|
||||
}
|
||||
|
||||
// draw to back buffer
|
||||
// log_misc("graphics::d3d9", "targetRect: {} {} {} {}",
|
||||
// targetRect.left, targetRect.top, targetRect.right, targetRect.bottom);
|
||||
|
||||
// phase 3 - draw the surface to back buffer
|
||||
backbuffer->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
|
||||
bool use_linear_filter = true;
|
||||
if (cfg::SCREENRESIZE->enable_screen_resize) {
|
||||
use_linear_filter = cfg::SCREENRESIZE->enable_linear_filter;
|
||||
}
|
||||
hr = pReal->StretchRect(
|
||||
topSurface, &targetRect,
|
||||
backbuffer, nullptr,
|
||||
cfg::SCREENRESIZE->enable_linear_filter ? D3DTEXF_LINEAR : D3DTEXF_NONE);
|
||||
use_linear_filter ? D3DTEXF_LINEAR : D3DTEXF_NONE);
|
||||
backbuffer->UnlockRect();
|
||||
if (hr != D3D_OK) {
|
||||
log_misc("graphics::d3d9", "StretchRect targetRect failed");
|
||||
log_warning(
|
||||
"graphics::d3d9",
|
||||
"SurfaceHook - StretchRect targetRect failed, you must reset image scaling to default values! rect: {} {} {} {}",
|
||||
targetRect.left, targetRect.top, targetRect.right, targetRect.bottom);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::EndScene() {
|
||||
WRAP_DEBUG;
|
||||
|
||||
if (cfg::SCREENRESIZE->enable_screen_resize) {
|
||||
if (cfg::SCREENRESIZE->enable_screen_resize || GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||
SurfaceHook(pReal);
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,10 @@ graphics_dx9on12_state GRAPHICS_9_ON_12_STATE = DX9ON12_AUTO;
|
||||
bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME = false;
|
||||
bool SUBSCREEN_FORCE_REDRAW = false;
|
||||
bool D3D9_DEVICE_HOOK_DISABLE = false;
|
||||
std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
|
||||
bool GRAPHICS_FS_ORIENTATION_SWAP = false;
|
||||
uint32_t GRAPHICS_FS_ORIGINAL_WIDTH = 0;
|
||||
uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT = 0;
|
||||
|
||||
// settings
|
||||
std::string GRAPHICS_DEVICEID = "PCI\\VEN_1002&DEV_7146";
|
||||
|
||||
@@ -35,6 +35,10 @@ extern UINT GRAPHICS_FORCE_REFRESH;
|
||||
extern std::optional<int> GRAPHICS_FORCE_VSYNC_BUFFER;
|
||||
extern bool GRAPHICS_FORCE_SINGLE_ADAPTER;
|
||||
extern bool GRAPHICS_PREVENT_SECONDARY_WINDOW;
|
||||
extern std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_FS_CUSTOM_RESOLUTION;
|
||||
extern bool GRAPHICS_FS_ORIENTATION_SWAP;
|
||||
extern uint32_t GRAPHICS_FS_ORIGINAL_WIDTH;
|
||||
extern uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT;
|
||||
|
||||
extern graphics_dx9on12_state GRAPHICS_9_ON_12_STATE;
|
||||
extern bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME;
|
||||
|
||||
@@ -123,10 +123,6 @@ void graphics_load_windowed_parameters() {
|
||||
}
|
||||
|
||||
log_debug("graphics-windowed", "graphics_load_windowed_parameters called");
|
||||
const auto remove_spaces = [](const char& c) {
|
||||
return c == ' ';
|
||||
};
|
||||
|
||||
if (GRAPHICS_WINDOW_STYLE.has_value()) {
|
||||
log_debug(
|
||||
"graphics-windowed",
|
||||
@@ -148,13 +144,11 @@ void graphics_load_windowed_parameters() {
|
||||
log_debug(
|
||||
"graphics-windowed",
|
||||
"graphics_load_windowed_parameters - load GRAPHICS_WINDOW_POS");
|
||||
int32_t x, y;
|
||||
auto s = GRAPHICS_WINDOW_POS.value();
|
||||
s.erase(std::remove_if(s.begin(), s.end(), remove_spaces), s.end());
|
||||
if (sscanf(s.c_str(), "%d,%d", &x, &y) == 2) {
|
||||
std::pair<uint32_t, uint32_t> result;
|
||||
if (parse_width_height(GRAPHICS_WINDOW_POS.value(), result)) {
|
||||
cfg::SCREENRESIZE->enable_window_resize = true;
|
||||
cfg::SCREENRESIZE->window_offset_x = x;
|
||||
cfg::SCREENRESIZE->window_offset_y = y;
|
||||
cfg::SCREENRESIZE->window_offset_x = result.first;
|
||||
cfg::SCREENRESIZE->window_offset_y = result.second;
|
||||
} else {
|
||||
log_warning("graphics-windowed", "failed to parse -windowpos");
|
||||
}
|
||||
@@ -174,20 +168,16 @@ void graphics_load_windowed_subscreen_parameters() {
|
||||
}
|
||||
|
||||
log_debug("graphics-windowed", "graphics_load_windowed_subscreen_parameters called");
|
||||
const auto remove_spaces = [](const char& c) {
|
||||
return c == ' ';
|
||||
};
|
||||
|
||||
|
||||
if (GRAPHICS_IIDX_WSUB_SIZE.has_value()) {
|
||||
log_debug(
|
||||
"graphics-windowed",
|
||||
"graphics_load_windowed_parameters - load GRAPHICS_IIDX_WSUB_SIZE");
|
||||
uint32_t w, h;
|
||||
auto s = GRAPHICS_IIDX_WSUB_SIZE.value();
|
||||
s.erase(std::remove_if(s.begin(), s.end(), remove_spaces), s.end());
|
||||
if (sscanf(s.c_str(), "%u,%u", &w, &h) == 2) {
|
||||
GRAPHICS_IIDX_WSUB_WIDTH = w;
|
||||
GRAPHICS_IIDX_WSUB_HEIGHT = h;
|
||||
|
||||
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;
|
||||
} else {
|
||||
log_warning("graphics-windowed", "failed to parse -wsubsize");
|
||||
}
|
||||
@@ -197,12 +187,11 @@ void graphics_load_windowed_subscreen_parameters() {
|
||||
log_debug(
|
||||
"graphics-windowed",
|
||||
"graphics_load_windowed_parameters - load GRAPHICS_IIDX_WSUB_POS");
|
||||
int32_t x, y;
|
||||
auto s = GRAPHICS_IIDX_WSUB_POS.value();
|
||||
s.erase(std::remove_if(s.begin(), s.end(), remove_spaces), s.end());
|
||||
if (sscanf(s.c_str(), "%d,%d", &x, &y) == 2) {
|
||||
GRAPHICS_IIDX_WSUB_X = x;
|
||||
GRAPHICS_IIDX_WSUB_Y = y;
|
||||
|
||||
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;
|
||||
} else {
|
||||
log_warning("graphics-windowed", "failed to parse -wsubpos");
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
#include "games/ccj/ccj.h"
|
||||
#include "games/ccj/trackball.h"
|
||||
#include "games/qks/qks.h"
|
||||
#include "games/mfg/mfg.h"
|
||||
#include "games/museca/museca.h"
|
||||
#include "hooks/avshook.h"
|
||||
#include "hooks/audio/audio.h"
|
||||
@@ -101,6 +102,7 @@
|
||||
#include "util/sysutils.h"
|
||||
#include "util/tapeled.h"
|
||||
#include "util/time.h"
|
||||
#include "util/utils.h"
|
||||
#include "avs/ssl.h"
|
||||
#include "nvapi/nvapi.h"
|
||||
#include "hooks/graphics/nvapi_hook.h"
|
||||
@@ -122,6 +124,8 @@ std::string LOG_FILE_PATH = "";
|
||||
int LAUNCHER_ARGC = 0;
|
||||
char **LAUNCHER_ARGV = nullptr;
|
||||
std::unique_ptr<std::vector<Option>> LAUNCHER_OPTIONS;
|
||||
|
||||
std::mutex CARD_OVERRIDES_LOCK;
|
||||
std::string CARD_OVERRIDES[2];
|
||||
|
||||
// sub-systems
|
||||
@@ -143,6 +147,8 @@ static bool check_dll(const std::string &model) {
|
||||
}
|
||||
}
|
||||
|
||||
void update_msvcrt_args(int argc, char *argv[]);
|
||||
|
||||
int main_implementation(int argc, char *argv[]) {
|
||||
|
||||
// remember argv, argv
|
||||
@@ -211,7 +217,9 @@ int main_implementation(int argc, char *argv[]) {
|
||||
bool attach_bc = false;
|
||||
bool attach_ccj = false;
|
||||
bool attach_qks = false;
|
||||
bool attach_mfg = false;
|
||||
bool attach_museca = false;
|
||||
bool show_cursor_if_no_touch = false;
|
||||
|
||||
// misc settings
|
||||
size_t user_heap_size = 0;
|
||||
@@ -434,7 +442,12 @@ int main_implementation(int argc, char *argv[]) {
|
||||
games::sdvx::OVERLAY_POS = games::sdvx::SDVX_OVERLAY_TOP;
|
||||
} else if (txt == "center") {
|
||||
games::sdvx::OVERLAY_POS = games::sdvx::SDVX_OVERLAY_MIDDLE;
|
||||
} else if (txt == "bottomleft") {
|
||||
games::sdvx::OVERLAY_POS = games::sdvx::SDVX_OVERLAY_BOTTOM_LEFT;
|
||||
} else if (txt == "bottomright") {
|
||||
games::sdvx::OVERLAY_POS = games::sdvx::SDVX_OVERLAY_BOTTOM_RIGHT;
|
||||
}
|
||||
// else - bottom
|
||||
}
|
||||
if (options[launcher::Options::spice2x_SDVXSubRedraw].value_bool()) {
|
||||
SUBSCREEN_FORCE_REDRAW = true;
|
||||
@@ -600,6 +613,9 @@ int main_implementation(int argc, char *argv[]) {
|
||||
if (options[launcher::Options::LoadQKSModule].value_bool()) {
|
||||
attach_qks = true;
|
||||
}
|
||||
if (options[launcher::Options::LoadMFGModule].value_bool()) {
|
||||
attach_mfg = true;
|
||||
}
|
||||
if (options[launcher::Options::LoadMusecaModule].value_bool()) {
|
||||
attach_museca = true;
|
||||
}
|
||||
@@ -730,11 +746,22 @@ int main_implementation(int argc, char *argv[]) {
|
||||
SetDllDirectoryW(MODULE_PATH.c_str());
|
||||
}
|
||||
if (options[launcher::Options::Player1Card].is_active()) {
|
||||
std::lock_guard<std::mutex> lock(CARD_OVERRIDES_LOCK);
|
||||
CARD_OVERRIDES[0] = options[launcher::Options::Player1Card].value_text();
|
||||
}
|
||||
if (options[launcher::Options::Player2Card].is_active()) {
|
||||
std::lock_guard<std::mutex> lock(CARD_OVERRIDES_LOCK);
|
||||
CARD_OVERRIDES[1] = options[launcher::Options::Player2Card].value_text();
|
||||
}
|
||||
if (options[launcher::Options::Player1PinMacro].is_active()) {
|
||||
PIN_MACRO_ENABLED = true;
|
||||
PIN_MACRO_VALUES[0] = options[launcher::Options::Player1PinMacro].value_text();
|
||||
}
|
||||
if (options[launcher::Options::Player2PinMacro].is_active()) {
|
||||
PIN_MACRO_ENABLED = true;
|
||||
PIN_MACRO_VALUES[1] = options[launcher::Options::Player2PinMacro].value_text();
|
||||
}
|
||||
|
||||
for (auto &reader : options[launcher::Options::ICCAReaderPort].values_text()) {
|
||||
static int reader_id = 0;
|
||||
if (reader_id < 2) {
|
||||
@@ -936,12 +963,9 @@ int main_implementation(int argc, char *argv[]) {
|
||||
GRAPHICS_WINDOW_STYLE = options[launcher::Options::spice2x_WindowBorder].value_uint32();
|
||||
}
|
||||
if (options[launcher::Options::spice2x_WindowSize].is_active()) {
|
||||
auto s = options[launcher::Options::spice2x_WindowSize].value_text();
|
||||
uint32_t w, h;
|
||||
const auto remove_spaces = [](const char& c) { return c == ' '; };
|
||||
s.erase(std::remove_if(s.begin(), s.end(), remove_spaces), s.end());
|
||||
if (sscanf(s.c_str(), "%u,%u", &w, &h) == 2) {
|
||||
GRAPHICS_WINDOW_SIZE = std::pair(w, h);
|
||||
std::pair<uint32_t, uint32_t> result;
|
||||
if (parse_width_height(options[launcher::Options::spice2x_WindowSize].value_text(), result)) {
|
||||
GRAPHICS_WINDOW_SIZE = result;
|
||||
} else {
|
||||
log_warning("launcher", "failed to parse -windowsize");
|
||||
}
|
||||
@@ -1017,6 +1041,15 @@ int main_implementation(int argc, char *argv[]) {
|
||||
if (options[launcher::Options::QKSArgs].is_active()) {
|
||||
games::qks::QKS_INJECT_ARGS = options[launcher::Options::QKSArgs].value_text();
|
||||
}
|
||||
if (options[launcher::Options::MFGArgs].is_active()) {
|
||||
games::mfg::MFG_INJECT_ARGS = options[launcher::Options::MFGArgs].value_text();
|
||||
}
|
||||
if (options[launcher::Options::MFGCabType].is_active()) {
|
||||
games::mfg::MFG_CABINET_TYPE = options[launcher::Options::MFGCabType].value_text();
|
||||
}
|
||||
if (options[launcher::Options::MFGNoIO].is_active()) {
|
||||
games::mfg::MFG_NO_IO = options[launcher::Options::MFGNoIO].value_bool();
|
||||
}
|
||||
if (options[launcher::Options::spice2x_EnableSMXStage].value_bool()) {
|
||||
rawinput::ENABLE_SMX_STAGE = true;
|
||||
}
|
||||
@@ -1085,6 +1118,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
|
||||
@@ -1128,6 +1163,30 @@ int main_implementation(int argc, char *argv[]) {
|
||||
}
|
||||
log_info("launcher", "arguments:\n{}", arguments.str());
|
||||
|
||||
if (options[launcher::Options::FullscreenResolution].is_active()) {
|
||||
std::pair<uint32_t, uint32_t> result;
|
||||
if (parse_width_height(options[launcher::Options::FullscreenResolution].value_text(), result)) {
|
||||
GRAPHICS_FS_CUSTOM_RESOLUTION = result;
|
||||
} else {
|
||||
log_warning("launcher", "failed to parse -forceres");
|
||||
}
|
||||
}
|
||||
|
||||
// // SDVXFullscreenLandscape disabled due to it messing with in-game camera angle
|
||||
// // FullscreenOrientationFlip continues to live on, however.
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
|
||||
// deleted options
|
||||
if (options[launcher::Options::OpenKFControl].value_bool()) {
|
||||
log_fatal("launcher", "KFControl has been removed from spice2x; please use an older version.");
|
||||
@@ -1243,11 +1302,7 @@ int main_implementation(int argc, char *argv[]) {
|
||||
avs::game::DLL_NAME = "system.dll";
|
||||
attach_io = true;
|
||||
attach_shogikai = true;
|
||||
|
||||
// automatically show cursor when no touchscreen is available
|
||||
if (!is_touch_available()) {
|
||||
GRAPHICS_SHOW_CURSOR = true;
|
||||
}
|
||||
show_cursor_if_no_touch = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1290,12 +1345,7 @@ int main_implementation(int argc, char *argv[]) {
|
||||
|
||||
// game crash fix
|
||||
easrv_maint = false;
|
||||
|
||||
// automatically show cursor when no touchscreen is available
|
||||
if (!is_touch_available()) {
|
||||
GRAPHICS_SHOW_CURSOR = true;
|
||||
}
|
||||
|
||||
show_cursor_if_no_touch = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1410,12 +1460,7 @@ int main_implementation(int argc, char *argv[]) {
|
||||
avs::game::DLL_NAME = "nostalgia.dll";
|
||||
attach_io = true;
|
||||
attach_nostalgia = true;
|
||||
|
||||
// automatically show cursor when no touchscreen is available
|
||||
if (!is_touch_available()) {
|
||||
GRAPHICS_SHOW_CURSOR = true;
|
||||
}
|
||||
|
||||
show_cursor_if_no_touch = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1521,12 +1566,7 @@ int main_implementation(int argc, char *argv[]) {
|
||||
avs::game::DLL_NAME = "arknck.dll";
|
||||
attach_io = true;
|
||||
attach_we = true;
|
||||
|
||||
// automatically show cursor when no touchscreen is available
|
||||
if (!is_touch_available()) {
|
||||
GRAPHICS_SHOW_CURSOR = true;
|
||||
}
|
||||
|
||||
show_cursor_if_no_touch = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1559,10 +1599,7 @@ int main_implementation(int argc, char *argv[]) {
|
||||
avs::game::DLL_NAME = "kamunity.dll";
|
||||
attach_io = true;
|
||||
attach_ccj = true;
|
||||
// automatically show cursor when no touchscreen is available
|
||||
if (!is_touch_available()) {
|
||||
GRAPHICS_SHOW_CURSOR = true;
|
||||
}
|
||||
show_cursor_if_no_touch = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1571,10 +1608,17 @@ int main_implementation(int argc, char *argv[]) {
|
||||
avs::game::DLL_NAME = "kamunity.dll";
|
||||
attach_io = true;
|
||||
attach_qks = true;
|
||||
// automatically show cursor when no touchscreen is available
|
||||
if (!is_touch_available()) {
|
||||
GRAPHICS_SHOW_CURSOR = true;
|
||||
}
|
||||
show_cursor_if_no_touch = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Mahjong Fight Girl
|
||||
if (check_dll("kamunity.dll") && fileutils::dir_exists("game/MFGClient_Data")) {
|
||||
avs::game::DLL_NAME = "kamunity.dll";
|
||||
attach_io = true;
|
||||
attach_mfg = true;
|
||||
launcher::signal::USE_VEH_WORKAROUND = true;
|
||||
show_cursor_if_no_touch = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1583,10 +1627,7 @@ int main_implementation(int argc, char *argv[]) {
|
||||
avs::game::DLL_NAME = "kamunity.dll";
|
||||
attach_io = true;
|
||||
attach_bc = true;
|
||||
// automatically show cursor when no touchscreen is available
|
||||
if (!is_touch_available()) {
|
||||
GRAPHICS_SHOW_CURSOR = true;
|
||||
}
|
||||
show_cursor_if_no_touch = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1729,6 +1770,10 @@ int main_implementation(int argc, char *argv[]) {
|
||||
avs::core::set_default_heap_size("kamunity.dll");
|
||||
games.push_back(new games::qks::QKSGame());
|
||||
}
|
||||
if (attach_mfg) {
|
||||
avs::core::set_default_heap_size("kamunity.dll");
|
||||
games.push_back(new games::mfg::MFGGame());
|
||||
}
|
||||
|
||||
// apply user heap size, if defined
|
||||
if (user_heap_size > 0) {
|
||||
@@ -1761,6 +1806,9 @@ int main_implementation(int argc, char *argv[]) {
|
||||
exit(spicecfg_run(sextet_devices));
|
||||
}
|
||||
|
||||
// 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();
|
||||
@@ -1778,6 +1826,11 @@ int main_implementation(int argc, char *argv[]) {
|
||||
// print devices
|
||||
RI_MGR->devices_print();
|
||||
|
||||
// for certain games, show cursor if no touch is available (must be called after RI_MGR is available)
|
||||
if (show_cursor_if_no_touch && !is_touch_available("launcher::main_implementation")) {
|
||||
GRAPHICS_SHOW_CURSOR = true;
|
||||
}
|
||||
|
||||
// cardio
|
||||
if (cardio_enabled) {
|
||||
cardio_runner_start(true);
|
||||
@@ -1923,6 +1976,8 @@ int main_implementation(int argc, char *argv[]) {
|
||||
log_warning("launcher", "https://github.com/mon/ifs_layeredfs");
|
||||
}
|
||||
|
||||
update_msvcrt_args(argc, argv);
|
||||
|
||||
// load hooks
|
||||
for (auto &hook : game_hooks) {
|
||||
log_info("launcher", "loading hook DLL {}", hook);
|
||||
@@ -1964,6 +2019,11 @@ int main_implementation(int argc, char *argv[]) {
|
||||
// start coin input thread
|
||||
eamuse_coin_start_thread();
|
||||
|
||||
// pin macro
|
||||
if (!cfg::CONFIGURATOR_STANDALONE && PIN_MACRO_ENABLED) {
|
||||
eamuse_pin_macro_start_thread();
|
||||
}
|
||||
|
||||
// print PEB
|
||||
if (peb_print) {
|
||||
peb::peb_print();
|
||||
@@ -2048,6 +2108,8 @@ int main_implementation(int argc, char *argv[]) {
|
||||
// stop coin input thread
|
||||
eamuse_coin_stop_thread();
|
||||
|
||||
eamuse_pin_macro_stop_thread();
|
||||
|
||||
// BT5API
|
||||
if (BT5API_ENABLED) {
|
||||
bt5api_dispose();
|
||||
@@ -2101,6 +2163,54 @@ int main_implementation(int argc, char *argv[]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// https://github.com/spice2x/spice2x.github.io/issues/264
|
||||
// huge ugly hack to work around things that broke when MinGW switched from msvcrt to ucrt
|
||||
// this is done to ensure that any DLL hooks that rely on msvcrt continue to work
|
||||
void update_msvcrt_args(int argc, char *argv[]) {
|
||||
#if defined(_UCRT)
|
||||
auto msvc = LoadLibraryA("msvcrt.dll");
|
||||
if (!msvc) {
|
||||
log_warning("launcher", "failed to load msvcrt.dll");
|
||||
return;
|
||||
}
|
||||
|
||||
// get __argc
|
||||
PINT32 argc_addr = (PINT32)GetProcAddress(msvc, "__argc");
|
||||
if (!argc_addr) {
|
||||
log_warning("launcher", "failed to find msvcrt!__argc");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (*argc_addr == argc) {
|
||||
log_warning("launcher", "msvcrt!__argc is already set");
|
||||
return;
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
log_warning("launcher", "exception while reading msvcrt!_argc: {}", e.what());
|
||||
}
|
||||
|
||||
// get __argv
|
||||
PCHAR **argv_addr = (PCHAR **)GetProcAddress(msvc, "__argv");
|
||||
if (!argv_addr) {
|
||||
log_warning("launcher", "failed to find msvcrt!__argv");
|
||||
return;
|
||||
}
|
||||
|
||||
// update them
|
||||
try {
|
||||
log_info("launcher", "msvcrt!__argc value before: {}", *argc_addr);
|
||||
*argc_addr = argc;
|
||||
log_info("launcher", "msvcrt!__argc value after: {}", *argc_addr);
|
||||
*argv_addr = argv;
|
||||
} catch (const std::exception &e) {
|
||||
log_warning("launcher", "exception while messing with msvcrt!_argc and _argv: {}", e.what());
|
||||
}
|
||||
|
||||
#else
|
||||
log_misc("launcher", "not UCRT, skipping msvcrt!_argc / _argv hacks");
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef SPICETOOLS_SPICECFG_STANDALONE
|
||||
int main(int argc, char *argv[]) {
|
||||
return main_implementation(argc, argv);
|
||||
|
||||
@@ -19,6 +19,7 @@ static const std::vector<std::string> CATEGORY_ORDER_BASIC = {
|
||||
"Network",
|
||||
"Overlay",
|
||||
"Graphics (Common)",
|
||||
"Graphics (Full Screen)",
|
||||
"Graphics (Windowed)",
|
||||
"Audio",
|
||||
};
|
||||
@@ -113,6 +114,26 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.category = "Network",
|
||||
.sensitive = true,
|
||||
},
|
||||
{
|
||||
// Player1PinMacro
|
||||
.title = "Player 1 PIN Macro",
|
||||
.name = "pinmacro0",
|
||||
.desc = "Set a PIN for Player 1 that will cause the PIN to be automatically typed when Player 1 PIN Macro overlay key is pressed",
|
||||
.type = OptionType::Text,
|
||||
.setting_name = "1234",
|
||||
.category = "Network (Advanced)",
|
||||
.sensitive = true,
|
||||
},
|
||||
{
|
||||
// Player2PinMacro
|
||||
.title = "Player 2 PIN Macro",
|
||||
.name = "pinmacro1",
|
||||
.desc = "Set a PIN for Player 2 that will cause the PIN to be automatically typed when Player 2 PIN Macro overlay key is pressed",
|
||||
.type = OptionType::Text,
|
||||
.setting_name = "5678",
|
||||
.category = "Network (Advanced)",
|
||||
.sensitive = true,
|
||||
},
|
||||
{
|
||||
.title = "Windowed Mode",
|
||||
.name = "w",
|
||||
@@ -165,9 +186,20 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
{
|
||||
.title = "Monitor",
|
||||
.name = "monitor",
|
||||
.desc = "Sets the display that the game will be opened in, for multiple monitors",
|
||||
.desc = "Sets the display that the game will be opened in, for multiple monitors.\n\n"
|
||||
"0 is the primary monitor, 1 is the second monitor, and so on.\n\n"
|
||||
"Not all games will respect this. Does not work at all with multi-monitor games like TDJ/UFC. "
|
||||
"Disable Full Screen Optimizations for best results",
|
||||
.type = OptionType::Integer,
|
||||
.category = "Graphics (Common)",
|
||||
.category = "Graphics (Full Screen)",
|
||||
},
|
||||
{
|
||||
.title = "Only Use One Monitor",
|
||||
.name = "graphics-force-single-adapter",
|
||||
.desc = "Force the graphics device to be opened utilizing only one adapter in multi-monitor systems.\n\n"
|
||||
"May cause unstable framerate and desyncs, especially if monitors have different refresh rates!",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Graphics (Full Screen)",
|
||||
},
|
||||
{
|
||||
.title = "Force Refresh Rate",
|
||||
@@ -177,12 +209,29 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.category = "Graphics (Common)",
|
||||
},
|
||||
{
|
||||
.title = "Only Use One Monitor",
|
||||
.name = "graphics-force-single-adapter",
|
||||
.desc = "Force the graphics device to be opened utilizing only one adapter in multi-monitor systems.\n\n"
|
||||
"May cause unstable framerate and desyncs, especially if monitors have different refresh rates!",
|
||||
// FullscreenResolution
|
||||
.title = "Force Full Screen Resolution",
|
||||
.name = "forceres",
|
||||
.desc =
|
||||
"For full screen mode, forcibly set a custom resolution.\n\n"
|
||||
"Works great for some games, but can COMPLETELY BREAK other games - YMMV!\n\n"
|
||||
"If you are using -forceresswap/-sdvxlandscape, put the TARGET monitor resolution in this field.\n\n"
|
||||
"This should only be used as last resort if your GPU/monitor can't display the resolution required by the game",
|
||||
.type = OptionType::Text,
|
||||
.setting_name = "1280,720",
|
||||
.category = "Graphics (Full Screen)"
|
||||
},
|
||||
{
|
||||
// FullscreenOrientationFlip
|
||||
.title = "Full Screen Orientation Swap",
|
||||
.name = "forceresswap",
|
||||
.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\n\n"
|
||||
"WARNING: for SDVX, this messes with camera angle for note lanes, causing it to be zoomed out!",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Graphics (Common)",
|
||||
.category = "Graphics (Full Screen)"
|
||||
},
|
||||
{
|
||||
// Graphics9On12
|
||||
@@ -713,7 +762,13 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.type = OptionType::Enum,
|
||||
.game_name = "Sound Voltex",
|
||||
.category = "Overlay",
|
||||
.elements = {{"top", ""}, {"center", ""}, {"bottom", ""}},
|
||||
.elements = {
|
||||
{"top", ""},
|
||||
{"center", ""},
|
||||
{"bottom", ""},
|
||||
{"bottomleft", "for landscape"},
|
||||
{"bottomright", "for landscape"},
|
||||
},
|
||||
},
|
||||
{
|
||||
// spice2x_SDVXSubRedraw
|
||||
@@ -986,6 +1041,15 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.game_name = "QuizKnock STADIUM",
|
||||
.category = "Game Options (Advanced)",
|
||||
},
|
||||
{
|
||||
// LoadMFGModule
|
||||
.title = "Force Load Mahjong Fight Girl",
|
||||
.name = "mfg",
|
||||
.desc = "manually enable Mahjong Fight Girl module",
|
||||
.type = OptionType::Bool,
|
||||
.game_name = "Mahjong Fight Girl",
|
||||
.category = "Game Options (Advanced)",
|
||||
},
|
||||
{
|
||||
// LoadMusecaModule
|
||||
.title = "Force Load Museca",
|
||||
@@ -1024,7 +1088,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",
|
||||
},
|
||||
@@ -1033,7 +1097,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",
|
||||
},
|
||||
@@ -1651,6 +1715,39 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.game_name = "Chase Chase Jokers",
|
||||
.category = "Game Options",
|
||||
},
|
||||
{
|
||||
.title = "MFG Arguments Override",
|
||||
.name = "mfgargs",
|
||||
.desc = "Command line arguments passed to the game.",
|
||||
.type = OptionType::Text,
|
||||
.setting_name = "",
|
||||
.game_name = "Mahjong Fight Girl",
|
||||
.category = "Game Options (Advanced)",
|
||||
},
|
||||
{
|
||||
.title = "MFG Cabinet Type",
|
||||
.name = "mfgcabtype",
|
||||
.desc = "MFG Cabinet Type. Default is HG.",
|
||||
.type = OptionType::Enum,
|
||||
.setting_name = "",
|
||||
.game_name = "Mahjong Fight Girl",
|
||||
.category = "Game Options",
|
||||
.elements = {
|
||||
{"HG", "HG"},
|
||||
{"B", "B"},
|
||||
{"C", "C"},
|
||||
{"UKS", "UKS"},
|
||||
},
|
||||
},
|
||||
{
|
||||
.title = "MFG Disable IO Emulation",
|
||||
.name = "mfgnoio",
|
||||
.desc = "Disables BI2X hooks for MFG",
|
||||
.type = OptionType::Bool,
|
||||
.setting_name = "",
|
||||
.game_name = "Mahjong Fight Girl",
|
||||
.category = "Game Options (Advanced)"
|
||||
},
|
||||
{
|
||||
// spice2x_LightsOverallBrightness
|
||||
.title = "Lights Brightness Adjustment",
|
||||
@@ -1721,7 +1818,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.title = "Window Forced Render Scaling",
|
||||
.name = "windowscale",
|
||||
.desc = "For windowed mode: forcibly set DX9 back buffer dimensions to match window size. "
|
||||
"Works great on some games, but completely broken on others.",
|
||||
"Reduces pixelated scaling artifacts. Works great for some games, but can COMPLETELY BREAK other games - YMMV!",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Graphics (Windowed)",
|
||||
},
|
||||
@@ -1951,6 +2048,20 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.game_name = "Sound Voltex",
|
||||
.category = "Game Options",
|
||||
},
|
||||
{
|
||||
// SDVXFullscreenLandscape
|
||||
.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",
|
||||
},
|
||||
{
|
||||
// spice2x_EnableSMXStage
|
||||
.title = "StepManiaX Stage Lighting Support",
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace launcher {
|
||||
PCBID,
|
||||
Player1Card,
|
||||
Player2Card,
|
||||
Player1PinMacro,
|
||||
Player2PinMacro,
|
||||
WindowedMode,
|
||||
InjectHook,
|
||||
EarlyInjectHook,
|
||||
@@ -25,8 +27,10 @@ namespace launcher {
|
||||
CaptureCursor,
|
||||
ShowCursor,
|
||||
DisplayAdapter,
|
||||
GraphicsForceRefresh,
|
||||
GraphicsForceSingleAdapter,
|
||||
GraphicsForceRefresh,
|
||||
FullscreenResolution,
|
||||
FullscreenOrientationFlip,
|
||||
Graphics9On12,
|
||||
spice2x_Dx9On12,
|
||||
NoLegacy,
|
||||
@@ -112,6 +116,7 @@ namespace launcher {
|
||||
LoadBusouShinkiModule,
|
||||
LoadCCJModule,
|
||||
LoadQKSModule,
|
||||
LoadMFGModule,
|
||||
LoadMusecaModule,
|
||||
PathToModules,
|
||||
ScreenshotFolder,
|
||||
@@ -191,6 +196,9 @@ namespace launcher {
|
||||
CCJMouseTrackball,
|
||||
CCJMouseTrackballWithToggle,
|
||||
CCJTrackballSensitivity,
|
||||
MFGArgs,
|
||||
MFGCabType,
|
||||
MFGNoIO,
|
||||
spice2x_LightsOverallBrightness,
|
||||
spice2x_WindowBorder,
|
||||
spice2x_WindowSize,
|
||||
@@ -215,6 +223,7 @@ namespace launcher {
|
||||
spice2x_NoNVAPI,
|
||||
spice2x_NoD3D9DeviceHook,
|
||||
spice2x_SDVXNoSub,
|
||||
SDVXFullscreenLandscape,
|
||||
spice2x_EnableSMXStage,
|
||||
spice2x_EnableSMXDedicab,
|
||||
IIDXRecQuality,
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace launcher::signal {
|
||||
|
||||
// settings
|
||||
bool DISABLE = false;
|
||||
bool USE_VEH_WORKAROUND = false;
|
||||
}
|
||||
|
||||
#define V(variant) case variant: return #variant
|
||||
@@ -167,7 +168,7 @@ static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter_hook(
|
||||
static PVOID WINAPI AddVectoredExceptionHandler_hook(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler) {
|
||||
log_info("signal", "AddVectoredExceptionHandler hook hit");
|
||||
|
||||
return nullptr;
|
||||
return launcher::signal::USE_VEH_WORKAROUND ? INVALID_HANDLE_VALUE : nullptr;
|
||||
}
|
||||
|
||||
void launcher::signal::attach() {
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace launcher::signal {
|
||||
|
||||
// settings
|
||||
extern bool DISABLE;
|
||||
extern bool USE_VEH_WORKAROUND;
|
||||
|
||||
//void print_stacktrace();
|
||||
void attach();
|
||||
|
||||
+117
-13
@@ -11,6 +11,7 @@
|
||||
#include "util/logging.h"
|
||||
#include "util/time.h"
|
||||
#include "util/utils.h"
|
||||
#include "overlay/overlay.h"
|
||||
|
||||
#include "bt5api.h"
|
||||
|
||||
@@ -39,6 +40,16 @@ static std::optional<double> AUTO_INSERT_CARD_FIRST_CONSUME_TIME;
|
||||
static bool AUTO_INSERT_CARD_CACHED[2];
|
||||
static uint8_t AUTO_INSERT_CARD_CACHED_DATA[2][8];
|
||||
|
||||
// pin macro
|
||||
bool PIN_MACRO_ENABLED = false;
|
||||
std::string PIN_MACRO_VALUES[2] = {"", ""};
|
||||
static std::thread *PIN_MACRO_THREAD = nullptr;
|
||||
static bool PIN_MACRO_THREAD_ACTIVE = false;
|
||||
static uint16_t PIN_MACRO_TRIGGER_KEYS[2] = {
|
||||
games::OverlayButtons::TriggerPinMacroP1,
|
||||
games::OverlayButtons::TriggerPinMacroP2
|
||||
};
|
||||
|
||||
bool eamuse_get_card(int active_count, int unit_id, uint8_t *card) {
|
||||
|
||||
// get unit index
|
||||
@@ -55,7 +66,7 @@ bool eamuse_get_card(int active_count, int unit_id, uint8_t *card) {
|
||||
if (CARD_INSERT_UID_ENABLE[index]) {
|
||||
CARD_INSERT_UID_ENABLE[index] = false;
|
||||
memcpy(card, CARD_INSERT_UID[index], 8);
|
||||
log_info("eamuse", "Inserted card from reader {}: {}", index, bin2hex(card, 8));
|
||||
log_info("eamuse", "[P{}] Inserted card from reader: {}", index+1, bin2hex(card, 8));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -72,35 +83,40 @@ bool eamuse_get_card(int active_count, int unit_id, uint8_t *card) {
|
||||
}
|
||||
|
||||
bool eamuse_get_card(const std::filesystem::path &path, uint8_t *card, int index) {
|
||||
|
||||
// do a quick copy under lock
|
||||
std::unique_lock<std::mutex> lock(CARD_OVERRIDES_LOCK);
|
||||
const auto card_override = CARD_OVERRIDES[index];
|
||||
lock.unlock();
|
||||
|
||||
// Check if card overrides are present
|
||||
if (!CARD_OVERRIDES[index].empty()) {
|
||||
if (!card_override.empty()) {
|
||||
|
||||
// Override is present
|
||||
for (int n = 0; n < 16; n++) {
|
||||
char c = CARD_OVERRIDES[index].c_str()[n];
|
||||
char c = card_override.c_str()[n];
|
||||
bool digit = c >= '0' && c <= '9';
|
||||
bool character_big = c >= 'A' && c <= 'F';
|
||||
bool character_small = c >= 'a' && c <= 'f';
|
||||
if (!digit && !character_big && !character_small) {
|
||||
log_warning("eamuse",
|
||||
"{} card override contains an invalid character sequence at byte {} (16 characters, 0-9/A-F only)",
|
||||
CARD_OVERRIDES[index], n);
|
||||
card_override, n);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Log info
|
||||
log_info("eamuse", "Inserted card override: {}", CARD_OVERRIDES[index]);
|
||||
log_info("eamuse", "[P{}] Inserted card override: {}", index+1, card_override);
|
||||
|
||||
// Card is valid, convert and set it.
|
||||
hex2bin(CARD_OVERRIDES[index].c_str(), card);
|
||||
hex2bin(card_override.c_str(), card);
|
||||
|
||||
// cache it for auto-insert
|
||||
if (AUTO_INSERT_CARD[index] && !AUTO_INSERT_CARD_CACHED[index]) {
|
||||
// always overwrite from overrides since override may have changed by user in card manager
|
||||
if (AUTO_INSERT_CARD[index]) {
|
||||
memcpy(AUTO_INSERT_CARD_CACHED_DATA[index], card, 8);
|
||||
AUTO_INSERT_CARD_CACHED[index] = true;
|
||||
log_info("eamuse", "Auto card insert - caching this card in memory: {}", CARD_OVERRIDES[index]);
|
||||
log_info("eamuse", "[P{}] Auto card insert - caching this card override in memory: {}", index+1, card_override);
|
||||
}
|
||||
|
||||
// success
|
||||
@@ -151,7 +167,7 @@ bool eamuse_get_card_from_file(const std::filesystem::path &path, uint8_t *card,
|
||||
}
|
||||
|
||||
// info
|
||||
log_info("eamuse", "Inserted {}: {}", path.string(), buffer);
|
||||
log_info("eamuse", "[P{}] Inserted {}: {}", index+1, path.string(), buffer);
|
||||
|
||||
// convert hex to bytes
|
||||
hex2bin(buffer, card);
|
||||
@@ -160,7 +176,7 @@ bool eamuse_get_card_from_file(const std::filesystem::path &path, uint8_t *card,
|
||||
if (AUTO_INSERT_CARD[index] && !AUTO_INSERT_CARD_CACHED[index]) {
|
||||
memcpy(AUTO_INSERT_CARD_CACHED_DATA[index], card, 8);
|
||||
AUTO_INSERT_CARD_CACHED[index] = true;
|
||||
log_info("eamuse", "Auto card insert - caching this card in memory: {}", buffer);
|
||||
log_info("eamuse", "[P{}] Auto card insert - caching this card from file in memory: {}", index+1, buffer);
|
||||
}
|
||||
|
||||
// success
|
||||
@@ -200,7 +216,6 @@ bool eamuse_card_insert_consume(int active_count, int unit_id) {
|
||||
if (!CARD_INSERT[index]) {
|
||||
eamuse_card_insert(index);
|
||||
// not logging anything here to prevent spam
|
||||
// log_info("eamuse", "Automatic card insert on {}/{} (-autocard)", unit_id + 1, active_count);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -212,7 +227,7 @@ bool eamuse_card_insert_consume(int active_count, int unit_id) {
|
||||
auto offset = unit_id * games::KeypadButtons::Size;
|
||||
if ((CARD_INSERT[index] && fabs(get_performance_seconds() - CARD_INSERT_TIME[index]) < CARD_INSERT_TIMEOUT)
|
||||
|| GameAPI::Buttons::getState(RI_MGR, keypad_buttons->at(games::KeypadButtons::InsertCard + offset))) {
|
||||
log_info("eamuse", "Card insert on {}/{}", unit_id + 1, active_count);
|
||||
log_info("eamuse", "[P{}] Card insert on reader (total active count: {})", unit_id+1, active_count);
|
||||
CARD_INSERT[index] = false;
|
||||
return true;
|
||||
}
|
||||
@@ -295,6 +310,93 @@ void eamuse_coin_stop_thread() {
|
||||
COIN_INPUT_THREAD = nullptr;
|
||||
}
|
||||
|
||||
void eamuse_pin_macro_start_thread() {
|
||||
|
||||
// set active
|
||||
PIN_MACRO_THREAD_ACTIVE = true;
|
||||
|
||||
// create thread
|
||||
PIN_MACRO_THREAD = new std::thread([]() {
|
||||
uint16_t keypad_overrides[] = {
|
||||
1 << EAM_IO_KEYPAD_0,
|
||||
1 << EAM_IO_KEYPAD_1,
|
||||
1 << EAM_IO_KEYPAD_2,
|
||||
1 << EAM_IO_KEYPAD_3,
|
||||
1 << EAM_IO_KEYPAD_4,
|
||||
1 << EAM_IO_KEYPAD_5,
|
||||
1 << EAM_IO_KEYPAD_6,
|
||||
1 << EAM_IO_KEYPAD_7,
|
||||
1 << EAM_IO_KEYPAD_8,
|
||||
1 << EAM_IO_KEYPAD_9,
|
||||
};
|
||||
auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game());
|
||||
size_t pin_index[2] = {PIN_MACRO_VALUES[0].length(), PIN_MACRO_VALUES[1].length()};
|
||||
|
||||
std::optional<uint8_t> active_unit = std::nullopt;
|
||||
|
||||
while (PIN_MACRO_THREAD_ACTIVE) {
|
||||
// wait for key press
|
||||
if (!active_unit.has_value()) {
|
||||
for (int unit = 0; unit < 2; unit++) {
|
||||
if (PIN_MACRO_VALUES[unit].empty()) {
|
||||
continue;
|
||||
}
|
||||
if (overlay_buttons &&
|
||||
(!overlay::OVERLAY || overlay::OVERLAY->hotkeys_triggered()) &&
|
||||
GameAPI::Buttons::getState(RI_MGR, overlay_buttons->at(PIN_MACRO_TRIGGER_KEYS[unit]))) {
|
||||
active_unit = unit;
|
||||
// Reset key index
|
||||
pin_index[unit] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!active_unit.has_value()) {
|
||||
Sleep(20);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const auto unit = active_unit.value();
|
||||
// get character from config
|
||||
if (pin_index[unit] < PIN_MACRO_VALUES[unit].length()) {
|
||||
|
||||
// insert character
|
||||
char pin_char = PIN_MACRO_VALUES[unit].at(pin_index[unit]);
|
||||
if (pin_char >= '0' && pin_char <= '9') {
|
||||
int char_index = pin_char - '0';
|
||||
eamuse_set_keypad_overrides(unit, keypad_overrides[char_index]);
|
||||
}
|
||||
pin_index[unit]++;
|
||||
Sleep(100);
|
||||
|
||||
// clear
|
||||
eamuse_set_keypad_overrides(unit, 0);
|
||||
Sleep(50);
|
||||
|
||||
// end of PIN
|
||||
if (pin_index[unit] == PIN_MACRO_VALUES[unit].length()) {
|
||||
active_unit = std::nullopt;
|
||||
Sleep(120);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Sleep(200);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void eamuse_pin_macro_stop_thread() {
|
||||
PIN_MACRO_THREAD_ACTIVE = false;
|
||||
if (PIN_MACRO_THREAD != nullptr) {
|
||||
PIN_MACRO_THREAD->join();
|
||||
delete PIN_MACRO_THREAD;
|
||||
PIN_MACRO_THREAD = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void eamuse_set_keypad_overrides(size_t unit, uint16_t keypad_state) {
|
||||
|
||||
// check unit
|
||||
@@ -555,6 +657,8 @@ void eamuse_autodetect_game() {
|
||||
eamuse_set_game("Chase Chase Jokers");
|
||||
else if (avs::game::is_model("UKS"))
|
||||
eamuse_set_game("QuizKnock STADIUM");
|
||||
else if (avs::game::is_model("VFG"))
|
||||
eamuse_set_game("Mahjong Fight Girl");
|
||||
else {
|
||||
log_warning("eamuse", "unknown game model: {}", avs::game::MODEL);
|
||||
eamuse_set_game("unknown");
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <mutex>
|
||||
|
||||
#include "external/scard/scard.h"
|
||||
|
||||
@@ -30,10 +31,14 @@ enum eam_io_keypad_scan_code {
|
||||
EAM_IO_INSERT = 13, /* SpiceTools Extension */
|
||||
};
|
||||
|
||||
extern std::mutex CARD_OVERRIDES_LOCK;
|
||||
extern std::string CARD_OVERRIDES[2];
|
||||
extern bool AUTO_INSERT_CARD[2];
|
||||
extern float AUTO_INSERT_CARD_COOLDOWN;
|
||||
|
||||
extern bool PIN_MACRO_ENABLED;
|
||||
extern std::string PIN_MACRO_VALUES[2];
|
||||
|
||||
bool eamuse_get_card(int active_count, int unit_id, uint8_t *card);
|
||||
bool eamuse_get_card(const std::filesystem::path &path, uint8_t *card, int unit_id);
|
||||
bool eamuse_get_card_from_file(const std::filesystem::path &path, uint8_t *card, int index);
|
||||
@@ -56,6 +61,9 @@ int eamuse_coin_add();
|
||||
void eamuse_coin_start_thread();
|
||||
void eamuse_coin_stop_thread();
|
||||
|
||||
void eamuse_pin_macro_start_thread();
|
||||
void eamuse_pin_macro_stop_thread();
|
||||
|
||||
void eamuse_set_keypad_overrides(size_t unit, uint16_t keypad_state);
|
||||
void eamuse_set_keypad_overrides_bt5(size_t unit, uint16_t keypad_state);
|
||||
void eamuse_set_keypad_overrides_reader(size_t unit, uint16_t keypad_state);
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace wintouchemu {
|
||||
bool LOG_FPS = false;
|
||||
|
||||
static inline bool is_emu_enabled() {
|
||||
return FORCE || !is_touch_available() || GRAPHICS_SHOW_CURSOR;
|
||||
return FORCE || !is_touch_available("wintouchemu::is_emu_enabled") || GRAPHICS_SHOW_CURSOR;
|
||||
}
|
||||
|
||||
static decltype(GetSystemMetrics) *GetSystemMetrics_orig = nullptr;
|
||||
|
||||
@@ -6,36 +6,61 @@
|
||||
|
||||
namespace ImGui {
|
||||
|
||||
const auto fg = ImVec4(0.910f, 0.914f, 0.922f, 1.0f);
|
||||
const auto bg = ImVec4(0.192f, 0.212f, 0.220f, 1.0f);
|
||||
|
||||
void HelpTooltip(const char* desc) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, bg);
|
||||
ImGui::PushStyleColor(ImGuiCol_BorderShadow, bg);
|
||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, bg);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, fg);
|
||||
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
|
||||
ImGui::TextUnformatted(desc);
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
|
||||
ImGui::PopStyleColor(4);
|
||||
}
|
||||
|
||||
void HelpMarker(const char* desc) {
|
||||
ImGui::TextDisabled("(?)");
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
|
||||
ImGui::TextUnformatted(desc);
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
HelpTooltip(desc);
|
||||
}
|
||||
}
|
||||
|
||||
void WarnTooltip(const char* desc, const char* warn) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, bg);
|
||||
ImGui::PushStyleColor(ImGuiCol_BorderShadow, bg);
|
||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, bg);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, fg);
|
||||
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
|
||||
if (desc && desc[0]) {
|
||||
ImGui::TextUnformatted(desc);
|
||||
ImGui::TextUnformatted("");
|
||||
}
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 0.f, 1.f));
|
||||
if (warn && warn[0]) {
|
||||
ImGui::TextUnformatted("WARNING:");
|
||||
ImGui::TextUnformatted(warn);
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
|
||||
ImGui::PopStyleColor(4);
|
||||
}
|
||||
|
||||
void WarnMarker(const char* desc, const char* warn) {
|
||||
ImGui::PushStyleColor(ImGuiCol_TextDisabled, ImVec4(1.f, 1.f, 0.f, 1.f));
|
||||
ImGui::TextDisabled("(!)");
|
||||
ImGui::PopStyleColor();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
|
||||
if (desc) {
|
||||
ImGui::TextUnformatted(desc);
|
||||
ImGui::TextUnformatted("");
|
||||
}
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 0.f, 1.f));
|
||||
if (warn) {
|
||||
ImGui::TextUnformatted("WARNING:");
|
||||
ImGui::TextUnformatted(warn);
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
WarnTooltip(desc, warn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
namespace ImGui {
|
||||
|
||||
void HelpTooltip(const char* desc);
|
||||
void HelpMarker(const char* desc);
|
||||
void WarnTooltip(const char* desc, const char* warn);
|
||||
void WarnMarker(const char* desc, const char* warn);
|
||||
void DummyMarker();
|
||||
void Knob(float fraction, float size, float thickness = 2.f,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "cfg/configurator.h"
|
||||
#include "games/io.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "launcher/superexit.h"
|
||||
@@ -12,6 +13,16 @@
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
#if !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) || \
|
||||
!defined(IMGUI_DISABLE_DEFAULT_ALLOCATORS) || \
|
||||
!defined(IMGUI_USE_BGRA_PACKED_COLOR) || \
|
||||
!defined(IMGUI_HAS_VIEWPORT) || \
|
||||
!defined(IMGUI_HAS_DOCK) || \
|
||||
!defined(IMGUI_DISABLE_DEMO_WINDOWS) || \
|
||||
defined(IMGUI_DISABLE_DEBUG_TOOLS)
|
||||
#error "fix imconfig.h after updating imgui version"
|
||||
#endif
|
||||
|
||||
// state
|
||||
static HWND g_hWnd = nullptr;
|
||||
static INT64 g_Time = 0;
|
||||
@@ -42,6 +53,7 @@ bool ImGui_ImplSpice_Init(HWND hWnd) {
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos;
|
||||
io.BackendPlatformName = "imgui_impl_spice";
|
||||
io.ConfigErrorRecoveryEnableTooltip = true;
|
||||
|
||||
// keyboard mapping
|
||||
io.KeyMap[ImGuiKey_Tab] = VK_TAB;
|
||||
@@ -59,7 +71,7 @@ bool ImGui_ImplSpice_Init(HWND hWnd) {
|
||||
io.KeyMap[ImGuiKey_Space] = VK_SPACE;
|
||||
io.KeyMap[ImGuiKey_Enter] = VK_RETURN;
|
||||
io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE;
|
||||
io.KeyMap[ImGuiKey_KeyPadEnter] = VK_RETURN;
|
||||
io.KeyMap[ImGuiKey_KeypadEnter] = VK_RETURN;
|
||||
io.KeyMap[ImGuiKey_A] = 'A';
|
||||
io.KeyMap[ImGuiKey_C] = 'C';
|
||||
io.KeyMap[ImGuiKey_V] = 'V';
|
||||
@@ -349,27 +361,6 @@ void ImGui_ImplSpice_NewFrame() {
|
||||
}
|
||||
}
|
||||
|
||||
// navigator input
|
||||
auto buttons = games::get_buttons_overlay(eamuse_get_game());
|
||||
if (buttons && (!overlay::OVERLAY || overlay::OVERLAY->hotkeys_triggered())) {
|
||||
struct {
|
||||
size_t index;
|
||||
Button &btn;
|
||||
} NAV_MAPPING[] = {
|
||||
{ ImGuiNavInput_Activate, buttons->at(games::OverlayButtons::NavigatorActivate )},
|
||||
{ ImGuiNavInput_Cancel, buttons->at(games::OverlayButtons::NavigatorCancel) },
|
||||
{ ImGuiNavInput_DpadUp, buttons->at(games::OverlayButtons::NavigatorUp) },
|
||||
{ ImGuiNavInput_DpadDown, buttons->at(games::OverlayButtons::NavigatorDown) },
|
||||
{ ImGuiNavInput_DpadLeft, buttons->at(games::OverlayButtons::NavigatorLeft) },
|
||||
{ ImGuiNavInput_DpadRight, buttons->at(games::OverlayButtons::NavigatorRight) },
|
||||
};
|
||||
for (auto mapping : NAV_MAPPING) {
|
||||
if (GameAPI::Buttons::getState(RI_MGR, mapping.btn)) {
|
||||
io.NavInputs[mapping.index] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set mouse wheel
|
||||
auto mouse_diff = mouse_wheel - mouse_wheel_last;
|
||||
mouse_wheel_last = mouse_wheel;
|
||||
@@ -378,10 +369,27 @@ void ImGui_ImplSpice_NewFrame() {
|
||||
// update OS mouse position
|
||||
ImGui_ImplSpice_UpdateMousePos();
|
||||
|
||||
// update OS mouse cursor with the cursor requested by imgui
|
||||
ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
|
||||
if (g_LastMouseCursor != mouse_cursor) {
|
||||
g_LastMouseCursor = mouse_cursor;
|
||||
ImGui_ImplSpice_UpdateMouseCursor();
|
||||
if (cfg::CONFIGURATOR_STANDALONE) {
|
||||
// if cursor is inside the client area, always set the OS cursor to what ImGui wants
|
||||
// this is to deal with cases where mouse cursor changes outside the client rect and comes
|
||||
// back into the window
|
||||
// i'm sure there might be better ways to deal with this but this works so whatever, right?
|
||||
RECT client_rect;
|
||||
if (GetClientRect(g_hWnd, &client_rect)) {
|
||||
POINT cursor;
|
||||
if (GetCursorPos(&cursor) && ScreenToClient(g_hWnd, &cursor)) {
|
||||
if (client_rect.left < cursor.x && cursor.x < client_rect.right &&
|
||||
client_rect.top < cursor.y && cursor.y < client_rect.bottom) {
|
||||
ImGui_ImplSpice_UpdateMouseCursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// update OS mouse cursor with the cursor requested by imgui
|
||||
ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
|
||||
if (g_LastMouseCursor != mouse_cursor) {
|
||||
g_LastMouseCursor = mouse_cursor;
|
||||
ImGui_ImplSpice_UpdateMouseCursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -678,7 +678,7 @@ void bind_imgui_painting()
|
||||
int font_width, font_height;
|
||||
io.Fonts->GetTexDataAsAlpha8(&tex_data, &font_width, &font_height);
|
||||
const auto texture = new Texture{tex_data, font_width, font_height};
|
||||
io.Fonts->TexID = texture;
|
||||
io.Fonts->TexID = reinterpret_cast<ImTextureID>(texture);
|
||||
}
|
||||
|
||||
static Stats s_stats; // TODO: pass as an argument?
|
||||
|
||||
@@ -7,14 +7,12 @@
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "touch/touch.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/resutils.h"
|
||||
#include "build/resource.h"
|
||||
|
||||
#include "imgui/impl_dx9.h"
|
||||
#include "imgui/impl_spice.h"
|
||||
#include "imgui/impl_sw.h"
|
||||
#include "overlay/imgui/impl_dx9.h"
|
||||
#include "external/imgui/backends/imgui_impl_dx9.h"
|
||||
#include "overlay/imgui/impl_spice.h"
|
||||
#include "overlay/imgui/impl_sw.h"
|
||||
|
||||
@@ -62,6 +60,7 @@ namespace overlay {
|
||||
std::mutex OVERLAY_MUTEX;
|
||||
std::unique_ptr<overlay::SpiceOverlay> OVERLAY = nullptr;
|
||||
ImFont* DSEG_FONT = nullptr;
|
||||
bool SHOW_DEBUG_LOG_WINDOW = false;
|
||||
}
|
||||
|
||||
static void *ImGui_Alloc(size_t sz, void *user_data) {
|
||||
@@ -242,13 +241,20 @@ void overlay::SpiceOverlay::init() {
|
||||
io.UserData = this;
|
||||
io.ConfigFlags = ImGuiConfigFlags_NavEnableKeyboard
|
||||
| ImGuiConfigFlags_NavEnableGamepad
|
||||
| ImGuiConfigFlags_NavEnableSetMousePos
|
||||
| ImGuiConfigFlags_DockingEnable
|
||||
| ImGuiConfigFlags_ViewportsEnable;
|
||||
if (is_touch_available()) {
|
||||
| ImGuiConfigFlags_NavEnableSetMousePos;
|
||||
|
||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
|
||||
}
|
||||
if (is_touch_available("SpiceOverlay::init")) {
|
||||
io.ConfigFlags |= ImGuiConfigFlags_IsTouchScreen;
|
||||
}
|
||||
|
||||
// temporarily turn this off as it can cause crashes during font load failures
|
||||
// turns back on in ImGui_ImplSpice_Init
|
||||
io.ConfigErrorRecoveryEnableTooltip = false;
|
||||
|
||||
io.MouseDrawCursor = !GRAPHICS_SHOW_CURSOR;
|
||||
|
||||
// disable config
|
||||
@@ -263,20 +269,14 @@ void overlay::SpiceOverlay::init() {
|
||||
// add fallback fonts for missing glyph ranges
|
||||
ImFontConfig config {};
|
||||
config.MergeMode = true;
|
||||
io.Fonts->AddFontFromFileTTF(R"(C:\Windows\Fonts\simsun.ttc)",
|
||||
13.0f, &config, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
|
||||
io.Fonts->AddFontFromFileTTF(R"(C:\Windows\Fonts\arial.ttf)",
|
||||
13.0f, &config, io.Fonts->GetGlyphRangesCyrillic());
|
||||
io.Fonts->AddFontFromFileTTF(R"(C:\Windows\Fonts\meiryu.ttc)",
|
||||
13.0f, &config, io.Fonts->GetGlyphRangesJapanese());
|
||||
io.Fonts->AddFontFromFileTTF(R"(C:\Windows\Fonts\meiryo.ttc)",
|
||||
13.0f, &config, io.Fonts->GetGlyphRangesJapanese());
|
||||
io.Fonts->AddFontFromFileTTF(R"(C:\Windows\Fonts\gulim.ttc)",
|
||||
13.0f, &config, io.Fonts->GetGlyphRangesKorean());
|
||||
io.Fonts->AddFontFromFileTTF(R"(C:\Windows\Fonts\cordia.ttf)",
|
||||
13.0f, &config, io.Fonts->GetGlyphRangesThai());
|
||||
io.Fonts->AddFontFromFileTTF(R"(C:\Windows\Fonts\arial.ttf)",
|
||||
13.0f, &config, io.Fonts->GetGlyphRangesVietnamese());
|
||||
|
||||
add_font("simsun.ttc", &config, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
|
||||
add_font("arial.ttc", &config, io.Fonts->GetGlyphRangesCyrillic());
|
||||
add_font("meiryu.ttc", &config, io.Fonts->GetGlyphRangesJapanese());
|
||||
add_font("meiryo.ttc", &config, io.Fonts->GetGlyphRangesJapanese());
|
||||
add_font("gulim.ttc", &config, io.Fonts->GetGlyphRangesKorean());
|
||||
add_font("cordia.ttc", &config, io.Fonts->GetGlyphRangesThai());
|
||||
add_font("arial.ttc", &config, io.Fonts->GetGlyphRangesVietnamese());
|
||||
|
||||
// add special font
|
||||
if (avs::game::is_model("LDJ")) {
|
||||
@@ -458,6 +458,10 @@ void overlay::SpiceOverlay::new_frame() {
|
||||
window->build();
|
||||
}
|
||||
|
||||
if (SHOW_DEBUG_LOG_WINDOW) {
|
||||
ImGui::ShowDebugLogWindow(&SHOW_DEBUG_LOG_WINDOW);
|
||||
}
|
||||
|
||||
// end frame
|
||||
ImGui::EndFrame();
|
||||
}
|
||||
@@ -693,3 +697,21 @@ uint32_t *overlay::SpiceOverlay::sw_get_pixel_data(int *width, int *height) {
|
||||
*height = this->pixel_data_height;
|
||||
return &this->pixel_data[0];
|
||||
}
|
||||
|
||||
void overlay::SpiceOverlay::add_font(const char* font, ImFontConfig* config, const ImWchar* glyphs) {
|
||||
CHAR fonts_dir[MAX_PATH];
|
||||
ExpandEnvironmentStringsA(R"(%SYSTEMROOT%\Fonts\)", fonts_dir, MAX_PATH);
|
||||
std::filesystem::path full_path = fonts_dir;
|
||||
full_path += font;
|
||||
|
||||
if (fileutils::file_exists(full_path)) {
|
||||
log_misc("overlay", "loading font: {}", full_path.string());
|
||||
ImGui::GetIO().Fonts->AddFontFromFileTTF(
|
||||
full_path.string().c_str(),
|
||||
13.0f,
|
||||
config,
|
||||
glyphs);
|
||||
} else {
|
||||
log_misc("overlay", "font not found: {}", full_path.string());
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ namespace overlay {
|
||||
extern bool AUTO_SHOW_KEYPAD_P2;
|
||||
extern bool USE_WM_CHAR_FOR_IMGUI_CHAR_INPUT;
|
||||
extern bool FPS_SHOULD_FLIP;
|
||||
extern bool SHOW_DEBUG_LOG_WINDOW;
|
||||
|
||||
class SpiceOverlay {
|
||||
public:
|
||||
@@ -128,6 +129,7 @@ namespace overlay {
|
||||
bool hotkey_toggle_last = false;
|
||||
|
||||
void init();
|
||||
void add_font(const char* font, ImFontConfig* config, const ImWchar* glyphs);
|
||||
};
|
||||
|
||||
// global
|
||||
|
||||
@@ -99,10 +99,9 @@ void overlay::Window::build() {
|
||||
for (auto &child : this->children) {
|
||||
child->build();
|
||||
}
|
||||
|
||||
// end window
|
||||
ImGui::End();
|
||||
}
|
||||
// end window
|
||||
ImGui::End();
|
||||
|
||||
if (this->remove_window_padding) {
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "external/imgui/imgui.h"
|
||||
#include "external/imgui/imgui_stdlib.h"
|
||||
#include "external/imgui/misc/cpp/imgui_stdlib.h"
|
||||
#include "overlay.h"
|
||||
|
||||
namespace overlay {
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "external/rapidjson/prettywriter.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "misc/clipboard.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "cfg/configurator.h"
|
||||
#include "overlay/imgui/extensions.h"
|
||||
|
||||
using namespace rapidjson;
|
||||
|
||||
@@ -27,19 +29,76 @@ 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();
|
||||
}
|
||||
|
||||
// load -card0 / -card1
|
||||
// -card0 / -card1 override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(CARD_OVERRIDES_LOCK);
|
||||
if (!CARD_OVERRIDES[0].empty()) {
|
||||
const CardEntry card0 = {
|
||||
.name = "P1 Default (-card0)",
|
||||
.id = CARD_OVERRIDES[0],
|
||||
.search_string = "p1 default (-card0)",
|
||||
.read_only = true,
|
||||
.color = {0.9f, 0.9f, 0.9f}
|
||||
};
|
||||
card_cmd_overrides[0].emplace(card0);
|
||||
this->loaded_card[0] = card0;
|
||||
}
|
||||
if (eamuse_get_game_keypads() > 1 && !CARD_OVERRIDES[1].empty()) {
|
||||
const CardEntry card1 = {
|
||||
.name = "P2 Default (-card1)",
|
||||
.id = CARD_OVERRIDES[1],
|
||||
.search_string = "p2 default (-card1)",
|
||||
.read_only = true,
|
||||
.color = {0.9f, 0.9f, 0.9f}
|
||||
};
|
||||
card_cmd_overrides[1].emplace(card1);
|
||||
this->loaded_card[1] = card1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CardManager::~CardManager() {
|
||||
}
|
||||
|
||||
void CardManager::build_content() {
|
||||
ImGui::SeparatorText("Selected card");
|
||||
build_card();
|
||||
ImGui::SeparatorText("Available cards");
|
||||
ImGui::TextColored(ImVec4(1, 0.7f, 0, 1), "Active card overrides");
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker(
|
||||
"Click to insert card now, or press Insert Card key. Auto Card Insert will also use these cards.\n\n"
|
||||
"If no override is set, pressing Insert Card will read from card0.txt / card1.txt.");
|
||||
if (ImGui::BeginTable("CardSetTable", eamuse_get_game_keypads() > 1 ? 2 : 1, ImGuiTableFlags_SizingFixedFit)) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("Player 1");
|
||||
if (eamuse_get_game_keypads() > 1) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("Player 2");
|
||||
}
|
||||
ImGui::TableNextRow();
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
if (eamuse_get_game_keypads() > (int)i) {
|
||||
ImGui::TableNextColumn();
|
||||
if (build_card(i) && this->loaded_card[i].has_value()) {
|
||||
insert_card_over_api(i, this->loaded_card[i].value());
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::TextColored(ImVec4(1, 0.7f, 0, 1), "Available cards");
|
||||
build_card_list();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
@@ -48,44 +107,13 @@ namespace overlay::windows {
|
||||
build_card_editor();
|
||||
}
|
||||
|
||||
void CardManager::build_card() {
|
||||
ImGui::BeginDisabled(this->current_card == nullptr);
|
||||
// insert P1 button
|
||||
if (ImGui::Button("Insert P1")) {
|
||||
const auto card = this->current_card;
|
||||
uint8_t card_bin[8];
|
||||
if (card && card->id.length() == 16 && hex2bin(card->id.c_str(), card_bin)) {
|
||||
eamuse_card_insert(0, card_bin);
|
||||
}
|
||||
}
|
||||
|
||||
// insert P2 button
|
||||
if (eamuse_get_game_keypads() > 1) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Insert P2")) {
|
||||
const auto card = this->current_card;
|
||||
uint8_t card_bin[8];
|
||||
if (card && card->id.length() == 16 && hex2bin(card->id.c_str(), card_bin)) {
|
||||
eamuse_card_insert(1, card_bin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// edit selected card
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Edit Card")) {
|
||||
open_card_editor();
|
||||
}
|
||||
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
// card ui
|
||||
if (this->current_card) {
|
||||
const auto card = this->current_card;
|
||||
const ImVec4 color(card->color[0], card->color[1], card->color[2], 1.f);
|
||||
float bg_luminance = (0.299f * card->color[0] + 0.587 * card->color[1] + 0.114 * card->color[2]);
|
||||
bool CardManager::build_card(int reader) {
|
||||
ImGui::PushID(reader);
|
||||
bool clicked = false;
|
||||
if (this->loaded_card[reader].has_value()) {
|
||||
const auto &card = this->loaded_card[reader].value();
|
||||
const ImVec4 color(card.color[0], card.color[1], card.color[2], 1.f);
|
||||
float bg_luminance = (0.299f * card.color[0] + 0.587 * card.color[1] + 0.114 * card.color[2]);
|
||||
|
||||
// text color
|
||||
ImVec4 text_color;
|
||||
@@ -101,22 +129,25 @@ namespace overlay::windows {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, text_color);
|
||||
if (ImGui::Button(fmt::format(
|
||||
" {} \n {} {} {} {} ",
|
||||
card->name.empty() ? "<blank>" : card->name.substr(0, 19),
|
||||
card->id.substr(0, 4).c_str(),
|
||||
card->id.substr(4, 4).c_str(),
|
||||
card->id.substr(8, 4).c_str(),
|
||||
card->id.substr(12, 4).c_str()
|
||||
card.name.empty() ? "<blank>" : card.name.substr(0, 19),
|
||||
card.id.substr(0, 4).c_str(),
|
||||
card.id.substr(4, 4).c_str(),
|
||||
card.id.substr(8, 4).c_str(),
|
||||
card.id.substr(12, 4).c_str()
|
||||
).c_str())) {
|
||||
|
||||
open_card_editor();
|
||||
clicked = true;
|
||||
}
|
||||
ImGui::PopStyleColor(4);
|
||||
} else {
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::Button(" <No card> \n xxxx xxxx xxxx xxxx ");
|
||||
ImGui::Button(" (No override set) \n"
|
||||
" xxxx xxxx xxxx xxxx ");
|
||||
ImGui::EndDisabled();
|
||||
ImGui::PopStyleColor(4);
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
return clicked;
|
||||
}
|
||||
|
||||
void CardManager::open_card_editor() {
|
||||
@@ -140,13 +171,18 @@ namespace overlay::windows {
|
||||
this->card_buffer,
|
||||
std::size(this->card_buffer),
|
||||
ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase);
|
||||
ImGui::EndDisabled();
|
||||
if (this->current_card == nullptr) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Generate")) {
|
||||
generate_ea_card(this->card_buffer);
|
||||
}
|
||||
} else {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Copy")) {
|
||||
clipboard::copy_text(this->current_card->id);
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
// name field
|
||||
ImGui::InputTextWithHint("Card Name", "Main Card",
|
||||
@@ -171,6 +207,18 @@ namespace overlay::windows {
|
||||
this->current_card->color[1] = this->color_buffer[1];
|
||||
this->current_card->color[2] = this->color_buffer[2];
|
||||
generate_search_string(this->current_card);
|
||||
|
||||
// ensure loaded cards are kept up to date
|
||||
// note: does not handle cases where multiple cards have the same ID
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
if (this->loaded_card[i].has_value() &&
|
||||
!this->loaded_card[i].value().read_only &&
|
||||
this->loaded_card[i].value().id == this->current_card->id) {
|
||||
this->loaded_card[i] = *this->current_card;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// create a new card
|
||||
CardEntry card {
|
||||
@@ -211,14 +259,54 @@ namespace overlay::windows {
|
||||
}
|
||||
}
|
||||
|
||||
void CardManager::build_card_selectable(CardEntry &card) {
|
||||
// generate card name
|
||||
std::string card_name = "";
|
||||
if (card.id.length() == 16) {
|
||||
card_name += card.id.substr(0, 4);
|
||||
card_name += " ";
|
||||
card_name += card.id.substr(4, 4);
|
||||
card_name += " ";
|
||||
card_name += card.id.substr(8, 4);
|
||||
card_name += " ";
|
||||
card_name += card.id.substr(12, 4);
|
||||
} else {
|
||||
card_name += card.id;
|
||||
}
|
||||
if (!card.name.empty()) {
|
||||
card_name += " - ";
|
||||
card_name += card.name;
|
||||
}
|
||||
|
||||
ImGui::PushID(&card);
|
||||
|
||||
// color button
|
||||
ImVec4 color(card.color[0], card.color[1], card.color[2], 1.f);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, color);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, color);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, color);
|
||||
ImGui::SmallButton(" ");
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
// selectable item
|
||||
if (ImGui::Selectable(card_name.c_str(), this->current_card == &card)) {
|
||||
this->current_card = &card;
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void CardManager::build_card_list() {
|
||||
|
||||
// search for card
|
||||
//
|
||||
// setting ImGuiInputTextFlags_CallbackCharFilter and pressing escape doesn't cause below
|
||||
// to return true, making it necessary to provide a callback...
|
||||
ImGui::SetNextItemWidth(220);
|
||||
ImGui::SetNextItemWidth(240);
|
||||
if (ImGui::InputTextWithHint("", "Type here to search..", &this->search_filter)) {
|
||||
this->current_card = nullptr;
|
||||
this->search_filter_in_lower_case = strtolower(this->search_filter);
|
||||
}
|
||||
if (!this->search_filter.empty()) {
|
||||
@@ -227,11 +315,49 @@ namespace overlay::windows {
|
||||
this->search_filter.clear();
|
||||
this->search_filter_in_lower_case.clear();
|
||||
}
|
||||
} else {
|
||||
ImGui::SameLine();
|
||||
|
||||
// move selected up/down the list
|
||||
ImGui::BeginDisabled(this->current_card == nullptr);
|
||||
}
|
||||
|
||||
// toolbar
|
||||
|
||||
// set card as p1/p2
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
if (eamuse_get_game_keypads() > (int)i) {
|
||||
if (i != 0) {
|
||||
ImGui::SameLine();
|
||||
}
|
||||
ImGui::PushID(i);
|
||||
ImGui::BeginDisabled(this->current_card == nullptr);
|
||||
if (ImGui::Button(i == 0 ? "Load P1" : "Load P2") && this->current_card) {
|
||||
this->loaded_card[i] = *this->current_card;
|
||||
log_info(
|
||||
"cardmanager",
|
||||
"[P{}] update override and insert card: {} ({})",
|
||||
i+1,
|
||||
this->current_card->id,
|
||||
this->current_card->name
|
||||
);
|
||||
|
||||
// update override
|
||||
std::lock_guard<std::mutex> lock(CARD_OVERRIDES_LOCK);
|
||||
CARD_OVERRIDES[i] = this->current_card->id;
|
||||
|
||||
// insert card over api
|
||||
insert_card_over_api(i, this->loaded_card[i].value());
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
|
||||
// edit selected card
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginDisabled(this->current_card == nullptr || this->current_card->read_only);
|
||||
if (ImGui::Button("Edit")) {
|
||||
open_card_editor();
|
||||
}
|
||||
|
||||
// move selected up/down the list
|
||||
if (this->search_filter.empty()) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Move Up")) {
|
||||
for (auto it = this->cards.begin(); it != this->cards.end(); ++it) {
|
||||
@@ -254,60 +380,37 @@ namespace overlay::windows {
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
// cards list
|
||||
// use all available vertical space, minus height of buttons, minus separator
|
||||
// use all available vertical space, minus height footer (a row of buttons and separator)
|
||||
if (ImGui::BeginChild(
|
||||
"cards",
|
||||
ImVec2(0, ImGui::GetContentRegionAvail().y - ImGui::GetFrameHeightWithSpacing() - 8.f))) {
|
||||
|
||||
// -card0 / -card1 override
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
if (card_cmd_overrides[i].has_value()) {
|
||||
build_card_selectable(card_cmd_overrides[i].value());
|
||||
}
|
||||
}
|
||||
|
||||
// cards from card manager JSON
|
||||
for (auto &card : this->cards) {
|
||||
|
||||
// get card name
|
||||
std::string card_name = "";
|
||||
if (card.id.length() == 16) {
|
||||
card_name += card.id.substr(0, 4);
|
||||
card_name += " ";
|
||||
card_name += card.id.substr(4, 4);
|
||||
card_name += " ";
|
||||
card_name += card.id.substr(8, 4);
|
||||
card_name += " ";
|
||||
card_name += card.id.substr(12, 4);
|
||||
} else {
|
||||
card_name += card.id;
|
||||
}
|
||||
if (!card.name.empty()) {
|
||||
card_name += " - ";
|
||||
card_name += card.name;
|
||||
}
|
||||
|
||||
|
||||
if (!this->search_filter_in_lower_case.empty() && !card.search_string.empty()) {
|
||||
const bool matched =
|
||||
card.search_string.find(this->search_filter_in_lower_case) != std::string::npos;
|
||||
|
||||
card.search_string.find(this->search_filter_in_lower_case) != std::string::npos;
|
||||
|
||||
if (!matched) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// draw entry
|
||||
ImGui::PushID(&card);
|
||||
|
||||
ImVec4 color(card.color[0], card.color[1], card.color[2], 1.f);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, color);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, color);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, color);
|
||||
ImGui::SmallButton(" ");
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Selectable(card_name.c_str(), this->current_card == &card)) {
|
||||
this->current_card = &card;
|
||||
}
|
||||
ImGui::PopID();
|
||||
build_card_selectable(card);
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
@@ -334,8 +437,6 @@ namespace overlay::windows {
|
||||
}
|
||||
|
||||
void CardManager::config_load() {
|
||||
log_info("cardmanager", "loading config");
|
||||
|
||||
// clear cards
|
||||
this->cards.clear();
|
||||
|
||||
@@ -466,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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,4 +605,11 @@ namespace overlay::windows {
|
||||
this->color_buffer[1] = g / 255.f;
|
||||
this->color_buffer[2] = b / 255.f;
|
||||
}
|
||||
|
||||
void CardManager::insert_card_over_api(int reader, CardEntry &card) {
|
||||
uint8_t card_bin[8];
|
||||
if (card.id.length() == 16 && hex2bin(card.id.c_str(), card_bin)) {
|
||||
eamuse_card_insert(reader, card_bin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
|
||||
#include "overlay/window.h"
|
||||
|
||||
@@ -10,6 +11,7 @@ namespace overlay::windows {
|
||||
std::string name = "unnamed";
|
||||
std::string id = "E004010000000000";
|
||||
std::string search_string = "";
|
||||
bool read_only = false;
|
||||
float color[3] {};
|
||||
};
|
||||
|
||||
@@ -29,7 +31,10 @@ namespace overlay::windows {
|
||||
char name_buffer[65] {};
|
||||
char card_buffer[17] {};
|
||||
float color_buffer[3] {};
|
||||
|
||||
std::optional<CardEntry> card_cmd_overrides[2];
|
||||
|
||||
std::optional<CardEntry> loaded_card[2];
|
||||
CardEntry *current_card = nullptr;
|
||||
|
||||
std::string search_filter = "";
|
||||
@@ -41,10 +46,13 @@ namespace overlay::windows {
|
||||
void generate_search_string(CardEntry *card);
|
||||
void generate_random_color();
|
||||
|
||||
void build_card();
|
||||
bool build_card(int reader);
|
||||
void open_card_editor();
|
||||
void build_card_editor();
|
||||
void build_card_list();
|
||||
void build_card_selectable(CardEntry &card);
|
||||
void build_footer();
|
||||
|
||||
void insert_card_over_api(int reader, CardEntry &card);
|
||||
};
|
||||
}
|
||||
|
||||
+1872
-1787
File diff suppressed because it is too large
Load Diff
@@ -178,9 +178,11 @@ namespace overlay::windows {
|
||||
static_cast<int>(ImGui::GetIO().DisplaySize.x),
|
||||
static_cast<int>(ImGui::GetIO().DisplaySize.y));
|
||||
|
||||
// removed for size (along with setting IMGUI_DISABLE_DEMO_WINDOWS
|
||||
// and IMGUI_DISABLE_DEBUG_TOOLS) - saves about 300kb in each
|
||||
// binary
|
||||
if (ImGui::Button("ImGui Debug Log")) {
|
||||
overlay::SHOW_DEBUG_LOG_WINDOW = !overlay::SHOW_DEBUG_LOG_WINDOW;
|
||||
}
|
||||
|
||||
// removed for size (IMGUI_DISABLE_DEMO_WINDOWS)
|
||||
|
||||
// metrics button
|
||||
// this->metrics_open |= ImGui::Button("Metrics Window");
|
||||
@@ -749,7 +751,7 @@ namespace overlay::windows {
|
||||
if (ImGui::CollapsingHeader("Touch")) {
|
||||
|
||||
// status
|
||||
ImGui::Text("Status: %s", is_touch_available() ? "available" : "unavailable");
|
||||
ImGui::Text("Status: %s", is_touch_available("Control::touch_view") ? "available" : "unavailable");
|
||||
|
||||
// touch points
|
||||
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
|
||||
|
||||
@@ -206,7 +206,7 @@ namespace overlay::windows {
|
||||
bottom_right.x += overlay_content_top_left.x;
|
||||
bottom_right.y += overlay_content_top_left.y;
|
||||
ImGui::GetBackgroundDrawList()->AddImage(
|
||||
reinterpret_cast<void *>(this->texture),
|
||||
reinterpret_cast<ImTextureID>(this->texture),
|
||||
overlay_content_top_left,
|
||||
bottom_right);
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ namespace overlay::windows {
|
||||
url_fetch_errors += fmt::format("WinHttpSendRequest failed: {}\n", gle);
|
||||
if (gle == 12175) {
|
||||
url_fetch_errors += "\nThis is ERROR_WINHTTP_SECURE_FAILURE - most likely TLS 1.1 / TLS 1.2 error on old OS versions.\n\n";
|
||||
url_fetch_errors += "Look up MSDN article on 'Update to enable TLS 1.1 and TLS 1.2 as default secure protocols in WinHTTP in Windows' for a fix.";
|
||||
url_fetch_errors += "Look up MSDN article on 'Update to enable TLS 1.1 and TLS 1.2 as default secure protocols in WinHTTP in Windows' for a fix.\n";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -183,7 +183,7 @@ namespace overlay::windows {
|
||||
statusCode,
|
||||
netutils::http_status_reason_phrase(statusCode));
|
||||
if (statusCode == 404) {
|
||||
url_fetch_errors += "(Patch server did not find any patches for this game version)";
|
||||
url_fetch_errors += "(Patch server did not find any patches for this game version)\n";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -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) {
|
||||
@@ -550,7 +551,7 @@ namespace overlay::windows {
|
||||
ImGui::Text("Failed to import patches from URL.");
|
||||
if (!url_fetch_errors.empty()) {
|
||||
ImGui::TextUnformatted("");
|
||||
ImGui::PushTextWrapPos(ImGui::GetIO().DisplaySize.x * 0.5);
|
||||
ImGui::PushTextWrapPos(ImGui::GetIO().DisplaySize.x * 0.7);
|
||||
ImGui::TextUnformatted(url_fetch_errors.c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
}
|
||||
@@ -558,6 +559,12 @@ namespace overlay::windows {
|
||||
if (ImGui::Button("OK")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
if (!url_fetch_errors.empty()) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Copy Error")) {
|
||||
clipboard::copy_text(url_fetch_errors);
|
||||
}
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
@@ -649,17 +656,9 @@ namespace overlay::windows {
|
||||
|
||||
// first column, part 1: help / caution marker
|
||||
ImGui::TableNextColumn();
|
||||
const std::string description = patch.description;
|
||||
const std::string caution = patch.caution;
|
||||
if (!description.empty() && !caution.empty()) {
|
||||
if (!patch.caution.empty()) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::WarnMarker(description.c_str(), caution.c_str());
|
||||
} else if (!description.empty()) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::HelpMarker(description.c_str());
|
||||
} else if (!caution.empty()) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::WarnMarker(nullptr, caution.c_str());
|
||||
ImGui::WarnMarker(patch.description.c_str(), patch.caution.c_str());
|
||||
} else {
|
||||
ImGui::DummyMarker();
|
||||
}
|
||||
@@ -705,6 +704,11 @@ namespace overlay::windows {
|
||||
if (style_color_pushed) {
|
||||
ImGui::PopStyleColor(style_color_pushed);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
show_patch_tooltip(patch);
|
||||
}
|
||||
|
||||
// show range after label for integer patches
|
||||
if (patch.type == PatchType::Integer) {
|
||||
ImGui::SameLine();
|
||||
auto& numpatch = patch.patch_number;
|
||||
@@ -741,6 +745,9 @@ namespace overlay::windows {
|
||||
patch.last_status = is_patch_active(patch);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
show_patch_tooltip(patch);
|
||||
}
|
||||
|
||||
// second column, part 2: additional options UI (dropdown, text input)
|
||||
ImGui::SameLine();
|
||||
@@ -767,6 +774,9 @@ namespace overlay::windows {
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
show_patch_tooltip(patch);
|
||||
}
|
||||
} else if (patch.type == PatchType::Integer) {
|
||||
ImGui::SetNextItemWidth(200.0f);
|
||||
auto& numpatch = patch.patch_number;
|
||||
@@ -780,6 +790,9 @@ namespace overlay::windows {
|
||||
apply_patch(patch, true);
|
||||
config_dirty = true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
show_patch_tooltip(patch);
|
||||
}
|
||||
}
|
||||
} else if (patch_status == PatchStatus::Disabled) {
|
||||
ImGui::SetNextItemWidth(200.0f);
|
||||
@@ -794,6 +807,9 @@ namespace overlay::windows {
|
||||
ImGui::InputInt("##dummy_int_input", &patch.patch_number.value);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
show_patch_tooltip(patch);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
@@ -822,6 +838,14 @@ namespace overlay::windows {
|
||||
}
|
||||
}
|
||||
|
||||
void PatchManager::show_patch_tooltip(const PatchData& patch) {
|
||||
if (!patch.caution.empty()) {
|
||||
ImGui::WarnTooltip(patch.description.c_str(), patch.caution.c_str());
|
||||
} else if (!patch.description.empty()) {
|
||||
ImGui::HelpTooltip(patch.description.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void PatchManager::hard_apply_patches() {
|
||||
std::vector<std::string> written_list;
|
||||
for (auto& patch : patches) {
|
||||
@@ -1104,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");
|
||||
@@ -1500,16 +1524,44 @@ namespace overlay::windows {
|
||||
try {
|
||||
auto patches_json = getFromUrl(dll_name, json_path);
|
||||
if (!patches_json.empty()) {
|
||||
if (!fileutils::dir_exists(LOCAL_PATCHES_PATH))
|
||||
|
||||
// see if this is valid JSON
|
||||
Document doc_temp;
|
||||
doc_temp.Parse(patches_json.c_str());
|
||||
const auto error = doc_temp.GetParseError();
|
||||
if (error) {
|
||||
log_warning(
|
||||
"patchmanager",
|
||||
"remotely fetched JSON file parse error: {}",
|
||||
rapidjson::GetParseError_En(error));
|
||||
url_fetch_errors += fmt::format(
|
||||
"Invalid JSON received from remote URL.\n"
|
||||
"Your DLL version might not be supported.\n"
|
||||
"URL: {}\n"
|
||||
"JSON Parse Error: {}\n",
|
||||
json_path,
|
||||
rapidjson::GetParseError_En(error));
|
||||
return false;
|
||||
} else {
|
||||
log_info("patchmanager", "remotely fetched JSON was successfully parsed");
|
||||
}
|
||||
|
||||
// create patches dir
|
||||
if (!fileutils::dir_exists(LOCAL_PATCHES_PATH)) {
|
||||
fileutils::dir_create(LOCAL_PATCHES_PATH);
|
||||
}
|
||||
// save to file
|
||||
std::filesystem::path save_path = LOCAL_PATCHES_PATH / (identifier + ".json");
|
||||
fileutils::text_write(save_path, patches_json);
|
||||
log_info("patchmanager", "remotely fetched JSON saved to: {}", save_path.string());
|
||||
return true;
|
||||
} else {
|
||||
log_warning("patchmanager", "failed to fetch patches JSON for {}", dll_name);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
log_warning("patchmanager", "exception occurred while loading remote patches JSON for {}: {}", dll_name, e.what());
|
||||
url_fetch_errors += fmt::format(
|
||||
"Exception while loading remote patches for {}: {}\n", dll_name, e.what());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -142,6 +142,7 @@ namespace overlay::windows {
|
||||
std::function<bool(const PatchData&)> filter = std::function<bool(const PatchData&)>(),
|
||||
std::string pe_identifier_for_patch = "");
|
||||
|
||||
void show_patch_tooltip(const PatchData& patch);
|
||||
};
|
||||
|
||||
PatchStatus is_patch_active(PatchData &patch);
|
||||
|
||||
@@ -17,6 +17,10 @@ namespace overlay::windows {
|
||||
this->init_pos = ImVec2(10, 10);
|
||||
this->toggle_button = games::OverlayButtons::ToggleScreenResize;
|
||||
this->toggle_screen_resize = games::OverlayButtons::ScreenResize;
|
||||
this->toggle_scene[0] = games::OverlayButtons::ScreenResizeScene1;
|
||||
this->toggle_scene[1] = games::OverlayButtons::ScreenResizeScene2;
|
||||
this->toggle_scene[2] = games::OverlayButtons::ScreenResizeScene3;
|
||||
this->toggle_scene[3] = games::OverlayButtons::ScreenResizeScene4;
|
||||
}
|
||||
|
||||
ScreenResize::~ScreenResize() {
|
||||
@@ -42,13 +46,16 @@ namespace overlay::windows {
|
||||
|
||||
void ScreenResize::reset_vars_to_default() {
|
||||
cfg::SCREENRESIZE->enable_screen_resize = false;
|
||||
cfg::SCREENRESIZE->screen_resize_current_scene = 0;
|
||||
cfg::SCREENRESIZE->enable_linear_filter = true;
|
||||
cfg::SCREENRESIZE->keep_aspect_ratio = true;
|
||||
cfg::SCREENRESIZE->centered = true;
|
||||
cfg::SCREENRESIZE->offset_x = 0;
|
||||
cfg::SCREENRESIZE->offset_y = 0;
|
||||
cfg::SCREENRESIZE->scale_x = 1.f;
|
||||
cfg::SCREENRESIZE->scale_y = 1.f;
|
||||
for (size_t i = 0; i < std::size(cfg::SCREENRESIZE->scene_settings); i++) {
|
||||
auto& scene = cfg::SCREENRESIZE->scene_settings[i];
|
||||
scene.keep_aspect_ratio = true;
|
||||
scene.offset_x = 0;
|
||||
scene.offset_y = 0;
|
||||
scene.scale_x = 1.f;
|
||||
scene.scale_y = 1.f;
|
||||
}
|
||||
|
||||
cfg::SCREENRESIZE->enable_window_resize = false;
|
||||
cfg::SCREENRESIZE->window_always_on_top = false;
|
||||
@@ -88,28 +95,58 @@ namespace overlay::windows {
|
||||
|
||||
void ScreenResize::build_fullscreen_config() {
|
||||
// enable checkbox
|
||||
ImGui::TextWrapped("Hint: bind a key to Screen Resize for quickly toggling this on/off.");
|
||||
ImGui::Checkbox("Enable", &cfg::SCREENRESIZE->enable_screen_resize);
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker("Hint: bind a key to Screen Resize for a quick toggle.");
|
||||
|
||||
ImGui::BeginDisabled(!cfg::SCREENRESIZE->enable_screen_resize);
|
||||
ImGui::Checkbox("Linear Filter", &cfg::SCREENRESIZE->enable_linear_filter);
|
||||
|
||||
if (ImGui::RadioButton("Scene 1", cfg::SCREENRESIZE->screen_resize_current_scene == 0)) {
|
||||
cfg::SCREENRESIZE->screen_resize_current_scene = 0;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Scene 2", cfg::SCREENRESIZE->screen_resize_current_scene == 1)) {
|
||||
cfg::SCREENRESIZE->screen_resize_current_scene = 1;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Scene 3", cfg::SCREENRESIZE->screen_resize_current_scene == 2)) {
|
||||
cfg::SCREENRESIZE->screen_resize_current_scene = 2;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Scene 4", cfg::SCREENRESIZE->screen_resize_current_scene == 3)) {
|
||||
cfg::SCREENRESIZE->screen_resize_current_scene = 3;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker(
|
||||
"Hint: bind a key to Screen Resize 1/2/3/4 for quick scene switching. "
|
||||
"Scene 1 is the default scene activated when starting the game.");
|
||||
|
||||
auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene];
|
||||
|
||||
// general settings
|
||||
ImGui::Checkbox("Linear Filter", &cfg::SCREENRESIZE->enable_linear_filter);
|
||||
ImGui::Checkbox("Centered", &cfg::SCREENRESIZE->centered);
|
||||
if (!cfg::SCREENRESIZE->centered) {
|
||||
ImGui::InputInt("X Offset", &cfg::SCREENRESIZE->offset_x);
|
||||
ImGui::InputInt("Y Offset", &cfg::SCREENRESIZE->offset_y);
|
||||
}
|
||||
ImGui::InputInt("X Offset", &scene.offset_x);
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker("Hint: ctrl + click on +/- buttons to move quickly.");
|
||||
ImGui::InputInt("Y Offset", &scene.offset_y);
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker("Hint: ctrl + click on +/- buttons to move quickly.");
|
||||
|
||||
// aspect ratio
|
||||
ImGui::Checkbox("Keep Aspect Ratio", &cfg::SCREENRESIZE->keep_aspect_ratio);
|
||||
if (cfg::SCREENRESIZE->keep_aspect_ratio) {
|
||||
if (ImGui::SliderFloat("Scale", &cfg::SCREENRESIZE->scale_x, 0.65f, 2.0f)) {
|
||||
cfg::SCREENRESIZE->scale_y = cfg::SCREENRESIZE->scale_x;
|
||||
ImGui::Checkbox("Keep Aspect Ratio", &scene.keep_aspect_ratio);
|
||||
if (scene.keep_aspect_ratio) {
|
||||
if (ImGui::SliderFloat("Scale", &scene.scale_x, 0.5f, 2.5f)) {
|
||||
scene.scale_y = scene.scale_x;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value.");
|
||||
} else {
|
||||
ImGui::SliderFloat("Width Scale", &cfg::SCREENRESIZE->scale_x, 0.65f, 2.0f);
|
||||
ImGui::SliderFloat("Height Scale", &cfg::SCREENRESIZE->scale_y, 0.65f, 2.0f);
|
||||
ImGui::SliderFloat("Width Scale", &scene.scale_x, 0.5f, 2.5f);
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value.");
|
||||
ImGui::SliderFloat("Height Scale", &scene.scale_y, 0.5f, 2.5f);
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker("Hint: ctrl + click on the slider to type in a numeric value.");
|
||||
}
|
||||
|
||||
ImGui::EndDisabled();
|
||||
@@ -139,8 +176,16 @@ namespace overlay::windows {
|
||||
if (ImGui::Checkbox("Always on Top", &cfg::SCREENRESIZE->window_always_on_top) ) {
|
||||
graphics_update_z_order(window);
|
||||
}
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::Checkbox("Forced Render Scaling", &GRAPHICS_WINDOW_BACKBUFFER_SCALE);
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker(
|
||||
"For windowed mode: forcibly set DX9 back buffer dimensions to match window size. "
|
||||
"Reduces pixelated scaling artifacts. Works great on some games, but completely broken on others.\n\n"
|
||||
"This can't be changed in-game; instead, set -windowscale option in spicecfg and restart.");
|
||||
|
||||
ImGui::Checkbox("Keep Aspect Ratio", &cfg::SCREENRESIZE->client_keep_aspect_ratio);
|
||||
ImGui::TextWrapped("Hint: if game is blurry after resizing, try -windowscale option in spicecfg.");
|
||||
ImGui::Checkbox("Manual window move/resize", &cfg::SCREENRESIZE->enable_window_resize);
|
||||
ImGui::BeginDisabled(!cfg::SCREENRESIZE->enable_window_resize);
|
||||
|
||||
@@ -209,8 +254,10 @@ namespace overlay::windows {
|
||||
|
||||
void ScreenResize::update() {
|
||||
Window::update();
|
||||
auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game());
|
||||
|
||||
// toggle
|
||||
if (this->toggle_screen_resize != ~0u) {
|
||||
auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game());
|
||||
bool toggle_screen_resize_new = overlay_buttons
|
||||
&& this->overlay->hotkeys_triggered()
|
||||
&& GameAPI::Buttons::getState(RI_MGR, overlay_buttons->at(this->toggle_screen_resize));
|
||||
@@ -220,5 +267,36 @@ namespace overlay::windows {
|
||||
}
|
||||
this->toggle_screen_resize_state = toggle_screen_resize_new;
|
||||
}
|
||||
|
||||
// scene switch
|
||||
auto toggle_scene_state_new = ~0u;
|
||||
for (size_t i = 0; i < std::size(this->toggle_scene); i++) {
|
||||
if (this->toggle_scene[i] == ~0u) {
|
||||
continue;
|
||||
}
|
||||
bool scene_switched = overlay_buttons
|
||||
&& this->overlay->hotkeys_triggered()
|
||||
&& GameAPI::Buttons::getState(RI_MGR, overlay_buttons->at(this->toggle_scene[i]));
|
||||
|
||||
if (scene_switched) {
|
||||
toggle_scene_state_new = (uint32_t)i;
|
||||
}
|
||||
|
||||
// only detect rising edges of key presses
|
||||
if (scene_switched && (this->toggle_scene_state != i)) {
|
||||
if (cfg::SCREENRESIZE->screen_resize_current_scene == (int8_t)i &&
|
||||
cfg::SCREENRESIZE->enable_screen_resize) {
|
||||
// this scene is already active, turn scaling off
|
||||
cfg::SCREENRESIZE->enable_screen_resize = false;
|
||||
} else {
|
||||
// switch to scene
|
||||
cfg::SCREENRESIZE->enable_screen_resize = true;
|
||||
cfg::SCREENRESIZE->screen_resize_current_scene = i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// remember if a key was pressed (or nothing pressed) this frame
|
||||
this->toggle_scene_state = toggle_scene_state_new;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ namespace overlay::windows {
|
||||
size_t toggle_screen_resize = ~0u;
|
||||
bool toggle_screen_resize_state = false;
|
||||
|
||||
size_t toggle_scene[4] = { ~0u, ~0u, ~0u, ~0u };
|
||||
uint32_t toggle_scene_state = ~0u;
|
||||
|
||||
void build_fullscreen_config();
|
||||
void build_windowed_config();
|
||||
void build_footer();
|
||||
|
||||
@@ -23,11 +23,35 @@ namespace overlay::windows {
|
||||
|
||||
const auto padding = ImGui::GetFrameHeight() / 2;
|
||||
|
||||
this->init_size = ImVec2(ImGui::GetIO().DisplaySize.x - (padding * 2), 0.f);
|
||||
this->init_size.y = (this->init_size.x * 9 / 16) + ImGui::GetFrameHeight();
|
||||
switch (games::sdvx::OVERLAY_POS) {
|
||||
case games::sdvx::SDVX_OVERLAY_BOTTOM_LEFT:
|
||||
case games::sdvx::SDVX_OVERLAY_BOTTOM_RIGHT:
|
||||
this->init_size.x = (ImGui::GetIO().DisplaySize.x - (ImGui::GetIO().DisplaySize.y * 9 / 16)) / 2 - padding;
|
||||
this->init_size.y = (this->init_size.x * 9 / 16) + ImGui::GetFrameHeight();
|
||||
break;
|
||||
case games::sdvx::SDVX_OVERLAY_TOP:
|
||||
case games::sdvx::SDVX_OVERLAY_BOTTOM:
|
||||
case games::sdvx::SDVX_OVERLAY_MIDDLE:
|
||||
default:
|
||||
this->init_size = ImVec2(ImGui::GetIO().DisplaySize.x - (padding * 2), 0.f);
|
||||
this->init_size.y = (this->init_size.x * 9 / 16) + ImGui::GetFrameHeight();
|
||||
if (GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||
this->init_size.x /= 2;
|
||||
this->init_size.y /= 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
this->init_pos = ImVec2(ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2, 0);
|
||||
switch (games::sdvx::OVERLAY_POS) {
|
||||
case games::sdvx::SDVX_OVERLAY_BOTTOM_LEFT:
|
||||
this->init_pos.x = 0;
|
||||
this->init_pos.y = ImGui::GetIO().DisplaySize.y - this->init_size.y;
|
||||
break;
|
||||
case games::sdvx::SDVX_OVERLAY_BOTTOM_RIGHT:
|
||||
this->init_pos.x = ImGui::GetIO().DisplaySize.x - this->init_size.x;
|
||||
this->init_pos.y = ImGui::GetIO().DisplaySize.y - this->init_size.y;
|
||||
break;
|
||||
case games::sdvx::SDVX_OVERLAY_TOP:
|
||||
this->init_pos.y = padding;
|
||||
break;
|
||||
|
||||
@@ -494,7 +494,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;
|
||||
|
||||
@@ -138,8 +138,15 @@ static void touch_initialize() {
|
||||
}
|
||||
SPICETOUCH_INITIALIZED = true;
|
||||
|
||||
if (!RI_MGR) {
|
||||
log_fatal(
|
||||
LOG_MODULE_NAME,
|
||||
"touch_initialize() - RI Manager not available, called too early! "
|
||||
"This is a BUG in spice, please file a bug report with log.txt.");
|
||||
}
|
||||
|
||||
// initialize handler
|
||||
if (RI_MGR && rawinput::touch::is_enabled(RI_MGR.get())) {
|
||||
if (rawinput::touch::is_enabled(RI_MGR.get())) {
|
||||
TOUCH_HANDLER = new RawInputTouchHandler();
|
||||
} else if (Win8Handler::is_available()) {
|
||||
TOUCH_HANDLER = new Win8Handler();
|
||||
@@ -166,13 +173,26 @@ static inline void touch_unregister_window(HWND hWnd) {
|
||||
}
|
||||
}
|
||||
|
||||
bool is_touch_available() {
|
||||
bool is_touch_available(LPCSTR caller) {
|
||||
static bool called_once = false;
|
||||
|
||||
if (!RI_MGR) {
|
||||
log_fatal(
|
||||
LOG_MODULE_NAME,
|
||||
"is_touch_available({}) - RI Manager not available, called too early! "
|
||||
"This is a BUG in spice, please file a bug report with log.txt.",
|
||||
caller);
|
||||
}
|
||||
|
||||
// initialize touch handler
|
||||
touch_initialize();
|
||||
|
||||
// Check if a touch handler has been set. No need to call `is_available` here
|
||||
// as `touch_initialize` does that.
|
||||
if (!called_once) {
|
||||
log_misc("touch", "is_touch_available called by: {}, returning {}",
|
||||
caller, (TOUCH_HANDLER != nullptr) ? "TRUE" : "false");
|
||||
called_once = true;
|
||||
}
|
||||
|
||||
return TOUCH_HANDLER != nullptr;
|
||||
}
|
||||
|
||||
@@ -202,7 +222,7 @@ static LRESULT CALLBACK SpiceTouchWndProc(HWND hWnd, UINT msg, WPARAM wParam, LP
|
||||
SPICETOUCH_REGISTERED_TOUCH = true;
|
||||
|
||||
// check if touch is available
|
||||
if (is_touch_available()) {
|
||||
if (is_touch_available("SpiceTouchWndProc")) {
|
||||
|
||||
// notify the handler of our window
|
||||
TOUCH_HANDLER->window_register(hWnd);
|
||||
|
||||
@@ -27,7 +27,7 @@ extern int SPICETOUCH_TOUCH_Y;
|
||||
extern int SPICETOUCH_TOUCH_WIDTH;
|
||||
extern int SPICETOUCH_TOUCH_HEIGHT;
|
||||
|
||||
bool is_touch_available();
|
||||
bool is_touch_available(LPCSTR caller);
|
||||
|
||||
void touch_attach_wnd(HWND hWnd);
|
||||
void touch_attach_dx_hook();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user