Compare commits

..

29 Commits

Author SHA1 Message Date
sp2xdev 205cf29aff release build script and update changelog 2025-04-24 17:21:34 -07:00
bicarus-dev bf9074a76e launcher: complain when duplicate DLLs are detected (#306)
## Link to GitHub Issue, if one exists
#304 

## Description of change
Complain loudly about DLL conflicts. Show warnings for each DLL that
exists in both modules directory and the root.

This is not based on LoadLibrary hooks, just enumerating DLLs via
filesystem access.

Deliberately not using `log_fatal` since these are not necessarily going
to crash the game. Some people just love to have messy installs and if
the game booted fine before, it should continue to boot.

## Testing
Tested with/without `-modules`, with/without duplicate DLLs.
2025-04-24 10:35:34 -07:00
bicarus-dev 44e1edb50c avs: make max path check stricter (#305)
## Link to GitHub Issue, if one exists
n/a

## Description of change
The check to warn the user about potential MAX_PATH violation should be
stricter. Saw a user with 150 characters run into a crash during boot
for SDVX3.

## Testing
Tested paths under 130 / over 130, in both 32/64 bits.
2025-04-24 10:18:09 -07:00
bicarus-dev 41d0dce6e9 cfg: use %appdata%\spice2x for new config files (#300)
## Link to GitHub Issue, if one exists
n/a

## Description of change

For JSON config files of the following features:

* patch manager
* screen resize
* IIDX camera hook
* card manager

when saving a new file, store them in %appdata%\spice2x instead of
%appdata%.

On load:
1. If the JSON file exists in %appdata%\spice2x, use that (new path)
1. If the JSON file exists in %appdata%, continue to use that (legacy
path)

It's common for people to have mixed versions of spicetools/spice2x so
we'll continue to read from the %appdata% root if the files are there,
but with a preference for the new path. We will not forcibly move files.

spicetools.xml will continue to live in the %appdata% root. Moving this
will confuse a lot of people, so I'm avoiding this.

Also, this fixes `-patchcfgpath` and `-resizecfgpath` to create
directories as needed (previously the parent directory must have existed
first)

## Testing
Tested -

* existing config files are continued to be read from %appdata%
* new files get created in %appdata%\spice2x\... (both in spicecfg and
in overlay)
* can provide custom path for `-patchcfgpath` `-resizecfgpath` and
observe directories + file created in custom path, try absolute or local
relative paths
2025-04-22 00:35:10 -07:00
bicarus-dev 598422b701 build: large address aware variant of 32-bit spice.exe (#298)
## Link to GitHub Issue, if one exists
Fixes #280

## Description of change
Introduce a variant of 32bit spice.exe that is large address aware.

## Compiling
👍 

## Testing
Minimal testing has been done to see if the LAA variant can boot
existing 32 bit games.
2025-04-22 00:21:45 -07:00
bicarus-dev b58a1ccfcc sdvx: disable sdvx landscape option (#303)
## Link to GitHub Issue, if one exists
Fixes #277 

## Description of change
To avoid confusion, temporarily remove `-sdvxlandscape` because of the
issue described in the bug above.

`forceresswap` is still kept, so there is still a way to do exactly what
`-sdvxlandscape` used to do, if someone really wants it.

## Testing
Checked that `-sdvxlandscape` is ignored, and that `-sdvxlandscape`
continues to be honored.
2025-04-19 20:44:24 -07:00
ASleepyCat 60efd643d1 Add ccache support for faster incremental builds (#301)
## Description of change
Adds `ccache` support for local compiling and Github Actions
environments. This significantly speeds up incremental builds, but cold
starts will take a little longer to compile.

The build script now has an `-i` flag to ignore the build cache if
needed. You can also manually delete the `.ccache` folder to force a
fresh build too.

For the GA workflow, it uses the
[`ccache-action`](https://github.com/hendrikmuhs/ccache-action) action
to manage and store the cached build objects. This cache should be
accessible to all branches.

I also removed the `--no-cache` flag when building the
`spicetools/spice` image since Docker will automatically invalidate
`COPY` layers if it detects the copied files have changed.

### Compile times
Compiled with a 7800X3D, 32GB 6000MT/s RAM.
| `main` | `ccache` (fresh) | `ccache` (cached) | `ccache` (ignore
cache) |
|--------|--------|--------|--------|
| 2m46.047s | 3m8.886s | 0m32.638s | 2m45.148s |

## Testing
Tested incremental builds by using another branch that's rebased on top
of this.
2025-04-19 20:43:10 -07:00
sp2xdev 0596eeb6fe Update pull_request_template.md 2025-04-19 14:24:21 -07:00
ASleepyCat 3d5c64feb7 Fix button HID string ranges not being retrieved correctly (#299)
## Description of change
Currently, retrieving HID strings using string index minimum/maximum
ranges for buttons is broken as it uses the same index for each usage
cap.

Now it properly ranges over the minimum/maximum ranges.

Before:

![image](https://github.com/user-attachments/assets/da8d0162-07d9-4027-9933-2348fbf43a27)

After:

![image](https://github.com/user-attachments/assets/c00f81fe-d9d4-43f8-9bdd-d570ef298479)


## Compiling
Check CI.

## Testing
Tested with Beef Board firmware using string index ranges in the USB HID
descriptor.
2025-04-18 01:23:00 -07:00
sp2xdev 610984f40e update changelog 2025-04-07 17:04:36 -07:00
sp2xdev 1e271b7352 Merge branch 'main' of https://github.com/spice2x/spice2x.github.io 2025-04-07 17:04:14 -07:00
bicarus-dev 07c78caf01 build: add git commit hash to produced artifact (#297)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Previously, the artifact produced by the CI action was `spice2x.zip`.

With this change, it will be `spice2x-ci-1234567.zip` where 1234567 is
the first 7 chars of the commit hash.

Note that for PRs, github produces a merge commit before queueing the
action, so the commit hash won't match up with the HEAD of a fork. The
goal is to just generate unique-ish filenames so this is not a huge
deal.

## Compiling
## Testing

Action has been run in the fork.
2025-04-07 15:56:46 -07:00
sp2xdev d153209d8a update changelog 2025-04-07 02:14:51 -07:00
bicarus-dev 48186245fe overlay: update UI for card manager (#294)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Change the usage pattern for Card Manager UI.

### Previous behavior

Previously, it was a list of cards that you select, and click a button
to insert as P1 or P2.

This is fine, but users got confused when they pressed `Insert Card`
overlay key and got a different card inserted, or when auto-insert
didn't work as expected.

Additionally, if you play a game without continue (IIDX/DDR for
example), then it's cumbersome to bring up the overlay and click on
insert card every time.

### New behavior

Insert Card overlay now lets you pick a card and *slot* it into P1 or P2
side.

From that point on, the slotted card number is used as the override,
replacing what was previously passed to `-card0` or `-card1`. This means
that `Insert Card` button and auto-insert will pick up the new card and
use that instead.

## Compiling
👍 

## Testing
Tested 1p-only game (popn) and 2p games (DDR, IIDX).
2025-04-07 01:49:07 -07:00
bicarus-dev f7c274591a museca: add Start and 10KEY lights (#296)
## Link to GitHub Issue, if one exists
Fixes #295 

## Description of change
Add missing `Start` and `10KEY` lights output, as see in the test menu.

## Compiling
👍 

## Testing
Seems fine in museca test mode.
2025-04-06 19:25:39 -07:00
ASleepyCat d1a20e58b6 Add CI and improve build system (#293)
## Link to GitHub Issue, if one exists
Fixes #292

## Description of change
This PR adds CI with Github Actions. Runs on every push and pull
request. If a new commit is pushed, any previous runs are cancelled for
that branch.

In total, these optimisations (on a 7800X3D with 32GB 6000MT/s RAM and
an NVMe Gen4 SSD):
* Reduces `spicetools/deps` image size by around 200 MB, and build time
by almost 50%
* Reduces compilation time by around 9%

Changes:
* Pull archlinux image from GHCR so that we don't run into pull limits
from docker.io
* Use `base-devel` archlinux image
* Reduces image build time from 141 seconds to 109 seconds on my machine
  * Clean up unneeded packages
* Small reduction in `spicetools/deps` image size (4.35 GB vs 4.14 GB)
* Use precompiled `yay` binary
* Reduces image build time from 163 seconds to 141 seconds on my machine
* Use `nproc` to get the number of cores instead of parsing
`/proc/cpuinfo` with `awk`
* Use `ninja` to compile
  * Speeds up compile times a little, around 13 seconds on my machine
* Don't run `docker run` in interactive TTY in `build_docker.sh`
  * Github Actions disallows interactive TTYs
  * Still allowed for the batch script
* `time` the compilation process
* Enable BuildKit in the bash script
  * Required for build contexts
* Don't use `USER` instruction
  * Breaks Github Actions, doesn't seem to affect build?
* More info:
https://docs.github.com/en/actions/sharing-automations/creating-actions/dockerfile-support-for-github-actions#user
  * `USER` also seems to cause issues on a mounted ReFS image
* Add executable bits to bash scripts

Future ideas:
* Combine `external/Dockerfile` and `./Dockerfile` into one to
deduplicate images and save disk space
* Fix the weird `/src/src` directory structure in the Dockerfile and
build scripts
* Investigate CMake and ninja optimisations?

## Compiling
Check CI status on my fork.

## Testing
Tested with docker on Windows. Also play tested a bit to make sure the
Github Actions artifact works.
2025-04-06 00:29:02 -07:00
sp2xdev 1e02c31524 update changelog 2025-04-04 22:13:01 -07:00
bicarus-dev 66fab48a5b options: update monitor option text (#291)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Update tooltip for `-monitor` option. Move to `Graphics (Full Screen)`
to reduce clutter.

## Compilin
👍 

## Testing
n/a
2025-04-04 22:09:21 -07:00
bicarus-dev ae06652148 touch: avoid calling is_touch_available too early (#289)
## Link to GitHub Issue, if one exists
Fixes #120 

## Description of change
Don't call `is_touch_available` in launcher before RawInput stack is
initialized. This causes us to incorrectly fall back to the Win7/Win8
touch handler.

The desired behavior is to use rawinput touch by default for all games,
unless overridden by the user via `-wintouch`.

Add a bunch of logging so we can spot who's calling
`is_touch_available`.

As a side effect, a handful of games that previously used Win7/8 touch
will now use RawInput touch instead. If this causes any issues, they can
turn `-wintouch` on to force Win7/8 touch logic again.

## Compiling
👍 

## Testing
Test:

* RB
* -wintouch
* TDJ, UFC subscreen and native touch
* overlay, especially in multi-mon
2025-04-04 21:31:18 -07:00
sp2xdev bd6c8d3f3c add gitignore to root 2025-04-04 17:51:40 -07:00
bicarus-dev e88abec2a6 overlay: remove navigator input (#288)
## Link to GitHub Issue, if one exists
n/a

## Description of change
No one uses this, remove them to reduce clutter. This is in preparation
to migrate from deprecated ImGui IO model to new one, and this is part
of that.

## Compiling
👍 

## Testing
2025-04-03 21:16:48 -07:00
bicarus-dev 6de8b529d2 imgui: update to v1.91.4, make various fixes to overlay (#286)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Update ImGui to `v1.91.4`. We can't update to `v1.91.5` or newer just
yet because impl_spice depends on the old IO model of ImGui which got
removed in `v1.91.5`.

We also can't update the DX9 renderer right now, because at some point
overlay breaks in TDJ (and only in TDJ...)

Enable the new debug logging functionality in ImGui. Add a button in
overlay to toggle debug log window.

Fix some minor bugs spotted by the new ImGui debug log: mismatched API
calls, font loading issues.

## Compiling
👍 

## Testing
In progress.
2025-04-03 19:14:48 -07:00
bicarus-dev 7301cdaa6c imgui: reorganize imgui library files to mirror what's in the imgui releases (#285)
## Link to GitHub Issue, if one exists
n/a

## Description of change
Functionally, no change.

Move ImGui library files around so that they mirror how they are
structured in imgui releases. This will make it easier to migrate to a
future version of ImGui.

Add a handful of static asserts to check that we have the necessary
features enabled / disabled in imgui header.

## Compiling
👍 

## Testing
Tested 32-bit overlay, 64-bit overlay, TDJ, and UFC overlays, and
spicecfg.

Interesting finds during ImGui testing (not part of this PR, but for
future reference when upgrading imgui version):
* Updating dx9 backend to the newest causes TDJ to stop rendering the
overlay completely (fine in UFC or any other game)
* Omitting `IMGUI_USE_BGRA_PACKED_COLOR` causes spicecfg software
renderer to use the wrong color (RGB format parsing error probably)
* At some point - v1.90? the `About` and `Licenses` buttons in spicecfg
stop responding after an upgrade, maybe our improper usage of MenuItem?
* Using the docking branch of ImGui is not required but we should just
stick to it for multi-viewport functionality which we may want to use
later.
2025-04-02 22:56:52 -07:00
bicarus-dev 6969dfcdd1 overlay: migrate configurator UI from ImGui Columns to Tables (#282)
## Link to GitHub Issue, if one exists
n/a

## Description of change
`ImGui::Columns` is a deprecated component with many issues. Migrate
Buttons/Analog/Overlay/Lights/Options... tabs to the new table API.
Clean up some UI elements around it to reduce clutter.

Note: there are still other usages of Columns API (MIDI debug output,
ACIO viewer, window manager) but no one hardly ever sees those.
Eventually they will need to be migrated if ImGui ever drops support for
them.

## Compiling
👍 

## Testing
Testing all games in spicecfg and overlay.
2025-04-02 18:33:39 -07:00
bicarus-dev edaf5a386f overlay: fix mouse cursor getting stuck in wrong shape when leaving and coming back to window (#283)
## Link to GitHub Issue, if one exists
Fixes #27 

## Description of change
In standalone configurator mode, when a mouse cursor moves outside the
window and comes back, it may get stuck in the wrong cursor type (e.g.,
get stuck in horizontal resize mode). This is a side effect of software
rendering we do that doesn't deal nicely with mouse events (i.e., not at
all).

## Compiling
Should be fine.

## Testing
Testing spicecfg, spice -cfg, spice64 -cfg, and in-game overlay.
2025-04-02 18:32:30 -07:00
bicarus-dev 302f08dd57 patcher: show errors on invalid json response from remote (#281)
## Link to GitHub Issue, if one exists
#0

## Description of change
When patcher URL returns an invalid response (as in, cannot be parsed as
JSON), show an error message to user.

Add a "copy text" button to copy the error message.

Fix WarnTooltip so that can handle the case where description is an
empty string for a patch - previously this was just showing as multiple
lines of empty space and then the warning.

## Compiling
It's fine except Docker build currently fails due to cmake version
issue.

## Testing
Tested valid / invalid datecodes and checked against responses.
2025-04-02 18:32:15 -07:00
sp2xdev 68f5a614ef update discord-rpc cmake version 2025-04-01 02:35:41 -07:00
sp2xdev 5db43031f2 fix option descriptions 2025-03-29 01:35:02 -07:00
sp2xdev 873ade757a show patch description in patch warn marker 2025-03-29 00:10:42 -07:00
67 changed files with 16640 additions and 8044 deletions
+32
View File
@@ -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
View File
@@ -0,0 +1,13 @@
.vscode/*
# JetBrains IDEs
.idea
.idea_modules
*.iws
# CMake
cmake-build*
# user config
build_all.local.sh
build_docker.local.sh
+1 -3
View File
@@ -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?*
+1
View File
@@ -2,3 +2,4 @@ bin/**
dist/**
docker/**
cmake-build*
.ccache/**
+3 -1
View File
@@ -16,4 +16,6 @@ build_docker.local.sh
bin/*
dist/*
external/cv2pdb/*
external/cv2pdb/*
.ccache/*
+18 -6
View File
@@ -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)
@@ -516,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
@@ -610,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
#############
@@ -629,7 +642,6 @@ IF(NOT MSVC)
set_target_properties(spicetools_spice64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
endif()
# spicecfg.exe
##############
@@ -735,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"
+4 -5
View File
@@ -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"]
+20
View File
@@ -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;
+11 -6
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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 "$@"
+2
View File
@@ -0,0 +1,2 @@
del /s /q .ccache
call build_docker.bat
+10 -12
View File
@@ -15,12 +15,18 @@ namespace cfg {
std::optional<std::string> SCREEN_RESIZE_CFG_PATH_OVERRIDE;
ScreenResize::ScreenResize() {
bool file_exists = false;
if (SCREEN_RESIZE_CFG_PATH_OVERRIDE.has_value()) {
this->config_path = SCREEN_RESIZE_CFG_PATH_OVERRIDE.value();
if (fileutils::file_exists(this->config_path)) {
log_info("ScreenResize", "loading config from: {}", this->config_path.string());
file_exists = true;
}
} else {
this->config_path = std::filesystem::path(_wgetenv(L"APPDATA")) / L"spicetools_screen_resize.json";
this->config_path =
fileutils::get_config_file_path("ScreenResize", "spicetools_screen_resize.json", &file_exists);
}
if (fileutils::file_exists(this->config_path)) {
if (file_exists) {
this->config_load();
}
}
@@ -29,12 +35,6 @@ namespace cfg {
}
void ScreenResize::config_load() {
if (SCREEN_RESIZE_CFG_PATH_OVERRIDE.has_value()) {
log_info("ScreenResize", "loading custom config: {}", this->config_path.string());
} else {
log_info("ScreenResize", "loading global config from APPDATA");
}
std::string config = fileutils::text_read(this->config_path);
if (config.empty()) {
log_info("ScreenResize", "config is empty");
@@ -170,8 +170,6 @@ namespace cfg {
}
void ScreenResize::config_save() {
log_info("ScreenResize", "saving config: {}", this->config_path.string());
rapidjson::Document doc;
std::string config = fileutils::text_read(this->config_path);
if (!config.empty()) {
@@ -221,10 +219,10 @@ namespace cfg {
doc.Accept(writer);
// save to file
if (fileutils::text_write(this->config_path, buffer.GetString())) {
if (fileutils::write_config_file("ScreenResize", this->config_path, buffer.GetString())) {
// this->config_dirty = false;
} else {
log_warning("ScreenResize", "unable to save config file to {}", this->config_path.string());
log_warning("ScreenResize", "unable to save config file");
}
}
}
+10
View File
@@ -1,3 +1,13 @@
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
+1 -1
View File
@@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 3.2.0)
cmake_minimum_required (VERSION 3.12)
project (DiscordRPC)
include(GNUInstallDirs)
+6 -14
View File
@@ -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
View File
@@ -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
View File
@@ -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})
@@ -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.
}
@@ -11,7 +11,7 @@
#pragma once
#include "external/imgui/imgui.h"
#include "imgui.h"
struct IDirect3DDevice9;
+33 -16
View File
@@ -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);
}
*/
+4651 -2301
View File
File diff suppressed because it is too large Load Diff
+1497 -902
View File
File diff suppressed because it is too large Load Diff
+3015 -663
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+139 -107
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.9)
cmake_minimum_required(VERSION 3.12)
cmake_policy(SET CMP0069 NEW)
project(minhook)
+5 -4
View File
@@ -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;
-12
View File
@@ -461,18 +461,6 @@ namespace games {
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");
-6
View File
@@ -30,12 +30,6 @@ namespace games {
ScreenResizeScene3,
ScreenResizeScene4,
SuperExit,
NavigatorActivate,
NavigatorCancel,
NavigatorUp,
NavigatorDown,
NavigatorLeft,
NavigatorRight,
HotkeyEnable1,
HotkeyEnable2,
HotkeyToggle,
+2
View File
@@ -71,6 +71,8 @@ std::vector<Light> &games::museca::get_lights() {
GameAPI::Lights::sortLights(
&lights,
"Start",
"Keypad",
"Title R",
"Title G",
"Title B",
+2
View File
@@ -46,6 +46,8 @@ namespace games::museca {
// all lights in correct order
namespace Lights {
enum {
Start,
Keypad,
TitleR,
TitleG,
TitleB,
+1 -1
View File
@@ -63,7 +63,7 @@ namespace games::qma {
touch_attach_dx_hook();
// cursor
if (!is_touch_available()) {
if (!is_touch_available("QMATouchDevice::open")) {
ShowCursor(true);
}
}
+1 -1
View File
@@ -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);
}
+1 -1
View File
@@ -72,7 +72,7 @@ public:
touch_attach_dx_hook();
// cursor
if (!is_touch_available()) {
if (!is_touch_available("TwTouchDevice::open")) {
ShowCursor(true);
}
}
+34 -46
View File
@@ -124,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
@@ -217,6 +219,7 @@ int main_implementation(int argc, char *argv[]) {
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;
@@ -743,9 +746,11 @@ 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()) {
@@ -1113,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
@@ -1165,13 +1172,17 @@ int main_implementation(int argc, char *argv[]) {
}
}
if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !GRAPHICS_WINDOWED) {
#if SPICE64
GRAPHICS_FS_ORIENTATION_SWAP = true;
#else
log_warning("launcher", "-sdvxlandscape is not supported in 32-bit SDVX, ignoring...");
#endif
}
// // SDVXFullscreenLandscape disabled due to it messing with in-game camera angle
// // FullscreenOrientationFlip continues to live on, however.
//
// if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !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;
}
@@ -1291,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;
}
@@ -1338,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;
}
@@ -1458,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;
}
@@ -1569,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;
}
@@ -1607,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;
}
@@ -1619,10 +1608,7 @@ 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;
}
@@ -1632,10 +1618,7 @@ int main_implementation(int argc, char *argv[]) {
attach_io = true;
attach_mfg = true;
launcher::signal::USE_VEH_WORKAROUND = true;
// automatically show cursor when no touchscreen is available
if (!is_touch_available()) {
GRAPHICS_SHOW_CURSOR = true;
}
show_cursor_if_no_touch = true;
break;
}
@@ -1644,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;
}
@@ -1826,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();
@@ -1843,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);
+15 -9
View File
@@ -186,9 +186,12 @@ 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",
@@ -212,7 +215,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.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 -sdvxlandscape, put the TARGET monitor resolution in this field.\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",
@@ -225,7 +228,8 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.desc =
"Allows you to play portrait games in in landscape (and vice versa) by transposing resolution and applying image scaling.\n\n"
"Works great for some games, but can COMPLETELY BREAK other games - YMMV!\n\n"
"Strongly consider combining this with -forceres option to render at monitor native resolution",
"Strongly consider combining this with -forceres option to render at monitor native resolution\n\n"
"WARNING: for SDVX, this messes with camera angle for note lanes, causing it to be zoomed out!",
.type = OptionType::Bool,
.category = "Graphics (Full Screen)"
},
@@ -1084,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",
},
@@ -1093,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",
},
@@ -2046,15 +2050,17 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
},
{
// SDVXFullscreenLandscape
.title = "SDVX Landscape Mode (SDVX5+)",
.title = "(DISABLED) SDVX Landscape Mode (SDVX5+)",
.name = "sdvxlandscape",
.desc =
"Option HIDDEN and DISABLED due to it affecting in-game camera angle for displaying lanes.\n\n"
"Allows you to play in landscape by transposing resolution and applying image scaling.\n\n"
"Works only for SDVX5 and above! This is identical to -forceorientation.\n\n"
"Works only for SDVX5 and above! This is identical to -forceresswap.\n\n"
"Will launch at 1080p by default; strongly consider combining this with -forceres option to render at monitor native resolution",
.type = OptionType::Bool,
.hidden = true,
.game_name = "Sound Voltex",
.category = "Game Options"
.category = "Game Options",
},
{
// spice2x_EnableSMXStage
+17 -13
View File
@@ -66,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;
}
@@ -83,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
@@ -162,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);
@@ -171,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
@@ -211,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;
@@ -223,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;
}
+2
View File
@@ -4,6 +4,7 @@
#include <filesystem>
#include <string>
#include <optional>
#include <mutex>
#include "external/scard/scard.h"
@@ -30,6 +31,7 @@ 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;
+1 -1
View File
@@ -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;
+2 -2
View File
@@ -39,12 +39,12 @@ namespace ImGui {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
if (desc) {
if (desc && desc[0]) {
ImGui::TextUnformatted(desc);
ImGui::TextUnformatted("");
}
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 0.f, 1.f));
if (warn) {
if (warn && warn[0]) {
ImGui::TextUnformatted("WARNING:");
ImGui::TextUnformatted(warn);
}
+35 -27
View File
@@ -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();
}
}
}
+1 -1
View File
@@ -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?
+44 -22
View File
@@ -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());
}
}
+2
View File
@@ -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
+2 -3
View File
@@ -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();
+1 -1
View File
@@ -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 {
+208 -100
View File
@@ -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);
}
}
}
+9 -1
View File
@@ -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);
};
}
File diff suppressed because it is too large Load Diff
+6 -4
View File
@@ -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);
+1 -1
View File
@@ -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);
+42 -7
View File
@@ -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();
}
@@ -651,7 +658,7 @@ namespace overlay::windows {
ImGui::TableNextColumn();
if (!patch.caution.empty()) {
ImGui::AlignTextToFramePadding();
ImGui::WarnMarker(nullptr, patch.caution.c_str());
ImGui::WarnMarker(patch.description.c_str(), patch.caution.c_str());
} else {
ImGui::DummyMarker();
}
@@ -1121,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");
@@ -1517,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;
}
+1 -1
View File
@@ -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;
+25 -5
View File
@@ -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);
+1 -1
View File
@@ -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();
+50
View File
@@ -264,3 +264,53 @@ std::vector<uint8_t> *fileutils::bin_read(const std::filesystem::path &path) {
}
return contents;
}
std::filesystem::path fileutils::get_config_file_path(const std::string module, const std::string filename, bool* file_exists) {
// try %appdata%\spice2x path first, if it exists
const auto appdata_spice2x = std::filesystem::path(_wgetenv(L"APPDATA")) / "spice2x" / filename;
if (fileutils::file_exists(appdata_spice2x)) {
log_info(module, "loading config from %appdata%\\spice2x\\{}", filename);
if (file_exists) {
*file_exists = true;
}
return appdata_spice2x;
}
// fallback to older %appdata% path (older spice2x or mainline spicetools), if it exists
const auto appdata = std::filesystem::path(_wgetenv(L"APPDATA")) / filename;
if (fileutils::file_exists(appdata)) {
log_info(module, "loading config from %appdata%\\{}", filename);
if (file_exists) {
*file_exists = true;
}
return appdata;
}
// prefer new path if no existing file found
if (file_exists) {
*file_exists = false;
}
return appdata_spice2x;
}
bool fileutils::write_config_file(const std::string_view &module, const std::filesystem::path path, std::string text) {
// attempt to undo %appdata% expansion to hide user name
const auto appdata = std::filesystem::path(_wgetenv(L"APPDATA")).string();
auto censored = path.string();
const auto substr_offset = censored.find(appdata);
if (substr_offset != std::string::npos) {
censored.replace(substr_offset, appdata.length(), "%appdata%");
}
// create directory path up to where the config file lives
if (!path.parent_path().empty() && !std::filesystem::exists(path.parent_path())) {
log_misc(module, "creating directory path to config file: {}", censored);
if (!fileutils::dir_create_recursive(path.parent_path())) {
return false;
}
}
// save file
log_info(module, "saving config file: {}", censored);
return fileutils::text_write(path, text);
}
+3
View File
@@ -33,4 +33,7 @@ namespace fileutils {
std::string text_read(const std::filesystem::path &file_path);
bool bin_write(const std::filesystem::path &path, uint8_t *data, size_t len);
std::vector<uint8_t> *bin_read(const std::filesystem::path &path);
std::filesystem::path get_config_file_path(const std::string module, const std::string filename, bool* file_exists=nullptr);
bool write_config_file(const std::string_view &module, const std::filesystem::path path, std::string text);
}
+33
View File
@@ -7,6 +7,7 @@
#include "logging.h"
#include "utils.h"
#include "peb.h"
#include "util/fileutils.h"
std::filesystem::path libutils::module_file_name(HMODULE module) {
std::wstring buf;
@@ -323,3 +324,35 @@ intptr_t libutils::offset2rva(const std::filesystem::path &path, intptr_t offset
return rva;
}
void libutils::check_duplicate_dlls() {
const auto &spice_bin_path = libutils::module_file_name(nullptr).parent_path();
if (MODULE_PATH == spice_bin_path) {
return;
}
for (const auto &file : std::filesystem::directory_iterator(MODULE_PATH)) {
const auto &filename = file.path().filename();
const auto extension = strtolower(filename.extension().string());
if (extension == ".dll" &&
fileutils::file_exists(spice_bin_path / filename)) {
log_warning(
"libutils",
"DLL CONFLICT WARNING\n\n\n"
"-------------------------------------------------------------------\n"
"WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING\n"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
"{} exists in BOTH of these directories:\n\n"
" 1. {}\n"
" 2. {}\n\n"
"due to Windows DLL load order rules, #1 will load instead of #2.\n"
"this has unintended consequences and may crash your game!\n"
"resolve the conflict by deleting the stale copy of the DLL\n"
"-------------------------------------------------------------------\n\n\n",
filename.string(),
(spice_bin_path / filename).string(),
(MODULE_PATH / filename).string());
}
}
}
+2
View File
@@ -24,6 +24,8 @@ namespace libutils {
return try_library(module_name.c_str());
}
void check_duplicate_dlls();
// get module handle helpers
HMODULE get_module(const char *module_name);
HMODULE try_module(const char *module_name);