91 Commits
Author SHA1 Message Date
Flyinghead 5eadfb388e dreampotato: change timeout values. disconnect on receive error
initial RTT timeout is 500 ms then 100 ms
2026-01-16 13:12:03 +01:00
Flyinghead 83f6d818b5 maplelink: disable savestates. Change some options default. Bug fix
Disable load/save state if at least one MapleLink with storage enabled.
Change UsePhysicalMemory default to false.
Change Per Game VMU A1 default to true.
Fix DreamLink infinite loop on exit.
dreampotato, dreamconn: don't use stringstream for output
dreampotato: libretro support (no VMU storage yet)
2026-01-16 13:08:54 +01:00
Flyinghead bbae3de9a4 switch: build fix 2026-01-15 17:23:08 +01:00
Flyinghead c2f66018d8 lr: build fix 2026-01-15 16:17:00 +01:00
Flyinghead 82ca2db61a !win32 build fix 2026-01-15 16:01:26 +01:00
Flyinghead b4d83c0d12 DreamLink refactoring
Introduce MapleLink base interface and DreamPotato implementation.
2026-01-15 15:45:14 +01:00
Stefan Schlosser 1aba1b467e deps: prevent installing DreamPicoPort artifacts
Currently the install-target of the DreamPicoPort-API dependency is
executed when installing flycast. This results in the include file
DreamPicoPortApi.hpp and the static library libdream_pico_port_api.a to
be installed alongside flycast.

Add EXCLUDE_FROM_ALL to add_subdirectory to prevent this.
2026-01-14 12:17:21 +01:00
Flyinghead 5be5eeb905 naomi: more rom crc32
[ci skip]
2026-01-14 12:17:02 +01:00
Flyinghead 2f842238a5 ini: don't save transient values and empty sections 2026-01-14 12:16:23 +01:00
flyinghead 3de74672ce Crowdin Translations: Japanese, Swedish 2026-01-13 11:41:42 +01:00
Flyinghead b76b1c7275 Merge remote-tracking branch 'origin/master' into dev
[ci skip]
2026-01-13 11:39:33 +01:00
Flyinghead 7ce50e5229 mmu: stricter test before enabling on-demand MMU
Visual Concepts games and many naomi and systemsp games create some
bogus mappings at boot. Ignore those

Issue #2184
2026-01-05 11:48:03 +01:00
Flyinghead 99fc194dd0 ini: transient values always hide regular ones regardless of order 2026-01-05 11:42:41 +01:00
vkedwardli bd8f7c8a75 Update imgui to v1.92.5 (#2180)
Update ImGui from v1.92.1 to v1.92.5
2026-01-04 10:56:36 +01:00
Flyinghead fd72eebbe3 mmu: enable full MMU when non-SQ mapping is created
Fixes super mario 64 audio
Issue #2184
[ci skip]
2026-01-04 10:53:12 +01:00
Flyinghead fea8699884 modem: enforce correct tx speed for dissymmetric connections 2026-01-04 10:48:01 +01:00
Flyinghead d00b5d4c17 modem: 56K support, V8 bis protocol 2026-01-03 17:52:43 +01:00
flyinghead 1652143f5f New translations flycast.pot (Hungarian)
New translations for {languageName}
2025-12-31 18:25:10 +01:00
flyinghead 75bca2a166 Crowdin Translations: Hungarian and Japanese 2025-12-28 17:51:30 +01:00
Flyinghead 13df86c520 modem: V42bis compression support
[ci skip]
2025-12-28 17:51:01 +01:00
flyinghead 8a228e4e16 Crowdin Translations: Swedish 2025-12-24 17:25:38 +01:00
Flyinghead 0a3c68f7cc i18n: delete bogus zh_CH.po. Load correct one
[ci skip]
2025-12-24 17:25:01 +01:00
flyinghead 4905dc21c8 Crowdin Translations: Japanese 2025-12-23 15:30:29 +01:00
Flyinghead 98dfb4c32a modem: implement V14 sync-async mode and V42 protocol
Use modem sync mode when ASYN is reset.
Implement V14 sync-async mode for V42 detection. Also used by Power
Smash and Pro Yakyu Team de Asobou Net!
Switch to V42 protocol for most games.
Set correct modem speed and timing as set by the game.
Get rid of modem speed/automode cheats.
[ci skip]
2025-12-23 15:29:42 +01:00
Flyinghead df2bdfc9bf ui: support for Asian fonts on linux
Fix width of "Filter" field in the game list.
crowdin.yml: fix typo for zh_CN resource files.
2025-12-23 12:00:53 +01:00
flyinghead e86c1c992f Crowdin Translations: Japanese
courtesy of @kihato
2025-12-22 21:23:57 +01:00
Flyinghead 5b6cc33a75 i18n: add Japanese translation
[ci skip]
2025-12-22 21:22:27 +01:00
flyinghead e37518524f Crowdin Translations: Hungarian and Portuguese, Brazilian 2025-12-21 15:04:05 +01:00
Flyinghead 994f72f954 ci: upload symbols to sentry on push only
[ci skip]
2025-12-21 15:03:09 +01:00
Flyinghead 5978b3a058 ui: merge Asian glyphs into large font too
Issue #2169
2025-12-21 15:00:28 +01:00
flyinghead bb12d5c72d Crowdin Translations (#2162) Hungarian
courtesy of oldman63
2025-12-18 15:56:18 +01:00
Flyinghead b127c96cf3 i18n: Add hu.po to resources
[ci skip]
2025-12-18 15:54:19 +01:00
scribam 0d3beb1c6d fix misleading indentation warning 2025-12-17 14:00:11 +01:00
Flyinghead 9bd3179745 i18n: set all stringstreams to classic locale
Fixes dumped texture names being formatted with the thousands separator
of the current locale.
[ci skip]
2025-12-17 13:59:25 +01:00
Flyinghead 5833fb3a47 ui: use imgui touch support and get rid of delay touch hack 2025-12-16 17:20:45 +01:00
Flyinghead 49ec396e24 i18n: delete empty message files. add zh_HK.po 2025-12-16 17:20:45 +01:00
Stefan Schlosser 5607b8eebd libretro: freebsd build fix 2025-12-16 17:18:52 +01:00
scribam 35888406d3 ci: libretro android build fixes (#2156)
Followup to #2151
[ci skip]
2025-12-16 17:18:32 +01:00
scribam 6b3bbd6f85 ci: update actions (#2155)
* ci: update actions
* ci: use freebsd 15 and openbsd 7.8
[ci skip]
2025-12-16 17:18:07 +01:00
James Smith ef689fd35d Minor bug fixes for DreamPicoPort 2025-12-15 18:05:32 +01:00
scribam 6a78f27bef ci: libretro android build fixes (#2151)
[ci skip]
2025-12-15 18:05:11 +01:00
scribam 97de155fd8 ci: enable libretro on bsd systems (#2150)
[ci skip]
2025-12-15 18:01:47 +01:00
scribam f183f30262 dragonflybsd: build fixes (#2149)
[ci skip]
2025-12-15 18:01:08 +01:00
flyinghead 065c91f9ad Crowdin Translations (#2148)
[ci skip]
2025-12-15 18:00:22 +01:00
scribam 161d057d4c haiku: build fixes 2025-12-14 14:47:24 +01:00
scribam a7ee2e9a66 libretro: netbsd/openbsd build fixes 2025-12-14 14:46:52 +01:00
flyinghead e08ec86962 Crowdin Translations (#2143)
[ci skip]
2025-12-14 14:46:29 +01:00
Flyinghead 7653b1c811 sh4: don't crash when FIPR is executed with PR=1
[ci skip]
2025-12-14 14:45:53 +01:00
Flyinghead 1475812a94 i18n: localize input devices names, buttons and axes 2025-12-14 14:35:09 +01:00
flyinghead c8a724ad63 Crowdin Translations (#2142)
French, Swedish
2025-12-13 17:25:16 +01:00
Flyinghead 2cfce747c5 ui: better egl init test. display message box on graphics init error
Fail if eglGetDisplay() is null.
Throw exception on initRenderApi/switchRenderApi failure.
Catch in SDL and Android and show error message.
Localize Android java code.

Fixes MINIDUMP-7SD MINIDUMP-2XH MINIDUMP-5KM MINIDUMP-2FF MINIDUMP-4ZP
MINIDUMP-6T1 MINIDUMP-5D3 MINIDUMP-7ME
[ci skip]
2025-12-13 17:23:55 +01:00
Flyinghead f8bdf6c35e crowdin.yml: fix pt_BR typo
[ci skip]
2025-12-12 21:51:46 +01:00
Flyinghead f4c2d001bd crowdin.yml: add android resource
[ci skip]
2025-12-12 20:04:07 +01:00
Flyinghead f3ddec5f1e android: add localizable strings resource
[ci skip]
2025-12-12 19:53:04 +01:00
scribam 13c7ddc6c9 deps: remove core/deps/libusb-cmake 2025-12-12 19:39:50 +01:00
nexus382 c24d7fb0fe Send game id to vmu pro/vm2 (#2126)
[ci skip]
2025-12-12 19:39:28 +01:00
Flyinghead 3ebc79a5ce Merge remote-tracking branch 'origin/master' into dev [ci skip] 2025-12-12 19:32:09 +01:00
Flyinghead 6989a0aa2c test: fneg on odd register in PR=1 mode
Issue #2139
2025-12-11 16:26:37 +01:00
Flyinghead bd24812d0c jvs: return value from RawDma is incorrect for MIE firmware upload
It didn't seem to bother anyone however
2025-12-11 16:25:03 +01:00
Flyinghead 15889616dd add zh_CH and zh_TW messages. delete outdated docs
Issue #1830
2025-12-11 15:15:43 +01:00
Flyinghead e60efed8b5 pvr: limit framebuffer height to 240 when interlacing is disabled
Fixes Confidential Mission PAL Echelon intro
Issue #2062
2025-12-11 15:11:24 +01:00
Flyinghead 11fcadf37e sh4: fneg doesn't depend on FPSCR.PR at all
When PR=1, fneg can also be used on odd registers.
Issue #2139
2025-12-11 15:03:16 +01:00
Flyinghead 88b8a0b51e crowdin.yml: add zh_HK mapping [ci skip] 2025-12-10 17:15:24 +01:00
flyinghead 8e9a0f9c1f Crowdin Translations (#2136) pt_BR
New translations flycast.pot (Portuguese, Brazilian)
2025-12-10 11:42:58 +01:00
Flyinghead dd50fcf81c i18n: include pt_BR.po [ci skip] 2025-12-10 11:41:30 +01:00
Flyinghead 7b8656b944 imgread: more checks when loading GDI and CUE files (fail fast)
Add missing CMakeLists.txt change for new cue and gdi tests
2025-12-10 11:40:41 +01:00
Flyinghead 00ae6c42a2 fix some include paths 2025-12-09 19:41:48 +01:00
Flyinghead 8c4c463c38 add cue and gdi parsing tests. add non-C locale tests
delete old en resource file
libretro doesn't need cfg/ini.cpp/h anymore
2025-12-09 17:31:31 +01:00
flyinghead 329b6a1e34 Crowdin Translations (#2134)
New translations: sv, pt, pt_BR
2025-12-09 09:23:08 +01:00
Flyinghead 9c28800384 i18n: std::from/to_chars missing on BSD and macOS
Use stringstream instead

[ci skip]
2025-12-09 09:21:54 +01:00
Flyinghead 602982fb56 add pt_BR crowdin mapping [ci skip] 2025-12-08 23:38:12 +01:00
flyinghead cb830e0bd3 Update Crowdin configuration file 2025-12-08 23:35:08 +01:00
Flyinghead ec3d85a96c remove [ci skip] in crowdin commit messages 2025-12-08 23:26:58 +01:00
Flyinghead d3bc72b0d0 ci: ignore push on l10n_* branches 2025-12-08 22:28:30 +01:00
Flyinghead 4170e783b9 fix GDI and CUE parsing error in non-English locale
IniFile: use locale-independant float conversions functions
Use std::ostringstream and std::istringstream as appropriate.
Use C locale for GDI and CUE parsing.
2025-12-08 22:26:33 +01:00
flyinghead 083ccb984a Crowdin Translations (#2133)
* New translations flycast.pot (French)
New translations for {languageName} [ci skip]
* New translations flycast.pot (Swedish)
New translations for {languageName} [ci skip]
2025-12-08 21:19:31 +01:00
Flyinghead 0220207545 strprintf utility. localize exception messages. add sv.po (Swedish) 2025-12-08 16:22:26 +01:00
flyinghead aee4cd34fc Crowdin Translations (#2132)
* New translations flycast.pot (French)
New translations for {languageName} [ci skip]
* New translations flycast.pot (Swedish)
New translations for {languageName} [ci skip]
* New translations flycast.pot (English, United Kingdom)
New translations for {languageName} [ci skip]
2025-12-08 16:15:17 +01:00
flyinghead a99cce27bb Crowdin Translations (#2131)
* New translations flycast.pot (French)
New translations for {languageName} [ci skip]
2025-12-08 14:11:01 +01:00
Flyinghead b2941a8593 ui: more l10n. add plural and context support. no l10n for debug stuff 2025-12-08 11:50:51 +01:00
flyinghead 3ad072f9aa New translations flycast.pot (French)
New translations for {languageName} [ci skip]
2025-12-08 09:37:39 +01:00
flyinghead 14374084d5 New translations flycast.pot (French)
New translations for {languageName} [ci skip]
2025-12-07 23:18:42 +01:00
Flyinghead 5c7a241897 uwp build fix 2025-12-07 21:39:13 +01:00
Flyinghead 7fcc5fa2f7 ui: update .pot and .po 2025-12-07 20:37:35 +01:00
Flyinghead cf2fe027f7 ci: bsd,switch,uwp build fixes. Better workflow names 2025-12-07 20:33:57 +01:00
Flyinghead b13685f0bb ui: switch to tinygettext for i18n 2025-12-07 19:57:03 +01:00
Flyinghead cf7b6bdcb0 ui: more localization 2025-12-06 17:11:48 +01:00
Flyinghead 894c61ed64 libretro build fix 2025-12-04 21:41:36 +01:00
Flyinghead 82cadfde22 win: use GetLocaleInfoEx and localtime. android: build fix 2025-12-04 21:29:20 +01:00
Flyinghead 1586cbaea9 android: use system getifaddrs if available (api 24+) 2025-12-04 18:21:17 +01:00
Flyinghead 2113e4e249 localization support WIP. rewrite ini file reader/writer and more.
Support for localized dates, collation and messages.
Rewrite ini file reader/writer and command line parser.
Standardize all config stuff in the config namespace.
2025-12-04 18:17:13 +01:00
259 changed files with 62884 additions and 5251 deletions

No files matched your search

+11 -7
View File
@@ -1,6 +1,10 @@
name: Android CI
name: Android
on: [push, pull_request]
on:
push:
branches-ignore:
- 'l10n_**'
pull_request:
jobs:
build:
@@ -13,18 +17,18 @@ jobs:
sudo apt-get update
sudo apt-get -y install ccache cmake libcurl4-openssl-dev ninja-build
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: recursive
- uses: actions/setup-java@v4
- uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'temurin'
cache: 'gradle'
- uses: actions/cache@v4
- uses: actions/cache@v5
with:
path: ~/.ccache
key: android-ccache-${{ github.sha }}
@@ -47,7 +51,7 @@ jobs:
SENTRY_UPLOAD_URL: ${{ secrets.SENTRY_UPLOAD_URL }}
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v6
with:
name: flycast-release.apk
path: shell/android-studio/flycast/build/outputs/apk/release/flycast-release.apk
@@ -102,4 +106,4 @@ jobs:
shell: bash
env:
SENTRY_TOKEN: ${{ secrets.SENTRY_TOKEN }}
if: ${{ env.SENTRY_TOKEN != '' }}
if: ${{ env.SENTRY_TOKEN != '' && github.event_name == 'push' }}
+14 -9
View File
@@ -1,6 +1,10 @@
name: BSD CI
name: BSD
on: [push, pull_request]
on:
push:
branches-ignore:
- 'l10n_**'
pull_request:
jobs:
build:
@@ -11,32 +15,33 @@ jobs:
matrix:
operating_system: [ freebsd, netbsd, openbsd ]
architecture: [ arm64, x86-64 ]
libretro: [ OFF, ON ]
include:
- operating_system: freebsd
version: '14.3'
version: '15.0'
pkginstall: sudo pkg update && sudo pkg install -y alsa-lib ccache cmake evdev-proto git libao libevdev libudev-devd libzip lua54 miniupnpc ninja pkgconf pulseaudio sdl2 libcdio
- operating_system: netbsd
version: '10.1'
pkginstall: sudo pkgin update && sudo pkgin -y install alsa-lib ccache clang cmake git libao libzip lua54 miniupnpc ninja-build pkgconf pulseaudio SDL2 libcdio
- operating_system: openbsd
version: '7.7'
version: '7.8'
pkginstall: sudo pkg_add -u && sudo pkg_add ccache cmake git libao libzip miniupnpc ninja pkgconf pulseaudio sdl2 libcdio
exclude:
- architecture: arm64
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: true
submodules: recursive
- uses: actions/cache@v4
- uses: actions/cache@v5
with:
path: ${{ env.CCACHE_DIR }}
key: ccache-${{ matrix.operating_system }}-${{ matrix.architecture }}-${{ github.sha }}
restore-keys: ccache-${{ matrix.operating_system }}-${{ matrix.architecture }}-
- uses: cross-platform-actions/action@v0.29.0
- uses: cross-platform-actions/action@v0.31.0
with:
operating_system: ${{ matrix.operating_system }}
architecture: ${{ matrix.architecture }}
@@ -44,5 +49,5 @@ jobs:
environment_variables: CCACHE_DIR
run: |
${{ matrix.pkginstall }}
cmake -B build -DUSE_LIBCDIO=ON -DCMAKE_BUILD_TYPE=Release -G Ninja
cmake -B build -DUSE_LIBCDIO=ON -DCMAKE_BUILD_TYPE=Release -G Ninja -DLIBRETRO=${{ matrix.libretro }}
cmake --build build --config Release
+13 -7
View File
@@ -1,6 +1,10 @@
name: C/C++ CI
name: Linux, macOS, Windows
on: [push, pull_request]
on:
push:
branches-ignore:
- 'l10n_**'
pull_request:
jobs:
build:
@@ -44,8 +48,10 @@ jobs:
run: |
sudo add-apt-repository ppa:christianrauch/libdecoration
sudo apt-get update
sudo apt-get -y install ccache libao-dev libasound2-dev libevdev-dev libgl1-mesa-dev liblua5.3-dev libminiupnpc-dev libpulse-dev libsdl2-dev libudev-dev libzip-dev ninja-build libcurl4-openssl-dev libcdio-dev libfuse2
sudo apt-get -y install ccache libao-dev libasound2-dev libevdev-dev libgl1-mesa-dev liblua5.3-dev libminiupnpc-dev libpulse-dev libsdl2-dev libudev-dev libzip-dev ninja-build libcurl4-openssl-dev libcdio-dev libfuse2 locales
sudo apt-get -y install libwayland-dev libdecor-0-dev libaudio-dev libjack-dev libsndio-dev libsamplerate0-dev libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxkbcommon-dev libdrm-dev libgbm-dev libgles2-mesa-dev libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev fcitx-libs-dev
sudo sed -i 's/# fr_FR.UTF-8 UTF-8/fr_FR.UTF-8 UTF-8/g' /etc/locale.gen
sudo locale-gen
if: runner.os == 'Linux'
- name: Set up build environment (Windows, MinGW)
@@ -66,7 +72,7 @@ jobs:
arch: ${{ matrix.config.arch }}
if: matrix.config.shell == 'cmd'
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: recursive
@@ -75,7 +81,7 @@ jobs:
run: HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 HOMEBREW_DEVELOPER=1 brew reinstall --build-from-source --formula ./shell/apple/libomp.rb
if: matrix.config.name == 'apple-darwin'
- uses: actions/cache@v4
- uses: actions/cache@v5
with:
path: ${{ env.CCACHE_DIR }}
key: ccache-${{ matrix.config.name }}-${{ github.sha }}
@@ -121,7 +127,7 @@ jobs:
rm artifact/bin/flycast
if: matrix.config.name == 'x86_64-pc-linux-gnu'
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v6
with:
name: flycast-${{ matrix.config.name }}
path: |
@@ -171,5 +177,5 @@ jobs:
shell: bash
env:
SENTRY_TOKEN: ${{ secrets.SENTRY_TOKEN }}
if: ${{ env.SENTRY_TOKEN != '' && (matrix.config.name == 'x86_64-w64-mingw32' || matrix.config.name == 'apple-darwin' || matrix.config.name == 'x86_64-pc-linux-gnu') }}
if: ${{ env.SENTRY_TOKEN != '' && (matrix.config.name == 'x86_64-w64-mingw32' || matrix.config.name == 'apple-darwin' || matrix.config.name == 'x86_64-pc-linux-gnu') && github.event_name == 'push' }}
+3 -3
View File
@@ -15,18 +15,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Setup Java JDK
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: 18
distribution: zulu
- name: Setup Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.10'
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Upload Source
shell: bash
+4 -4
View File
@@ -12,18 +12,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Setup Java JDK
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: 18
distribution: zulu
- name: Setup Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.10'
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal access token.
fetch-depth: 0 # otherwise, there would be errors pushing refs to the destination repository.
@@ -43,7 +43,7 @@ jobs:
git commit -m "Fetch translations & Recreate libretro_core_options_intl.h"
- name: GitHub Push
uses: ad-m/github-push-action@v0.8.0
uses: ad-m/github-push-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ github.ref }}
+10 -6
View File
@@ -1,6 +1,10 @@
name: Nintendo Switch CI
name: Nintendo Switch
on: [push, pull_request]
on:
push:
branches-ignore:
- 'l10n_**'
pull_request:
jobs:
build:
@@ -20,15 +24,15 @@ jobs:
sudo apt-get update
sudo apt-get -y install ccache cmake
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: true
submodules: recursive
- name: Mark git repository as safe
run: git config --global --add safe.directory $PWD
- uses: actions/cache@v4
- uses: actions/cache@v5
with:
path: ~/.ccache
key: ccache-switch-${{ matrix.config.name }}-${{ github.sha }}
@@ -39,7 +43,7 @@ jobs:
$DEVKITPRO/portlibs/switch/bin/aarch64-none-elf-cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=artifact -G Ninja ${{ matrix.config.cmakeArgs }}
cmake --build build --config Release --target install
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v6
with:
name: flycast-switch-${{ matrix.config.name }}
path: |
+9 -5
View File
@@ -1,6 +1,10 @@
name: Windows UWP CI
name: Xbox, UWP
on: [push, pull_request]
on:
push:
branches-ignore:
- 'l10n_**'
pull_request:
jobs:
build:
@@ -12,10 +16,10 @@ jobs:
arch: [ Win32, x64, ARM64 ]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: true
submodules: recursive
- name: CMake
run: |
@@ -39,7 +43,7 @@ jobs:
run: signtool sign /f shell\uwp\sign_cert.pfx /p '${{ secrets.SIGN_CERT_PWD }}' /v /fd SHA256 build\artifact\flycast.appx
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push'
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v6
with:
name: flycast-uwp-${{ matrix.arch }}
path: build/artifact
+5 -2
View File
@@ -40,12 +40,15 @@
.core-defs-android:
extends: .core-defs
script:
- cmake -DLIBRETRO=ON -DUSE_OPENMP=OFF -DANDROID_PLATFORM=android-$API_LEVEL -DCMAKE_TOOLCHAIN_FILE=$NDK_ROOT/build/cmake/android.toolchain.cmake -DANDROID_STL=c++_static -DANDROID_ABI=$ANDROID_ABI -DANDROID_ARM_MODE=arm "$CMAKE_SOURCE_ROOT" -B$BUILD_DIR
- echo y | /android-sdk-linux/cmdline-tools/latest/bin/sdkmanager "ndk;${ANDROID_NDK_VERSION}"
- cmake -DLIBRETRO=ON -DUSE_OPENMP=OFF -DANDROID_PLATFORM=android-$API_LEVEL -DCMAKE_TOOLCHAIN_FILE=$NDK_ROOT/build/cmake/android.toolchain.cmake -DANDROID_STL=c++_static -DANDROID_ABI=$ANDROID_ABI -DANDROID_ARM_MODE=arm -DANDROID_WEAK_API_DEFS=ON "$CMAKE_SOURCE_ROOT" -B$BUILD_DIR
- cmake --build $BUILD_DIR --target ${CORENAME}_libretro --config Release -- -j $NUMPROC
- mv $BUILD_DIR/${CORENAME}_libretro.so $LIBNAME
- if [ $STRIP_CORE_LIB -eq 1 ]; then $NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip $LIBNAME; fi
variables:
API_LEVEL: 18
ANDROID_NDK_VERSION: 29.0.14206865
API_LEVEL: 21
NDK_ROOT: /android-sdk-linux/ndk/$ANDROID_NDK_VERSION
# Inclusion templates, required for the build to work
include:
+3 -3
View File
@@ -56,6 +56,6 @@
[submodule "core/deps/DreamPicoPort-API"]
path = core/deps/DreamPicoPort-API
url = https://github.com/OrangeFox86/DreamPicoPort-API.git
[submodule "core/deps/libusb-cmake"]
path = core/deps/libusb-cmake
url = https://github.com/libusb/libusb-cmake.git
[submodule "core/deps/tinygettext"]
path = core/deps/tinygettext
url = https://github.com/flyinghead/tinygettext
+25 -18
View File
@@ -43,7 +43,7 @@ endif()
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR NINTENDO_SWITCH)
if(CMAKE_SYSTEM_NAME MATCHES "NetBSD|Haiku" OR NINTENDO_SWITCH)
set(CMAKE_CXX_EXTENSIONS ON)
else()
set(CMAKE_CXX_EXTENSIONS OFF)
@@ -61,7 +61,7 @@ if(CMAKE_CXX_COMPILER MATCHES "webos" OR
endif()
set(USE_HOST_SDL_DEFAULT OFF)
if(NOT APPLE AND (NOT UNIX OR CMAKE_SYSTEM_NAME MATCHES "(FreeBSD|OpenBSD|NetBSD)"))
if(NOT APPLE AND (NOT UNIX OR CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD|OpenBSD|NetBSD|Haiku"))
set(USE_HOST_SDL_DEFAULT ON)
endif()
@@ -110,6 +110,7 @@ include(CMakeRC)
if(ENABLE_CTEST)
include(CTest)
add_compile_definitions(FLYCAST_TEST_FILES="${CMAKE_CURRENT_SOURCE_DIR}/tests/files")
endif()
if(IOS AND NOT LIBRETRO)
@@ -186,7 +187,9 @@ elseif(LIBRETRO)
endif()
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/shell/libretro/libretro.osx.def")
else()
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
if(NOT CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
endif()
endif()
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "flycast_libretro"
XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO"
@@ -195,7 +198,6 @@ elseif(LIBRETRO)
set(CMAKE_SHARED_LIBRARY_PREFIX "")
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
target_compile_definitions(${PROJECT_NAME} PRIVATE LIBRETRO)
target_compile_options(${PROJECT_NAME} PRIVATE -Wno-misleading-indentation)
if(APPLE)
find_library(FOUNDATION Foundation)
target_link_libraries(${PROJECT_NAME} PRIVATE ${FOUNDATION})
@@ -217,6 +219,14 @@ elseif(LIBRETRO)
target_link_libraries(${PROJECT_NAME} PRIVATE "-lGLESv2")
endif()
elseif(USE_OPENGL)
if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD|OpenBSD|NetBSD")
find_package(OpenGL REQUIRED)
if(CMAKE_VERSION VERSION_LESS 3.29.0)
target_include_directories(${PROJECT_NAME} PRIVATE ${OPENGL_INCLUDE_DIR})
else()
target_include_directories(${PROJECT_NAME} PRIVATE ${OPENGL_INCLUDE_DIRS})
endif()
endif()
target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_OPENGL)
if(APPLE)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-framework,OpenGL")
@@ -474,7 +484,7 @@ if(NOT LIBRETRO)
if (WINDOWS_STORE)
# Use winrt implementation of the DreamPicoPort-API
option(DREAMPICOPORT_WITH_LIBUSB "Use libusb to access DreamPicoPort-API" OFF)
elseif (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
elseif (CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD|Haiku")
# A version of libusb is installed in FreeBSD
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBUSB REQUIRED libusb-1.0)
@@ -483,12 +493,12 @@ if(NOT LIBRETRO)
set(DREAMPICOPORT_LIBUSB_INCLUDE_DIRS "${LIBUSB_INCLUDE_DIRS}" CACHE STRING "Path to libusb include directory, when applicable")
else()
# libusb
add_subdirectory(core/deps/libusb-cmake)
add_subdirectory(core/deps/DreamPicoPort-API/ext/libusb-cmake)
endif()
# DreamPicoPort-API
option(DREAMPICOPORT_ADD_LIBUSB "Add internal libusb library" OFF) # Already included above, when applicable
add_subdirectory(core/deps/DreamPicoPort-API)
add_subdirectory(core/deps/DreamPicoPort-API EXCLUDE_FROM_ALL)
target_link_libraries(${PROJECT_NAME} PRIVATE dream_pico_port_api)
endif()
@@ -783,6 +793,11 @@ if(NOT LIBRETRO AND NOT NINTENDO_SWITCH)
endif()
endif()
set(TINYGETTEXT_WO_ICONV ON)
add_subdirectory(core/deps/tinygettext EXCLUDE_FROM_ALL)
target_include_directories(${PROJECT_NAME} PRIVATE core/deps/tinygettext/include)
target_link_libraries(${PROJECT_NAME} PRIVATE tinygettext::tinygettext)
target_include_directories(${PROJECT_NAME} PRIVATE core/deps/picotcp/include core/deps/picotcp/modules)
target_sources(${PROJECT_NAME} PRIVATE
core/deps/picotcp/include/arch/pico_arm9.h
@@ -943,7 +958,7 @@ add_subdirectory(core/lua)
add_subdirectory(core/profiler)
add_subdirectory(core/wsi)
include(cmake/resources.cmake)
include(resources/resources.cmake)
if(WIN32)
target_sources(${PROJECT_NAME} PRIVATE
@@ -1355,17 +1370,9 @@ if(NOT LIBRETRO)
if(ANDROID)
target_compile_definitions(${PROJECT_NAME} PRIVATE GLES GLES3)
target_sources(${PROJECT_NAME} PRIVATE
shell/android-studio/flycast/src/main/jni/src/Android.cpp
shell/android-studio/flycast/src/main/jni/src/android_gamepad.h
shell/android-studio/flycast/src/main/jni/src/android_storage.h
shell/android-studio/flycast/src/main/jni/src/http_client.h
shell/android-studio/flycast/src/main/jni/src/jni_util.h
shell/android-studio/flycast/src/main/jni/src/android_input.cpp
shell/android-studio/flycast/src/main/jni/src/android_keyboard.h)
add_subdirectory(shell/android-studio/flycast/src/main/jni/src)
target_link_libraries(${PROJECT_NAME} PRIVATE android log)
elseif(APPLE)
string(TIMESTAMP YEAR "%Y")
string(REGEX MATCH "^[^-]+" TAG_VERSION ${GIT_VERSION})
+23 -22
View File
@@ -29,6 +29,7 @@
#include "oslib/oslib.h"
#include "emulator.h"
#include "stdclass.h"
#include "oslib/i18n.h"
#include <cassert>
#include <rc_client.h>
#include <rc_hash.h>
@@ -283,7 +284,8 @@ std::pair<std::string, bool> Achievements::getCachedImage(const char *url)
}
else
{
std::stringstream stream;
std::ostringstream stream;
stream.imbue(std::locale::classic());
stream << std::hex << hash << ".png";
return std::make_pair(cachePath + stream.str(), false);
}
@@ -303,7 +305,8 @@ std::string Achievements::getOrDownloadImage(const char *url)
int rc = http::get(url, content, content_type);
if (!http::success(rc))
return {};
std::stringstream stream;
std::ostringstream stream;
stream.imbue(std::locale::classic());
stream << std::hex << hash << ".png";
std::string localPath = cachePath + stream.str();
FILE *f = nowide::fopen(localPath.c_str(), "wb");
@@ -338,9 +341,10 @@ void Achievements::authenticationSuccess(const rc_client_user_t *user)
int rc = rc_client_user_get_image_url(user, url.data(), url.size());
if (rc == RC_OK)
{
asyncTask([this, url]() {
asyncTask([this, url]()
{
std::string image = getOrDownloadImage(url.c_str());
std::string text = "User " + config::AchievementsUserName.get() + " authenticated";
std::string text = strprintf(i18n::T("User %s authenticated"), config::AchievementsUserName.get().c_str());
notifier.notify(Notification::Login, image, text);
});
}
@@ -356,7 +360,7 @@ void Achievements::clientLoginWithTokenCallback(int result, const char *error_me
if (result != RC_OK)
{
WARN_LOG(COMMON, "RA Login failed: %s", error_message);
notifier.notify(Notification::Login, "", "RetroAchievements authentication failed", error_message);
notifier.notify(Notification::Login, "", i18n::Ts("RetroAchievements authentication failed"), error_message);
return;
}
achievements->authenticationSuccess(rc_client_get_user_info(client));
@@ -391,7 +395,7 @@ void Achievements::clientLoginWithPasswordCallback(int result, const char *error
if (!user || !user->token)
{
WARN_LOG(COMMON, "RA: rc_client_get_user_info() returned NULL");
promise->set_exception(std::make_exception_ptr(FlycastException("No user token returned")));
promise->set_exception(std::make_exception_ptr(FlycastException(i18n::Ts("No user token returned"))));
delete promise;
return;
}
@@ -543,11 +547,11 @@ void Achievements::clientEventHandler(const rc_client_event_t* event, rc_client_
// TODO case RC_CLIENT_EVENT_LEADERBOARD_SCOREBOARD:
case RC_CLIENT_EVENT_DISCONNECTED:
notifyError("RetroAchievements disconnected");
notifyError(i18n::Ts("RetroAchievements disconnected"));
break;
case RC_CLIENT_EVENT_RECONNECTED:
notifyError("RetroAchievements reconnected");
notifyError(i18n::Ts("RetroAchievements reconnected"));
break;
default:
@@ -577,7 +581,7 @@ void Achievements::handleUnlockEvent(const rc_client_event_t *event)
{
asyncTask([this, url, title, description]() {
std::string image = getOrDownloadImage(url.c_str());
std::string text = "Achievement " + title + " unlocked!";
std::string text = strprintf(i18n::T("Achievement %s unlocked!"), title.c_str());
notifier.notify(Notification::Login, image, text, description);
});
}
@@ -617,21 +621,21 @@ void Achievements::handleLeaderboardStarted(const rc_client_event_t *event)
{
const rc_client_leaderboard_t *leaderboard = event->leaderboard;
INFO_LOG(COMMON, "RA: Leaderboard started: %s", leaderboard->title);
std::string text = "Leaderboard " + std::string(leaderboard->title) + " started";
std::string text = strprintf(i18n::T("Leaderboard %s started"), leaderboard->title);
notifier.notify(Notification::Unlocked, "", text, leaderboard->description);
}
void Achievements::handleLeaderboardFailed(const rc_client_event_t *event)
{
const rc_client_leaderboard_t *leaderboard = event->leaderboard;
INFO_LOG(COMMON, "RA: Leaderboard failed: %s", leaderboard->title);
std::string text = "Leaderboard " + std::string(leaderboard->title) + " failed";
std::string text = strprintf(i18n::T("Leaderboard %s failed"), leaderboard->title);
notifier.notify(Notification::Unlocked, "", text, leaderboard->description);
}
void Achievements::handleLeaderboardSubmitted(const rc_client_event_t *event)
{
const rc_client_leaderboard_t *leaderboard = event->leaderboard;
INFO_LOG(COMMON, "RA: Leaderboard submitted: %s", leaderboard->title);
std::string text = "Leaderboard " + std::string(leaderboard->title) + " submitted";
std::string text = strprintf(i18n::T("Leaderboard %s submitted"), leaderboard->title);
notifier.notify(Notification::Unlocked, "", text, leaderboard->description);
}
void Achievements::handleShowLeaderboardTracker(const rc_client_event_t *event)
@@ -656,12 +660,10 @@ void Achievements::handleUpdateLeaderboardTracker(const rc_client_event_t *event
void Achievements::handleGameCompleted(const rc_client_event_t *event)
{
const rc_client_game_t* game = rc_client_get_game_info(rc_client);
std::string text1 = (rc_client_get_hardcore_enabled(rc_client) ? "Mastered " : "Completed ") + std::string(game->title);
std::string text1 = strprintf(rc_client_get_hardcore_enabled(rc_client) ? i18n::T("Mastered %s") : i18n::T("Completed %s"), game->title);
rc_client_user_game_summary_t summary;
rc_client_get_user_game_summary(rc_client, &summary);
std::stringstream ss;
ss << summary.num_unlocked_achievements << " achievements, " << summary.points_unlocked << " points";
std::string text2(ss.str());
std::string text2 = strprintf(i18n::T("%d achievements, %d points"), summary.num_unlocked_achievements, summary.points_unlocked);
std::string text3 = rc_client_get_user_info(rc_client)->display_name;
std::string url(512, '\0');
if (rc_client_game_get_image_url(game, url.data(), url.size()) != RC_OK)
@@ -907,15 +909,14 @@ void Achievements::gameLoaded(int result, const char *errorMessage)
rc_client_get_user_game_summary(rc_client, &summary);
std::string text2;
if (summary.num_core_achievements > 0)
text2 = "You have " + std::to_string(summary.num_unlocked_achievements)
+ " of " + std::to_string(summary.num_core_achievements) + " achievements unlocked.";
text2 = strprintf(i18n::T("You have %d of %d achievements unlocked."), summary.num_unlocked_achievements, summary.num_core_achievements);
else
text2 = "This game has no achievements.";
text2 = i18n::Ts("This game has no achievements.");
asyncTask([this, url, text1, text2]() {
std::string image;
if (!url.empty())
image = getOrDownloadImage(url.c_str());
std::string text3 = settings.raHardcoreMode ? "Hardcore Mode" : "";
std::string text3 = settings.raHardcoreMode ? i18n::T("Hardcore Mode") : "";
notifier.notify(Notification::Login, image, text1, text2, text3);
});
}
@@ -946,14 +947,14 @@ void Achievements::diskChange()
rc_client_begin_change_media_from_hash(rc_client, hash.c_str(), [](int result, const char *errorMessage, rc_client_t *client, void *userdata) {
if (result == RC_HARDCORE_DISABLED) {
settings.raHardcoreMode = false;
notifier.notify(Notification::Login, "", "Hardcore mode disabled", "Unrecognized media inserted");
notifier.notify(Notification::Login, "", i18n::Ts("Hardcore mode disabled"), i18n::Ts("Unrecognized media inserted"));
}
else if (result != RC_OK)
{
settings.raHardcoreMode = false;
if (errorMessage == nullptr)
errorMessage = rc_error_str(result);
notifier.notify(Notification::Login, "", "Media change failed", errorMessage);
notifier.notify(Notification::Login, "", i18n::Ts("Media change failed"), errorMessage);
}
}, this);
}
+3 -3
View File
@@ -19,9 +19,9 @@
along with reicast. If not, see <https://www.gnu.org/licenses/>.
*/
#include "7zArchive.h"
#include "deps/lzma/7z.h"
#include "deps/lzma/7zCrc.h"
#include "deps/lzma/Alloc.h"
#include "lzma/7z.h"
#include "lzma/7zCrc.h"
#include "lzma/Alloc.h"
#include <cstring>
+2 -2
View File
@@ -21,8 +21,8 @@
#pragma once
#include "archive.h"
#include "deps/lzma/7z.h"
#include "deps/lzma/7zFile.h"
#include "lzma/7z.h"
#include "lzma/7zFile.h"
#include <algorithm>
#include <cstring>
+1 -1
View File
@@ -20,7 +20,7 @@ public:
{
snd_pcm_hw_params_t *params;
std::string device = cfgLoadStr("alsa", "device", "");
std::string device = config::loadStr("alsa", "device");
int rc = -1;
if (!device.empty() && device != "auto") {
+6 -2
View File
@@ -1,5 +1,5 @@
#include "audiostream.h"
#include "oslib/i18n.h"
#include <chrono>
#include <thread>
@@ -9,7 +9,7 @@ class NullAudioBackend : public AudioBackend
public:
NullAudioBackend()
: AudioBackend("null", "No Audio") {}
: AudioBackend("null", Tnop("No Audio")) {}
bool init() override
{
@@ -48,6 +48,10 @@ public:
return samples;
}
std::string getName() const override {
return i18n::Ts(name);
}
private:
the_clock::time_point last_time;
};
+8 -2
View File
@@ -19,12 +19,13 @@
#ifdef USE_OBOE
#include "audiostream.h"
#include "cfg/option.h"
#include "stdclass.h"
#include "oslib/i18n.h"
#include <oboe/Oboe.h>
#include <vector>
#include <algorithm>
#include <atomic>
#include <memory>
#include "stdclass.h"
class OboeBackend : AudioBackend
{
@@ -77,7 +78,7 @@ class OboeBackend : AudioBackend
public:
OboeBackend()
: AudioBackend("Oboe", "Automatic AAudio / OpenSL selection"), audioCallback(this), errorCallback(this) {}
: AudioBackend("Oboe", Tnop("Automatic AAudio / OpenSL selection")), audioCallback(this), errorCallback(this) {}
bool init() override
{
@@ -195,6 +196,11 @@ public:
}
return std::max(0, result.value());
}
std::string getName() const override {
return i18n::Ts(name);
}
};
static OboeBackend oboeBackend;
+1 -1
View File
@@ -66,7 +66,7 @@ class SDLAudioBackend : AudioBackend
public:
SDLAudioBackend()
: AudioBackend("sdl2", "Simple DirectMedia Layer 2 Audio") {}
: AudioBackend("sdl2", "Simple DirectMedia Layer 2") {}
bool init() override
{
+3 -3
View File
@@ -35,7 +35,7 @@ AudioBackend *AudioBackend::getBackend(const std::string& slug)
autoBackend = sdlBackend;
if (autoBackend == nullptr)
autoBackend = backends->front();
INFO_LOG(AUDIO, "Auto-selected audio backend \"%s\" (%s).", autoBackend->slug.c_str(), autoBackend->name.c_str());
INFO_LOG(AUDIO, "Auto-selected audio backend \"%s\" (%s).", autoBackend->slug.c_str(), autoBackend->getName().c_str());
return autoBackend;
}
@@ -75,7 +75,7 @@ void InitAudio()
}
if (currentBackend != nullptr)
{
INFO_LOG(AUDIO, "Initializing audio backend \"%s\" (%s)...", currentBackend->slug.c_str(), currentBackend->name.c_str());
INFO_LOG(AUDIO, "Initializing audio backend \"%s\" (%s)...", currentBackend->slug.c_str(), currentBackend->getName().c_str());
if (!currentBackend->init())
{
currentBackend = nullptr;
@@ -114,7 +114,7 @@ void TermAudio()
StopAudioRecording();
audio_recording_started = rec_started;
currentBackend->term();
INFO_LOG(AUDIO, "Terminating audio backend \"%s\" (%s)...", currentBackend->slug.c_str(), currentBackend->name.c_str());
INFO_LOG(AUDIO, "Terminating audio backend \"%s\" (%s)...", currentBackend->slug.c_str(), currentBackend->getName().c_str());
currentBackend = nullptr;
}
+2 -1
View File
@@ -35,7 +35,7 @@ public:
virtual void termRecord() {}
std::string slug;
std::string name;
virtual std::string getName() const { return name; }
static size_t getCount() { return backends == nullptr ? 0 : backends->size(); }
static AudioBackend *getBackend(size_t index) { return backends == nullptr ? nullptr : (*backends)[index]; }
@@ -46,6 +46,7 @@ protected:
: slug(slug), name(name) {
registerAudioBackend(this);
}
std::string name;
private:
static void registerAudioBackend(AudioBackend *backend)
+59 -69
View File
@@ -1,37 +1,33 @@
#include "cfg.h"
#include "ini.h"
#include "stdclass.h"
#include <cerrno>
namespace config
{
static std::string cfgPath;
static bool save_config = true;
static bool autoSave = true;
static bool autoSaving = true;
static IniFile cfgdb;
static emucfg::ConfigFile cfgdb;
static void saveConfigFile()
static void saveFile()
{
FILE* cfgfile = nowide::fopen(cfgPath.c_str(), "wt");
if (!cfgfile)
{
if (!cfgfile) {
WARN_LOG(COMMON, "Error: Unable to open file '%s' for saving", cfgPath.c_str());
}
else
{
else {
cfgdb.save(cfgfile);
std::fclose(cfgfile);
}
}
void cfgSaveStr(const std::string& section, const std::string& key, const std::string& value)
{
cfgdb.set(section, key, value);
if (save_config && autoSave)
saveConfigFile();
static void autoSave() {
if (autoSaving)
saveFile();
}
bool cfgOpen()
bool open()
{
if (get_writable_config_path("").empty())
// Config dir not set (android onboarding)
@@ -41,9 +37,9 @@ bool cfgOpen()
std::string config_path_read = get_readonly_config_path(filename);
cfgPath = get_writable_config_path(filename);
FILE* cfgfile = nowide::fopen(config_path_read.c_str(), "r");
if(cfgfile != NULL) {
cfgdb.parse(cfgfile);
FILE* cfgfile = nowide::fopen(config_path_read.c_str(), "rt");
if (cfgfile != nullptr) {
cfgdb.load(cfgfile);
std::fclose(cfgfile);
}
else
@@ -55,87 +51,81 @@ bool cfgOpen()
{
// Config file didn't exist
INFO_LOG(COMMON, "Creating new empty config file at '%s'", cfgPath.c_str());
saveConfigFile();
}
else
{
// There was some other error (may be a permissions problem or something like that)
save_config = false;
saveFile();
}
}
return true;
}
std::string cfgLoadStr(const std::string& section, const std::string& key, const std::string& def)
{
return cfgdb.get(section, key, def);
void saveStr(const std::string& section, const std::string& key, const std::string& value) {
cfgdb.set(section, key, value);
autoSave();
}
void cfgSaveInt(const std::string& section, const std::string& key, s32 value)
{
cfgSaveStr(section, key, std::to_string(value));
std::string loadStr(const std::string& section, const std::string& key, const std::string& def) {
return cfgdb.getString(section, key, def);
}
s32 cfgLoadInt(const std::string& section, const std::string& key, s32 def)
{
return cfgdb.get_int(section, key, def);
void saveInt(const std::string& section, const std::string& key, int value) {
cfgdb.set(section, key, value);
autoSave();
}
int64_t cfgLoadInt64(const std::string& section, const std::string& key, int64_t def) {
return cfgdb.get_int64(section, key, def);
}
void cfgSaveInt64(const std::string& section, const std::string& key, int64_t value) {
cfgdb.set_int64(section, key, value);
int loadInt(const std::string& section, const std::string& key, int def) {
return cfgdb.getInt(section, key, def);
}
void cfgSaveBool(const std::string& section, const std::string& key, bool value)
{
cfgSaveStr(section, key, value ? "yes" : "no");
int64_t loadInt64(const std::string& section, const std::string& key, int64_t def) {
return cfgdb.getInt64(section, key, def);
}
void saveInt64(const std::string& section, const std::string& key, int64_t value) {
cfgdb.set(section, key, value);
autoSave();
}
bool cfgLoadBool(const std::string& section, const std::string& key, bool def)
{
return cfgdb.get_bool(section, key, def);
void saveBool(const std::string& section, const std::string& key, bool value) {
cfgdb.set(section, key, value);
autoSave();
}
void cfgSetVirtual(const std::string& section, const std::string& key, const std::string& value)
{
bool loadBool(const std::string& section, const std::string& key, bool def) {
return cfgdb.getBool(section, key, def);
}
void setTransient(const std::string& section, const std::string& key, const std::string& value) {
cfgdb.set(section, key, value, true);
}
bool cfgIsVirtual(const std::string& section, const std::string& key)
{
return cfgdb.is_virtual(section, key);
bool isTransient(const std::string& section, const std::string& key) {
return cfgdb.isTransient(section, key);
}
bool cfgHasSection(const std::string& section)
{
return cfgdb.has_section(section);
bool hasSection(const std::string& section) {
return cfgdb.hasSection(section);
}
void cfgDeleteSection(const std::string& section)
{
cfgdb.delete_section(section);
void deleteSection(const std::string& section) {
cfgdb.deleteSection(section);
}
void cfgDeleteEntry(const std::string& section, const std::string& key)
{
cfgdb.delete_entry(section, key);
void deleteEntry(const std::string& section, const std::string& key) {
cfgdb.deleteEntry(section, key);
}
void cfgSetAutoSave(bool autoSave)
void setAutoSave(bool autoSaving)
{
::autoSave = autoSave;
if (autoSave)
saveConfigFile();
config::autoSaving = autoSaving;
if (autoSaving)
saveFile();
}
void cfgSaveFloat(const std::string& section, const std::string& key, float value)
{
cfgdb.set_float(section, key, value);
void saveFloat(const std::string& section, const std::string& key, float value) {
cfgdb.set(section, key, value);
autoSave();
}
float cfgLoadFloat(const std::string& section, const std::string& key, float def)
{
return cfgdb.get_float(section, key, def);
float loadFloat(const std::string& section, const std::string& key, float def) {
return cfgdb.getFloat(section, key, def);
}
} // namespace config
+23 -19
View File
@@ -1,24 +1,28 @@
#pragma once
#include "types.h"
bool cfgOpen();
s32 cfgLoadInt(const std::string& section, const std::string& key, s32 def);
void cfgSaveInt(const std::string& section, const std::string& key, s32 value);
int64_t cfgLoadInt64(const std::string& section, const std::string& key, int64_t def);
void cfgSaveInt64(const std::string& section, const std::string& key, int64_t value);
std::string cfgLoadStr(const std::string& section, const std::string& key, const std::string& def);
void cfgSaveStr(const std::string& section, const std::string& key, const std::string& value);
void cfgSaveBool(const std::string& section, const std::string& key, bool value);
bool cfgLoadBool(const std::string& section, const std::string& key, bool def);
void cfgSaveFloat(const std::string& section, const std::string& key, float value);
float cfgLoadFloat(const std::string& section, const std::string& key, float def);
void cfgSetVirtual(const std::string& section, const std::string& key, const std::string& value);
bool cfgIsVirtual(const std::string& section, const std::string& key);
namespace config
{
void ParseCommandLine(int argc, char *argv[]);
bool open();
int loadInt(const std::string& section, const std::string& key, int def = 0);
void saveInt(const std::string& section, const std::string& key, int value);
int64_t loadInt64(const std::string& section, const std::string& key, int64_t def = 0);
void saveInt64(const std::string& section, const std::string& key, int64_t value);
std::string loadStr(const std::string& section, const std::string& key, const std::string& def = {});
void saveStr(const std::string& section, const std::string& key, const std::string& value);
bool loadBool(const std::string& section, const std::string& key, bool def = false);
void saveBool(const std::string& section, const std::string& key, bool value);
float loadFloat(const std::string& section, const std::string& key, float def = 0.f);
void saveFloat(const std::string& section, const std::string& key, float value);
void setTransient(const std::string& section, const std::string& key, const std::string& value);
bool isTransient(const std::string& section, const std::string& key);
void cfgSetAutoSave(bool autoSave);
bool cfgHasSection(const std::string& section);
void cfgDeleteSection(const std::string& section);
void cfgDeleteEntry(const std::string& section, const std::string& key);
void parseCommandLine(int argc, const char * const argv[]);
void setAutoSave(bool autoSave);
bool hasSection(const std::string& section);
void deleteSection(const std::string& section);
void deleteEntry(const std::string& section, const std::string& key);
}
+91 -119
View File
@@ -1,143 +1,115 @@
/*
Command line parsing
*/
Copyright 2025 flyinghead
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#include "cfg/cfg.h"
#include "stdclass.h"
#include <cstdio>
#include <cstring>
#include "cfg/cfg.h"
#include "stdclass.h"
static int setconfig(char *arg[], int cl)
namespace config
{
int rv=0;
if (cl < 1)
static void usage(const char *exe)
{
fprintf(stderr, "Usage: %s [option]... [<rom path>]\n", exe);
fprintf(stderr, "Options:\n");
fprintf(stderr, "-config section:key=value,... set a transient config value.\n");
fprintf(stderr, " Transient config values won't be saved to emu.cfg.\n");
fprintf(stderr, "-help display this help\n");
}
static void parseConfigOption(const std::string& str)
{
size_t pos = 0;
while (pos < str.length())
{
WARN_LOG(COMMON, "-config : invalid number of parameters, format is section:key=value,section:key=value,...");
return rv;
size_t comma = str.find(',', pos);
if (comma == str.npos)
comma = str.length();
std::string option = str.substr(pos, comma - pos);
size_t colpos = option.find(':');
size_t eqpos = option.npos;
if (colpos != option.npos)
{
std::string section = option.substr(0, colpos);
eqpos = option.find('=', colpos);
if (eqpos != option.npos)
{
std::string key = option.substr(colpos + 1, eqpos - (colpos + 1));
std::string value = option.substr(eqpos + 1);
setTransient(section, key, value);
DEBUG_LOG(COMMON, "-config [%s] %s = %s", section.c_str(), key.c_str(), value.c_str());
}
}
if (colpos == option.npos || eqpos == option.npos) {
WARN_LOG(COMMON, "Invalid -config option '%s'. Format is: -config section:key=value,...", str.c_str());
return;
}
pos = comma + 1;
}
std::string value(arg[1]);
for(;;)
{
auto seppos = value.find(':');
if (seppos == std::string::npos)
{
WARN_LOG(COMMON, "-config : invalid parameter %s, format is section:key=value,section:key=value,...", value.c_str());
break;
}
auto eqpos = value.find('=', seppos);
if (eqpos == std::string::npos)
{
WARN_LOG(COMMON, "-config : invalid parameter %s, format is section:key=value,section:key=value,...", value.c_str());
break;
}
auto commapos = value.find(',', eqpos);
std::string sect = trim_ws(value.substr(0, seppos));
std::string key = trim_ws(value.substr(seppos + 1, eqpos - seppos - 1));
auto endofcurrent = (commapos == std::string::npos) ? value.size() : commapos;
std::string next = (commapos == std::string::npos) ? "" : trim_ws(value.substr(endofcurrent + 1, value.size() - endofcurrent));
value = trim_ws(value.substr(eqpos + 1, endofcurrent - eqpos - 1));
if (sect.empty() || key.empty())
{
WARN_LOG(COMMON, "-config : invalid parameter, format is section:key=value,section:key=value,...");
break;
}
INFO_LOG(COMMON, "Virtual cfg %s:%s=%s", sect.c_str(), key.c_str(), value.c_str());
cfgSetVirtual(sect, key, value);
if (commapos == std::string::npos)
rv++;
if (commapos != std::string::npos)
{
value = next;
continue;
}
else if (cl>=3 && strcmp(arg[2],",")==0)
{
cl-=2;
arg+=2;
rv++;
continue;
}
else
break;
}
return rv;
}
static int showhelp()
{
printf("Usage: flycast [OPTION]... [CONTENT]\n\n");
printf("Options:\n");
printf("-config section:key=value add a virtual config value;\n");
printf(" virtual config values won't be saved to the .cfg file\n");
printf(" unless a different value is written to them\n");
printf("-help display this help\n");
exit(0);
return 0;
}
void ParseCommandLine(int argc,char* argv[])
void parseCommandLine(int argc, const char * const argv[])
{
settings.content.path.clear();
int cl=argc-2;
char** arg=argv+1;
while(cl>=0)
const char *exe = argv[0];
for (int i = 1; i < argc; i++)
{
if (stricmp(*arg,"-help")==0 || stricmp(*arg,"--help")==0)
{
showhelp();
if (!strcmp(argv[i], "-help") || !strcmp(argv[i], "--help")) {
usage(exe);
exit(0);
}
else if (stricmp(*arg,"-config")==0 || stricmp(*arg,"--config")==0)
if (!strcmp(argv[i], "-config") || !strcmp(argv[i], "--config"))
{
int as=setconfig(arg,cl);
cl-=as;
arg+=as;
if (i < argc - 1)
parseConfigOption(argv[++i]);
continue;
}
#if defined(__APPLE__)
else if (!strncmp(*arg, "-NSDocumentRevisions", 20))
// macOS
if (!strncmp(argv[i], "-NSDocumentRevisions", 20)) {
i++;
continue;
}
if (argv[i][0] == '-') {
WARN_LOG(COMMON, "Ignoring unknown command line option '%s'", argv[i]);
continue;
}
std::string extension = get_file_extension(argv[i]);
if (extension == "cdi" || extension == "chd"
|| extension == "gdi"|| extension == "cue")
{
arg++;
cl--;
INFO_LOG(COMMON, "Using '%s' as CD image", argv[i]);
settings.content.path = argv[i];
}
#endif
else if ((*arg)[0] == '-')
else if (extension == "elf")
{
WARN_LOG(COMMON, "Ignoring unknown command line option '%s'", *arg);
INFO_LOG(COMMON, "Using '%s' as reios elf file", argv[i]);
setTransient("config", "bios.UseReios", "yes");
settings.content.path = argv[i];
}
else
{
char* extension = strrchr(*arg, '.');
if (extension
&& (stricmp(extension, ".cdi") == 0 || stricmp(extension, ".chd") == 0
|| stricmp(extension, ".gdi") == 0 || stricmp(extension, ".cue") == 0))
{
INFO_LOG(COMMON, "Using '%s' as cd image", *arg);
settings.content.path = *arg;
}
else if (extension && stricmp(extension, ".elf") == 0)
{
INFO_LOG(COMMON, "Using '%s' as reios elf file", *arg);
cfgSetVirtual("config", "bios.UseReios", "yes");
settings.content.path = *arg;
}
else
{
INFO_LOG(COMMON, "Using '%s' as rom", *arg);
settings.content.path = *arg;
}
else {
INFO_LOG(COMMON, "Using '%s' as rom", argv[i]);
settings.content.path = argv[i];
}
arg++;
cl--;
if (i < argc - 1)
WARN_LOG(COMMON, "Rest of command line ignored: '%s'...", argv[i + 1]);
break;
}
}
} // namespace config
+301 -298
View File
@@ -1,319 +1,322 @@
/*
Copyright 2025 flyinghead
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ini.h"
#include "types.h"
#include <sstream>
#include "stdclass.h"
namespace emucfg {
namespace config {
/* ConfigEntry */
const std::string& ConfigEntry::get_string() const
const IniFile::Entry *IniFile::getEntry(const std::string& sectionName, const std::string& name) const
{
return this->value;
auto it = sections.find(sectionName);
if (it == sections.end())
return nullptr;
const Section& section = it->second;
auto it2 = section.entries.find(name);
if (it2 == section.entries.end())
return nullptr;
return &it2->second;
}
int ConfigEntry::get_int()
const std::string *IniFile::getRaw(const std::string& section, const std::string& name) const
{
if (strstr(this->value.c_str(), "0x") != NULL)
{
return (int)strtoul(this->value.c_str(), NULL, 16);
}
else
{
return atoi(this->value.c_str());
}
}
int64_t ConfigEntry::get_int64()
{
int base = 10;
if (strstr(this->value.c_str(), "0x") != nullptr)
base = 16;
return (int64_t)strtoll(this->value.c_str(), nullptr, base);
}
bool ConfigEntry::get_bool()
{
if (stricmp(this->value.c_str(), "yes") == 0 ||
stricmp(this->value.c_str(), "true") == 0 ||
stricmp(this->value.c_str(), "on") == 0 ||
stricmp(this->value.c_str(), "1") == 0)
{
return true;
}
else
{
return false;
}
}
float ConfigEntry::get_float()
{
return atof(this->value.c_str());
}
/* ConfigSection */
bool ConfigSection::has_entry(const std::string& name)
{
return (this->entries.count(name) == 1);
}
ConfigEntry* ConfigSection::get_entry(const std::string& name)
{
if(this->has_entry(name))
{
return &this->entries[name];
}
return NULL;
}
void ConfigSection::set(const std::string& name, const std::string& value)
{
ConfigEntry new_entry = { value };
this->entries[name] = new_entry;
}
void ConfigSection::delete_entry(const std::string& name)
{
this->entries.erase(name);
}
/* ConfigFile */
ConfigSection* ConfigFile::add_section(const std::string& name, bool is_virtual)
{
ConfigSection new_section;
if (is_virtual)
{
this->virtual_sections.insert(std::make_pair(name, new_section));
return &this->virtual_sections[name];
}
this->sections.insert(std::make_pair(name, new_section));
return &this->sections[name];
}
bool ConfigFile::has_section(const std::string& name)
{
return (this->virtual_sections.count(name) == 1 || this->sections.count(name) == 1);
}
bool ConfigFile::has_entry(const std::string& section_name, const std::string& entry_name)
{
ConfigSection* section = this->get_section(section_name, true);
if ((section != NULL) && section->has_entry(entry_name))
{
return true;
}
section = this->get_section(section_name, false);
return ((section != NULL) && section->has_entry(entry_name));
}
ConfigSection* ConfigFile::get_section(const std::string& name, bool is_virtual)
{
if(is_virtual)
{
if (this->virtual_sections.count(name) == 1)
{
return &this->virtual_sections[name];
}
}
else
{
if (this->sections.count(name) == 1)
{
return &this->sections[name];
}
}
return NULL;
}
ConfigEntry* ConfigFile::get_entry(const std::string& section_name, const std::string& entry_name)
{
ConfigSection* section = this->get_section(section_name, true);
if(section != NULL && section->has_entry(entry_name))
{
return section->get_entry(entry_name);
}
section = this->get_section(section_name, false);
if(section != NULL)
{
return section->get_entry(entry_name);
}
return NULL;
}
std::string ConfigFile::get(const std::string& section_name, const std::string& entry_name, const std::string& default_value)
{
ConfigEntry* entry = this->get_entry(section_name, entry_name);
if (entry == NULL)
{
return default_value;
}
else
{
return entry->get_string();
}
}
int ConfigFile::get_int(const std::string& section_name, const std::string& entry_name, int default_value)
{
ConfigEntry* entry = this->get_entry(section_name, entry_name);
if (entry == NULL)
{
return default_value;
}
else
{
return entry->get_int();
}
}
int64_t ConfigFile::get_int64(const std::string& section_name, const std::string& entry_name, int64_t default_value)
{
ConfigEntry* entry = this->get_entry(section_name, entry_name);
if (entry == NULL)
{
return default_value;
}
else
{
return entry->get_int64();
}
}
bool ConfigFile::get_bool(const std::string& section_name, const std::string& entry_name, bool default_value)
{
ConfigEntry* entry = this->get_entry(section_name, entry_name);
if (entry == NULL)
{
return default_value;
}
else
{
return entry->get_bool();
}
}
float ConfigFile::get_float(const std::string& section_name, const std::string& entry_name, float default_value)
{
ConfigEntry* entry = get_entry(section_name, entry_name);
const Entry *entry = getEntry(section, name);
if (entry == nullptr)
return default_value;
return nullptr;
if (entry->transient)
return &entry->transientValue;
else
return entry->get_float();
return &entry->value;
}
void ConfigFile::set(const std::string& section_name, const std::string& entry_name, const std::string& value, bool is_virtual)
const std::string& IniFile::getString(const std::string& section, const std::string& entry, const std::string& defaultValue) const
{
ConfigSection* section = this->get_section(section_name, is_virtual);
if(section == NULL)
{
section = this->add_section(section_name, is_virtual);
}
section->set(entry_name, value);
const std::string *pValue = getRaw(section, entry);
if (pValue == nullptr)
return defaultValue;
else
return *pValue;
}
void ConfigFile::set_int(const std::string& section_name, const std::string& entry_name, int value, bool is_virtual)
bool IniFile::getBool(const std::string& section, const std::string& entry, bool defaultValue) const
{
std::stringstream str_value;
str_value << value;
this->set(section_name, entry_name, str_value.str(), is_virtual);
}
void ConfigFile::set_int64(const std::string& section_name, const std::string& entry_name, int64_t value, bool is_virtual)
{
std::stringstream str_value;
str_value << value;
this->set(section_name, entry_name, str_value.str(), is_virtual);
}
void ConfigFile::set_bool(const std::string& section_name, const std::string& entry_name, bool value, bool is_virtual)
{
std::string str_value = (value ? "yes" : "no");
this->set(section_name, entry_name, str_value, is_virtual);
}
void ConfigFile::set_float(const std::string& section_name, const std::string& entry_name, float value, bool is_virtual)
{
std::stringstream str_value;
str_value << value;
this->set(section_name, entry_name, str_value.str(), is_virtual);
}
void ConfigFile::parse(FILE* file)
{
if (file == nullptr)
return;
char line[512];
std::string section;
int cline = 0;
while (true)
{
if (std::fgets(line, sizeof(line), file) == nullptr)
break;
cline++;
std::string s(line);
s = trim_ws(s, " \r\n");
if (s.empty())
continue;
if (s.length() >= 3 && s[0] == '[' && s[s.length() - 1] == ']')
{
section = s.substr(1, s.length() - 2);
continue;
}
auto eqpos = s.find('=');
if (eqpos == std::string::npos)
{
WARN_LOG(COMMON, "Malformed entry on config - ignoring line %d: %s", cline, s.c_str());
continue;
}
std::string property = trim_ws(s.substr(0, eqpos));
std::string value = trim_ws(s.substr(eqpos + 1));
if (value.length() >= 2 && value[0] == '"' && value[value.length() - 1] == '"')
value = value.substr(1, value.length() - 2);
set(section, property, value);
}
}
void ConfigFile::save(FILE* file)
{
for (const auto& [section_name, section] : this->sections)
{
if (!section_name.empty())
std::fprintf(file, "[%s]\n", section_name.c_str());
for (const auto& [entry_name, entry] : section.entries)
{
std::fprintf(file, "%s = %s\n", entry_name.c_str(), entry.get_string().c_str());
}
if (!section_name.empty())
std::fputc('\n', file);
}
}
void ConfigFile::delete_section(const std::string& section_name) {
sections.erase(section_name);
virtual_sections.erase(section_name);
}
void ConfigFile::delete_entry(const std::string& section_name, const std::string& entry_name)
{
ConfigSection *section = get_section(section_name, false);
if (section != NULL)
section->delete_entry(entry_name);
}
bool ConfigFile::is_virtual(const std::string& section_name, const std::string& entry_name)
{
ConfigSection *section = get_section(section_name, true);
if (section == nullptr)
const std::string *pValue = getRaw(section, entry);
if (pValue == nullptr)
return defaultValue;
if (!stricmp("yes", pValue->c_str())
|| !stricmp("true", pValue->c_str())
|| !stricmp("on", pValue->c_str())
|| !stricmp("1", pValue->c_str()))
return true;
else
return false;
return section->has_entry(entry_name);
}
} // namespace emucfg
static bool hasHexPrefix(const std::string& s)
{
size_t pos = 0;
while (pos < s.length() - 1 && std::isspace((int8_t)s[pos]))
pos++;
if (pos + 1 >= s.length())
return false;
return (s[pos] == '0' && (s[pos + 1] == 'x' || s[pos + 1] == 'X'));
}
int IniFile::getInt(const std::string& section, const std::string& entry, int defaultValue) const
{
const std::string *pValue = getRaw(section, entry);
if (pValue == nullptr)
return defaultValue;
std::istringstream ss(*pValue);
ss.imbue(std::locale::classic());
if (hasHexPrefix(*pValue))
ss.setf(std::ios_base::hex, std::ios_base::basefield);
else
ss.setf(std::ios_base::dec, std::ios_base::basefield);
unsigned value = defaultValue;
ss >> value;
return (int)value;
}
int64_t IniFile::getInt64(const std::string& section, const std::string& entry, int64_t defaultValue) const
{
const std::string *pValue = getRaw(section, entry);
if (pValue == nullptr)
return defaultValue;
std::istringstream ss(*pValue);
ss.imbue(std::locale::classic());
if (hasHexPrefix(*pValue))
ss.setf(std::ios_base::hex, std::ios_base::basefield);
else
ss.setf(std::ios_base::dec, std::ios_base::basefield);
int64_t value = defaultValue;
ss >> value;
return value;
}
float IniFile::getFloat(const std::string& section, const std::string& entry, float defaultValue) const
{
const std::string *pValue = getRaw(section, entry);
if (pValue == nullptr || pValue->empty())
return defaultValue;
std::istringstream ss(*pValue);
ss.imbue(std::locale::classic());
float value = defaultValue;
ss >> value;
return value;
}
void IniFile::setRaw(const std::string& sectionName, const std::string& entryName, const std::string& value, bool transient)
{
Section& section = sections[sectionName];
Entry& entry = section.entries[entryName];
if (entry.name.empty())
entry.name = entryName;
if (transient) {
entry.transientValue = value;
entry.transient = true;
}
else {
entry.value = value;
}
}
static std::string handleEscapeSeq(const std::string& s)
{
std::string ret;
ret.reserve(s.length());
for (size_t i = 0; i < s.length(); i++)
{
char c = s[i];
if (c == '\\' && i < s.length() - 1)
{
switch (s[++i])
{
case 'n':
ret += '\n';
break;
case 't':
ret += '\t';
break;
case 'r':
ret += '\r';
break;
case 'f':
ret += '\f';
break;
case '\\':
ret += '\\';
break;
default:
WARN_LOG(COMMON, "Unrecognized escape sequence [\\%c] in [%s]", s[i], s.c_str());
ret += '\\';
--i;
break;
}
}
else {
ret += c;
}
}
return ret;
}
void IniFile::load(const std::string& data, bool cEscape)
{
std::istringstream fss(data);
int cline = 1;
std::string curSection;
for (std::string line; std::getline(fss, line); cline++)
{
size_t pos = 0;
while (pos < line.length() && std::isspace((int8_t)line[pos]))
pos++;
if (pos == line.length() || line[pos] == ';')
// Ignore empty lines and comments
continue;
if (line[pos] == '[')
{
// Section
size_t end = line.find(']', ++pos);
if (end == line.npos) {
WARN_LOG(COMMON, "Missing ']' character - ignoring line %d: %s", cline, line.c_str());
continue;
}
curSection = line.substr(pos, end - pos);
if (cEscape)
curSection = handleEscapeSeq(curSection);
}
else
{
// Entry
pos = line.find('=', pos);
if (pos == line.npos) {
WARN_LOG(COMMON, "Malformed entry - ignoring line %d: %s", cline, line.c_str());
continue;
}
std::string entry = trim_ws(line.substr(0, pos), " \t");
if (entry.empty()) {
WARN_LOG(COMMON, "Invalid empty entry name - ignoring line %d: %s", cline, line.c_str());
continue;
}
std::string value = line.substr(pos + 1);
// Space characters before and after the equal sign are for readability, and not part of the entry name or value
if (value[0] == ' ')
value = value.substr(1);
if (value[0] == '"')
{
// quoted value
size_t end = value.rfind('"');
if (end == value.npos || end == 0)
end = value.length();
value = value.substr(1, end - 1);
}
if (cEscape) {
entry = handleEscapeSeq(entry);
value = handleEscapeSeq(value);
}
set(curSection, entry, value);
}
}
}
void IniFile::load(FILE *file, bool cEscape)
{
std::string data;
for (;;)
{
char buffer[4096];
int n = fread(buffer, 1, sizeof(buffer), file);
if (n <= 0)
break;
data += std::string(buffer, buffer + n);
}
load(data, cEscape);
}
void IniFile::save(std::string& data) const
{
std::ostringstream ss;
for (const auto& [sectionName, section] : sections)
{
bool sectionSaved = sectionName.empty();
for (const auto& [name, entry] : section.entries)
{
if (entry.transient && entry.value.empty())
// Don't save transient-only values
continue;
if (!sectionSaved) {
ss << '[' << sectionName << ']' << std::endl;
sectionSaved = true;
}
ss << name << " = " << entry.value << std::endl;
}
ss << std::endl;
}
data = ss.str();
}
void IniFile::save(FILE *file) const
{
std::string data;
save(data);
fwrite(data.c_str(), 1, data.length(), file);
}
bool IniFile::hasSection(const std::string& section) const {
return sections.count(section) != 0;
}
bool IniFile::hasEntry(const std::string& section, const std::string& name) const {
return getEntry(section, name) != nullptr;
}
bool IniFile::isTransient(const std::string& section, const std::string& name) const
{
const Entry *entry = getEntry(section, name);
if (entry == nullptr)
return false;
else
return entry->transient;
}
void IniFile::deleteSection(const std::string& section) {
sections.erase(section);
}
void IniFile::deleteEntry(const std::string& sectionName, const std::string& entry)
{
auto it = sections.find(sectionName);
if (it == sections.end())
return;
Section& section = it->second;
section.entries.erase(entry);
}
std::vector<std::string> IniFile::getEntryNames(const std::string& sectionName) const
{
std::vector<std::string> v;
auto it = sections.find(sectionName);
if (it != sections.end())
{
const Section& section = it->second;
for (const auto& [name, entry] : section.entries)
v.push_back(name);
}
return v;
}
} // namespace config
+95 -52
View File
@@ -1,62 +1,105 @@
#pragma once
/*
Copyright 2025 flyinghead
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdio>
#include <map>
#include <string>
#include <cstdint>
#include <vector>
#include <locale>
#include <sstream>
namespace emucfg {
namespace config {
struct ConfigEntry {
std::string value;
const std::string& get_string() const;
int get_int();
bool get_bool();
int64_t get_int64();
float get_float();
class IniFile
{
public:
void load(FILE *file, bool cEscape = false);
void load(const std::string& data, bool cEscape = false);
void save(FILE *file) const;
void save(std::string& data) const;
bool hasSection(const std::string& section) const;
bool hasEntry(const std::string& section, const std::string& name) const;
bool isTransient(const std::string& section, const std::string& name) const;
void deleteSection(const std::string& section);
void deleteEntry(const std::string& section, const std::string& name);
template<typename T>
void set(const std::string& section, const std::string& entry, T value, bool transient = false) {
setRaw(section, entry, std::to_string(value), transient);
}
const std::string& getString(const std::string& section, const std::string& entry, const std::string& defaultValue = {}) const;
const std::string& get(const std::string& section, const std::string& entry, const std::string& defaultValue = {}) const {
return getString(section, entry, defaultValue);
}
bool getBool(const std::string& section, const std::string& entry, bool defaultValue = false) const;
int getInt(const std::string& section, const std::string& entry, int defaultValue = 0) const;
int64_t getInt64(const std::string& section, const std::string& entry, int64_t defaultValue = 0ll) const;
float getFloat(const std::string& section, const std::string& entry, float defaultValue = 0.f) const;
std::vector<std::string> getEntryNames(const std::string& section) const;
private:
struct Entry
{
std::string name;
std::string value;
std::string transientValue;
bool transient = false;
};
struct Section
{
std::string name;
std::map<std::string, Entry> entries;
};
const Entry *getEntry(const std::string& section, const std::string& entry) const;
const std::string *getRaw(const std::string& section, const std::string& name) const;
void setRaw(const std::string& section, const std::string& name, const std::string& value, bool transient);
std::map<std::string, Section> sections;
};
struct ConfigSection {
std::map<std::string, ConfigEntry> entries;
bool has_entry(const std::string& name);
void set(const std::string& name, const std::string& value);
void delete_entry(const std::string& name);
ConfigEntry* get_entry(const std::string& name);
};
struct ConfigFile {
private:
std::map<std::string, ConfigSection> sections;
std::map<std::string, ConfigSection> virtual_sections;
ConfigSection* add_section(const std::string& name, bool is_virtual);
ConfigSection* get_section(const std::string& name, bool is_virtual);
ConfigEntry* get_entry(const std::string& section_name, const std::string& entry_name);
public:
bool has_section(const std::string& name);
bool has_entry(const std::string& section_name, const std::string& entry_name);
bool is_virtual(const std::string& section_name, const std::string& entry_name);
void parse(FILE* file);
void save(FILE* file);
/* getting values */
std::string get(const std::string& section_name, const std::string& entry_name, const std::string& default_value = "");
int get_int(const std::string& section_name, const std::string& entry_name, int default_value = 0);
int64_t get_int64(const std::string& section_name, const std::string& entry_name, int64_t default_value = 0);
bool get_bool(const std::string& section_name, const std::string& entry_name, bool default_value = false);
float get_float(const std::string& section_name, const std::string& entry_name, float default_value = 0.f);
/* setting values */
void set(const std::string& section_name, const std::string& entry_name, const std::string& value, bool is_virtual = false);
void set_int(const std::string& section_name, const std::string& entry_name, int value, bool is_virtual = false);
void set_int64(const std::string& section_name, const std::string& entry_name, int64_t value, bool is_virtual = false);
void set_bool(const std::string& section_name, const std::string& entry_name, bool value, bool is_virtual = false);
void set_float(const std::string& section_name, const std::string& entry_name, float value, bool is_virtual = false);
void delete_section(const std::string& section_name);
void delete_entry(const std::string& section_name, const std::string& entry_name);
};
} // namespace emucfg
template<>
inline void IniFile::set(const std::string& section, const std::string& entry, const std::string& value, bool transient) {
setRaw(section, entry, value, transient);
}
template<>
inline void IniFile::set(const std::string& section, const std::string& entry, std::string value, bool transient) {
setRaw(section, entry, value, transient);
}
template<>
inline void IniFile::set(const std::string& section, const std::string& entry, const char *value, bool transient) {
setRaw(section, entry, value, transient);
}
template<>
inline void IniFile::set(const std::string& section, const std::string& entry, bool value, bool transient) {
setRaw(section, entry, value ? "yes" : "no", transient);
}
template<>
inline void IniFile::set(const std::string& section, const std::string& entry, float value, bool transient)
{
std::ostringstream ss;
ss.imbue(std::locale::classic());
ss.precision(7);
ss << value;
setRaw(section, entry, ss.str(), transient);
}
} // namespace config
+14 -8
View File
@@ -214,18 +214,24 @@ std::array<std::array<Option<MapleDeviceType>, 2>, 4> MapleExpansionDevices {{
Option<MapleDeviceType>("device4.2", MDT_None, "input")}},
}};
std::array<Option<bool>, 4> UseNetworkExpansionDevices{
Option<bool>("device1.UseNetworkExpansionDevices", false, "input"),
Option<bool>("device2.UseNetworkExpansionDevices", false, "input"),
Option<bool>("device3.UseNetworkExpansionDevices", false, "input"),
Option<bool>("device4.UseNetworkExpansionDevices", false, "input"),
};
std::array<std::array<Option<int>, 2>, 4> NetworkExpansionDevices{{
{{Option<int>("device1.1.net", 0, "input"),
Option<int>("device1.2.net", 0, "input")}},
Option<bool> PerGameVmu("PerGameVmu", false, "config");
{{Option<int>("device2.1.net", 0, "input"),
Option<int>("device2.2.net", 0, "input")}},
{{Option<int>("device3.1.net", 0, "input"),
Option<int>("device3.2.net", 0, "input")}},
{{Option<int>("device4.1.net", 0, "input"),
Option<int>("device4.2.net", 0, "input")}},
}};
Option<bool> PerGameVmu("PerGameVmu", true, "config");
#ifdef _WIN32
Option<bool, false> UseRawInput("RawInput", false, "input");
#endif
Option<bool> UsePhysicalVmuMemory("UsePhysicalVmuMemory", true);
Option<bool> UsePhysicalVmuMemory("UsePhysicalVmuMemory", false);
#ifdef USE_LUA
Option<std::string, false> LuaFileName("LuaFileName", "flycast.lua");
+19 -19
View File
@@ -56,7 +56,7 @@ public:
{
if (gameId.empty())
return;
if (!cfgHasSection(gameId))
if (!hasSection(gameId))
return;
perGameConfig = true;
}
@@ -65,10 +65,10 @@ public:
}
void save() {
cfgSetAutoSave(false);
setAutoSave(false);
for (const auto& o : options)
o->save();
cfgSetAutoSave(true);
setAutoSave(true);
}
void setGameId(const std::string& gameId) {
@@ -82,7 +82,7 @@ public:
this->perGameConfig = perGameConfig;
if (!perGameConfig) {
if (!gameId.empty())
cfgDeleteSection(gameId);
deleteSection(gameId);
reset();
}
}
@@ -122,7 +122,7 @@ public:
else
{
set(doLoad(section, name));
if (cfgIsVirtual(section, name))
if (isTransient(section, name))
override(value);
}
}
@@ -141,7 +141,7 @@ public:
if (value == doLoad(section, name))
{
// delete existing per-game option if any
cfgDeleteEntry(settings.gameId, section + "." + name);
deleteEntry(settings.gameId, section + "." + name);
return;
}
}
@@ -173,14 +173,14 @@ protected:
std::enable_if_t<std::is_same_v<U, bool>, T>
doLoad(const std::string& section, const std::string& name) const
{
return cfgLoadBool(section, name, value);
return loadBool(section, name, value);
}
template <typename U = T>
std::enable_if_t<std::is_same<U, int64_t>::value, T>
doLoad(const std::string& section, const std::string& name) const
{
return cfgLoadInt64(section, name, value);
return loadInt64(section, name, value);
}
template <typename U = T>
@@ -188,28 +188,28 @@ protected:
&& !std::is_same_v<U, bool> && !std::is_same_v<U, int64_t>, T>
doLoad(const std::string& section, const std::string& name) const
{
return (T)cfgLoadInt(section, name, (int)value);
return (T)loadInt(section, name, (int)value);
}
template <typename U = T>
std::enable_if_t<std::is_same_v<U, std::string>, T>
doLoad(const std::string& section, const std::string& name) const
{
return cfgLoadStr(section, name, value);
return loadStr(section, name, value);
}
template <typename U = T>
std::enable_if_t<std::is_same_v<float, U>, T>
doLoad(const std::string& section, const std::string& name) const
{
return cfgLoadFloat(section, name, value);
return loadFloat(section, name, value);
}
template <typename U = T>
std::enable_if_t<std::is_same_v<std::vector<std::string>, U>, T>
doLoad(const std::string& section, const std::string& name) const
{
std::string paths = cfgLoadStr(section, name, "");
std::string paths = loadStr(section, name);
if (paths.empty())
return value;
std::string::size_type start = 0;
@@ -268,14 +268,14 @@ protected:
std::enable_if_t<std::is_same_v<U, bool>>
doSave(const std::string& section, const std::string& name) const
{
cfgSaveBool(section, name, value);
saveBool(section, name, value);
}
template <typename U = T>
std::enable_if_t<std::is_same<U, int64_t>::value>
doSave(const std::string& section, const std::string& name) const
{
cfgSaveInt64(section, name, value);
saveInt64(section, name, value);
}
template <typename U = T>
@@ -283,21 +283,21 @@ protected:
&& !std::is_same_v<U, bool> && !std::is_same_v<U, int64_t>>
doSave(const std::string& section, const std::string& name) const
{
cfgSaveInt(section, name, (int)value);
saveInt(section, name, (int)value);
}
template <typename U = T>
std::enable_if_t<std::is_same_v<U, std::string>>
doSave(const std::string& section, const std::string& name) const
{
cfgSaveStr(section, name, value);
saveStr(section, name, value);
}
template <typename U = T>
std::enable_if_t<std::is_same_v<float, U>>
doSave(const std::string& section, const std::string& name) const
{
cfgSaveFloat(section, name, value);
saveFloat(section, name, value);
}
template <typename U = T>
@@ -332,7 +332,7 @@ protected:
else
s += v;
}
cfgSaveStr(section, name, s);
saveStr(section, name, s);
}
std::string section;
@@ -551,7 +551,7 @@ extern Option<int> VirtualGamepadVibration;
extern Option<int> VirtualGamepadTransparency;
extern std::array<Option<MapleDeviceType>, 4> MapleMainDevices;
extern std::array<std::array<Option<MapleDeviceType>, 2>, 4> MapleExpansionDevices;
extern std::array<Option<bool>, 4> UseNetworkExpansionDevices;
extern std::array<std::array<Option<int>, 2>, 4> NetworkExpansionDevices;
extern Option<bool> PerGameVmu;
#ifdef _WIN32
extern Option<bool, false> UseRawInput;
+52 -85
View File
@@ -28,6 +28,8 @@
#include "cfg/option.h"
#include "emulator.h"
#include "oslib/storage.h"
#include "oslib/i18n.h"
using namespace i18n;
const WidescreenCheat CheatManager::widescreen_cheats[] =
{
@@ -395,18 +397,18 @@ void CheatManager::loadCheatFile(const std::string& filename)
WARN_LOG(COMMON, "Cannot open cheat file '%s'", filename.c_str());
return;
}
emucfg::ConfigFile cfg;
cfg.parse(cheatfile);
config::IniFile cfg;
cfg.load(cheatfile);
fclose(cheatfile);
int count = cfg.get_int("", "cheats", 0);
int count = cfg.getInt("", "cheats", 0);
cheats.clear();
for (int i = 0; count == 0 || i < count; i++)
{
std::string prefix = "cheat" + std::to_string(i) + "_";
Cheat cheat{};
cheat.description = cfg.get("", prefix + "desc", "Cheat " + std::to_string(i + 1));
cheat.address = cfg.get_int("", prefix + "address", -1);
cheat.description = cfg.getString("", prefix + "desc", "Cheat " + std::to_string(i + 1));
cheat.address = cfg.getInt("", prefix + "address", -1);
if (count == 0 && cheat.address == (u32)-1)
break;
if (cheat.address >= RAM_SIZE)
@@ -414,27 +416,27 @@ void CheatManager::loadCheatFile(const std::string& filename)
WARN_LOG(COMMON, "Invalid address %x", cheat.address);
continue;
}
cheat.type = (Cheat::Type)cfg.get_int("", prefix + "cheat_type", (int)Cheat::Type::disabled);
cheat.size = 1 << cfg.get_int("", prefix + "memory_search_size", 0);
cheat.value = cfg.get_int("", prefix + "value", cheat.value);
cheat.repeatCount = cfg.get_int("", prefix + "repeat_count", cheat.repeatCount);
cheat.repeatValueIncrement = cfg.get_int("", prefix + "repeat_add_to_value", cheat.repeatValueIncrement);
cheat.repeatAddressIncrement = cfg.get_int("", prefix + "repeat_add_to_address", cheat.repeatAddressIncrement);
cheat.enabled = cfg.get_bool("", prefix + "enable", false);
cheat.destAddress = cfg.get_int("", prefix + "dest_address", 0);
cheat.type = (Cheat::Type)cfg.getInt("", prefix + "cheat_type", (int)Cheat::Type::disabled);
cheat.size = 1 << cfg.getInt("", prefix + "memory_search_size", 0);
cheat.value = cfg.getInt("", prefix + "value", cheat.value);
cheat.repeatCount = cfg.getInt("", prefix + "repeat_count", cheat.repeatCount);
cheat.repeatValueIncrement = cfg.getInt("", prefix + "repeat_add_to_value", cheat.repeatValueIncrement);
cheat.repeatAddressIncrement = cfg.getInt("", prefix + "repeat_add_to_address", cheat.repeatAddressIncrement);
cheat.enabled = cfg.getBool("", prefix + "enable", false);
cheat.destAddress = cfg.getInt("", prefix + "dest_address", 0);
if (cheat.destAddress >= RAM_SIZE)
{
WARN_LOG(COMMON, "Invalid address %x", cheat.destAddress);
continue;
}
cheat.valueMask = cfg.get_int("", prefix + "address_bit_position", 0);
cheat.valueMask = cfg.getInt("", prefix + "address_bit_position", 0);
if (cheat.type != Cheat::Type::disabled)
cheats.push_back(cheat);
}
setActive(!cheats.empty());
INFO_LOG(COMMON, "%d cheats loaded", (int)cheats.size());
if (!cheats.empty())
cfgSaveStr("cheats", gameId, filename);
config::saveStr("cheats", gameId, filename);
#endif
}
@@ -450,7 +452,7 @@ void CheatManager::reset(const std::string& gameId)
#ifndef LIBRETRO
if (!settings.raHardcoreMode)
{
std::string cheatFile = cfgLoadStr("cheats", gameId, "");
std::string cheatFile = config::loadStr("cheats", gameId);
if (!cheatFile.empty())
loadCheatFile(cheatFile);
else
@@ -470,7 +472,7 @@ void CheatManager::reset(const std::string& gameId)
if (hostfs::storage().exists(candidate))
{
loadCheatFile(candidate);
cfgSaveStr("cheats", gameId, candidate);
config::saveStr("cheats", gameId, candidate);
goto found_cheats;
}
}
@@ -568,45 +570,10 @@ found_cheats:
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass auth ifeq", true, 32, 0x00800524, 0x2fd62fe6, true);
cheats.emplace_back(Cheat::Type::setValue, "bypass dricas auth", true, 32, 0x00800524, 0xe000000b, true);
}
else if (gameId == "T40214N") { // The Next Tetris (US)
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass speed ifeq", true, 32, 0x0016d5d4, 0x2f862fe6, true);
cheats.emplace_back(Cheat::Type::setValue, "bypass speed check", true, 32, 0x0016d5d4, 0xe001000b, true);
}
else if (gameId == "MK-51065") { // Bomberman Online
cheats.emplace_back(Cheat::Type::runNextIfEq, "modem automode ifeq", true, 32, 0x00196da8, 0x2c302c30, true); // 0,0,
cheats.emplace_back(Cheat::Type::setValue, "modem automode set", true, 32, 0x00196da8, 0x2c302c31, true); // 1,0,
}
else if (gameId == "MK-51102") { // Outtrigger (US)
cheats.emplace_back(Cheat::Type::runNextIfEq, "modem automode ifeq", true, 32, 0x001bdb48, 0x2c302c30, true); // 0,0,
cheats.emplace_back(Cheat::Type::setValue, "modem automode set", true, 32, 0x001bdb48, 0x2c302c31, true); // 1,0,
}
else if (gameId == "HDR-0118") { // Outtrigger (JP)
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass auth ifeq", true, 32, 0x00139f54, 0x2fd62fe6, true);
cheats.emplace_back(Cheat::Type::setValue, "bypass dricas auth", true, 32, 0x00139f54, 0xe000000b, true);
}
else if (gameId == "T13306M") // Mobile Suit Gundam: Federation vs. Zeon
{
cheats.emplace_back(Cheat::Type::runNextIfEq, "modem rxspeed ifeq", true, 32, 0x0016e710, 0x3434312c, true); // ",144"
cheats.emplace_back(Cheat::Type::setValue, "modem rxspeed set", true, 32, 0x0016e710, 0x2020202c, true); // ", "
cheats.emplace_back(Cheat::Type::runNextIfEq, "modem txspeed ifeq", true, 32, 0x0016e71c, 0x3434312c, true); // ",144"
cheats.emplace_back(Cheat::Type::setValue, "modem txspeed set", true, 32, 0x0016e71c, 0x2020202c, true); // ", "
}
else if (gameId == "MK-51162") // Propeller Arena
{
// 0c1f56f0: ,0,28800,28800,28800,28800\\r"\n
// 0c1f5770: ,0,28800,28800,28800,28800\\r"\n
for (u32 addr = 0x001f56f0; addr <= 0x001f5770; addr += 0x80)
{
cheats.emplace_back(Cheat::Type::runNextIfEq, "modem automode1 ifeq", true, 32, addr + 0x00, 0x322c302c, true); // ",0,2"
cheats.emplace_back(Cheat::Type::setValue, "modem automode1 set", true, 32, addr + 0x00, 0x202c312c, true); // ",1, "
cheats.emplace_back(Cheat::Type::runNextIfEq, "modem automode2 ifeq", true, 32, addr + 0x04, 0x30303838, true); // "8800"
cheats.emplace_back(Cheat::Type::setValue, "modem automode2 set", true, 32, addr + 0x04, 0x30202020, true); // " 0"
cheats.emplace_back(Cheat::Type::runNextIfEq, "modem automode3 ifeq", true, 32, addr + 0x0c, 0x322c3030, true); // "00,2"
cheats.emplace_back(Cheat::Type::setValue, "modem automode3 set", true, 32, addr + 0x0c, 0x202c3030, true); // "00, "
cheats.emplace_back(Cheat::Type::runNextIfEq, "modem automode4 ifeq", true, 32, addr + 0x10, 0x30303838, true); // "8800"
cheats.emplace_back(Cheat::Type::setValue, "modem automode4 set", true, 32, addr + 0x10, 0x30202020, true); // " 0"
}
}
else if (gameId == "HDR-0113") // Power Smash
{
cheats.emplace_back(Cheat::Type::runNextIfEq, "no dupe SYN ifeq", true, 16, 0x14d258, 0xbbce, true); // bsr SendNormalSYN
@@ -801,12 +768,12 @@ static std::vector<u32> parseCodes(const std::string& s)
}
}
else if (!curCode.empty())
throw FlycastException("Invalid cheat code");
throw FlycastException(Ts("Invalid cheat code"));
}
if (!curCode.empty())
{
if (curCode.length() != 8)
throw FlycastException("Invalid cheat code");
throw FlycastException(Ts("Invalid cheat code"));
codes.push_back(strtoul(curCode.c_str(), nullptr, 16));
}
@@ -888,7 +855,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
{
// 8/16/32-bit write
if (i + 1 >= codes.size())
throw FlycastException("Missing value");
throw FlycastException(Ts("Missing value"));
cheat.type = Cheat::Type::setValue;
cheat.size = code == 0 ? 8 : code == 1 ? 16 : 32;
cheat.address = codes[i] & 0x00ffffff;
@@ -906,7 +873,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
// Group write
int count = codes[i] & 0xffff;
if (i + count + 1 >= codes.size())
throw FlycastException("Missing values");
throw FlycastException(Ts("Missing values"));
cheat.type = Cheat::Type::setValue;
cheat.size = 32;
cheat.address = codes[++i] & 0x00ffffff;
@@ -927,7 +894,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
{
// 8-bit inc/decrement
if (i + 1 >= codes.size())
throw FlycastException("Missing value");
throw FlycastException(Ts("Missing value"));
cheat.type = subcode == 1 ? Cheat::Type::increase : Cheat::Type::decrease;
cheat.size = 8;
cheat.value = codes[i] & 0xff;
@@ -940,7 +907,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
{
// 16-bit inc/decrement
if (i + 1 >= codes.size())
throw FlycastException("Missing value");
throw FlycastException(Ts("Missing value"));
cheat.type = subcode == 3 ? Cheat::Type::increase : Cheat::Type::decrease;
cheat.size = 16;
cheat.value = codes[i] & 0xffff;
@@ -953,7 +920,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
{
// 32-bit inc/decrement
if (i + 2 >= codes.size())
throw FlycastException("Missing address or value");
throw FlycastException(Ts("Missing address or value"));
cheat.type = subcode == 5 ? Cheat::Type::increase : Cheat::Type::decrease;
cheat.size = 32;
cheat.address = codes[++i] & 0x00ffffff;
@@ -962,7 +929,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
}
break;
default:
throw FlycastException("Unsupported cheat type");
throw FlycastException(Ts("Unsupported cheat type"));
}
}
break;
@@ -970,7 +937,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
{
// 32-bit repeat write
if (i + 2 >= codes.size())
throw FlycastException("Missing count or value");
throw FlycastException(Ts("Missing count or value"));
cheat.type = Cheat::Type::setValue;
cheat.size = 32;
cheat.address = codes[i] & 0x00ffffff;
@@ -984,7 +951,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
{
// copy bytes
if (i + 2 >= codes.size())
throw FlycastException("Missing count or destination address");
throw FlycastException(Ts("Missing count or destination address"));
cheat.type = Cheat::Type::copy;
cheat.size = 8;
cheat.address = codes[i] & 0x00ffffff;
@@ -996,7 +963,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
case 7:
// change decryption type: 071000XX (example: 07100005)
throw FlycastException("Master codes aren't supported");
throw FlycastException(Ts("Master codes aren't supported"));
// TODO 0xb delay applying codes: 0b0xxxxx
// Delay putting on codes for xxxxx cycles. Default 1000 (0x3e7)
@@ -1008,7 +975,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
{
// enable next code if eq/neq/lt/gt
if (i + 1 >= codes.size())
throw FlycastException("Missing count or destination address");
throw FlycastException(Ts("Missing count or destination address"));
cheat.size = 16;
cheat.address = codes[i] & 0x00ffffff;
switch (codes[++i] >> 16)
@@ -1026,7 +993,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
cheat.type = Cheat::Type::runNextIfGt;
break;
default:
throw FlycastException("Unsupported conditional code");
throw FlycastException(Ts("Unsupported conditional code"));
}
cheat.value = codes[i] & 0xffff;
cheats.push_back(cheat);
@@ -1036,7 +1003,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
{
// multiline enable codes if eq/neq/lt/gt
if (i + 1 >= codes.size())
throw FlycastException("Missing test address");
throw FlycastException(Ts("Missing test address"));
cheat.size = 16;
cheat.value = codes[i] & 0xffff;
conditionLimit = i + 1 + ((codes[i] >> 16) & 0xff);
@@ -1055,7 +1022,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
cheat.type = Cheat::Type::runNextIfGt;
break;
default:
throw FlycastException("Unsupported conditional code");
throw FlycastException(Ts("Unsupported conditional code"));
}
cheat.address = codes[i] & 0x00ffffff;
conditionCheat = cheat;
@@ -1066,15 +1033,15 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
// 16-Bit Write Once Immediately. (Activator code)
default:
throw FlycastException("Unsupported cheat type");
throw FlycastException(Ts("Unsupported cheat type"));
}
}
#ifndef LIBRETRO
std::string path = cfgLoadStr("cheats", gameId, "");
std::string path = config::loadStr("cheats", gameId);
if (path == "")
{
path = get_game_save_prefix() + ".cht";
cfgSaveStr("cheats", gameId, path);
config::saveStr("cheats", gameId, path);
}
saveCheatFile(path);
#endif
@@ -1088,7 +1055,7 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
void CheatManager::saveCheatFile(const std::string& filename)
{
#ifndef LIBRETRO
emucfg::ConfigFile cfg;
config::IniFile cfg;
int i = 0;
for (const Cheat& cheat : cheats)
@@ -1096,15 +1063,15 @@ void CheatManager::saveCheatFile(const std::string& filename)
if (cheat.builtIn)
continue;
std::string prefix = "cheat" + std::to_string(i) + "_";
cfg.set_int("", prefix + "address", cheat.address);
cfg.set_int("", prefix + "address_bit_position", cheat.valueMask);
cfg.set_bool("", prefix + "big_endian", false);
cfg.set_int("", prefix + "cheat_type", (int)cheat.type);
cfg.set("", prefix + "address", cheat.address);
cfg.set("", prefix + "address_bit_position", cheat.valueMask);
cfg.set("", prefix + "big_endian", false);
cfg.set("", prefix + "cheat_type", (int)cheat.type);
cfg.set("", prefix + "code", "");
cfg.set("", prefix + "desc", cheat.description);
cfg.set_int("", prefix + "dest_address", cheat.destAddress);
cfg.set_bool("", prefix + "enable", false); // force all cheats disabled at start
cfg.set_int("", prefix + "handler", 1);
cfg.set("", prefix + "dest_address", cheat.destAddress);
cfg.set("", prefix + "enable", false); // force all cheats disabled at start
cfg.set("", prefix + "handler", 1);
int memSize;
switch (cheat.size) {
case 1:
@@ -1127,17 +1094,17 @@ void CheatManager::saveCheatFile(const std::string& filename)
memSize = 5;
break;
}
cfg.set_int("", prefix + "memory_search_size", memSize);
cfg.set_int("", prefix + "value", cheat.value);
cfg.set_int("", prefix + "repeat_count", cheat.repeatCount);
cfg.set_int("", prefix + "repeat_add_to_value", cheat.repeatValueIncrement);
cfg.set_int("", prefix + "repeat_add_to_address", cheat.repeatAddressIncrement);
cfg.set("", prefix + "memory_search_size", memSize);
cfg.set("", prefix + "value", cheat.value);
cfg.set("", prefix + "repeat_count", cheat.repeatCount);
cfg.set("", prefix + "repeat_add_to_value", cheat.repeatValueIncrement);
cfg.set("", prefix + "repeat_add_to_address", cheat.repeatAddressIncrement);
i++;
}
cfg.set_int("", "cheats", i);
cfg.set("", "cheats", i);
FILE *fp = hostfs::storage().openFile(filename.c_str(), "w");
if (fp == nullptr)
throw FlycastException("Can't save cheat file");
throw FlycastException(Ts("Can't save cheat file"));
cfg.save(fp);
fclose(fp);
#endif
+1 -1
View File
@@ -56,7 +56,7 @@ public:
*/
#if defined(_WIN32)
# include "platform_windows.h"
#elif defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__)
#elif defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__) || defined(__HAIKU__)
# include "platform_linux.h"
#else
# error Unsupported platform
+1 -1
View File
@@ -4,7 +4,7 @@
* Use of this software is governed by the MIT license that can be found
* in the LICENSE file.
*/
#if defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__)
#if defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__) || defined(__HAIKU__)
#include "platform_linux.h"
#include <time.h>
Regular → Executable
+3 -1
View File
@@ -15,7 +15,8 @@
#pragma once
//---- Define assertion handler. Defaults to calling assert().
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
// - If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
// - Compiling with NDEBUG will usually strip out assert() to nothing, which is NOT recommended because we use asserts to notify of programmer mistakes.
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
@@ -83,6 +84,7 @@
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
// Note that imgui_freetype.cpp may be used _without_ this define, if you manually call ImFontAtlas::SetFontLoader(). The define is simply a convenience.
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
//#define IMGUI_ENABLE_FREETYPE
+804 -502
View File
File diff suppressed because it is too large. Load diff
+181 -124
View File
@@ -1,35 +1,36 @@
// dear imgui, v1.92.1
// dear imgui, v1.92.5
// (headers)
// Help:
// - See links below.
// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
// - Read top of imgui.cpp for more details, links and comments.
// - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including this file (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4.
// - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including imgui.h (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4.
// Resources:
// - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md)
// - Homepage ................... https://github.com/ocornut/imgui
// - Releases & changelog ....... https://github.com/ocornut/imgui/releases
// - Releases & Changelog ....... https://github.com/ocornut/imgui/releases
// - Gallery .................... https://github.com/ocornut/imgui/issues?q=label%3Agallery (please post your screenshots/video there!)
// - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there)
// - Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code)
// - Third-party Extensions https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more)
// - Bindings/Backends https://github.com/ocornut/imgui/wiki/Bindings (language bindings, backends for various tech/engines)
// - Glossary https://github.com/ocornut/imgui/wiki/Glossary
// - Bindings/Backends https://github.com/ocornut/imgui/wiki/Bindings (language bindings + backends for various tech/engines)
// - Debug Tools https://github.com/ocornut/imgui/wiki/Debug-Tools
// - Glossary https://github.com/ocornut/imgui/wiki/Glossary
// - Software using Dear ImGui https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui
// - Issues & support ........... https://github.com/ocornut/imgui/issues
// - Test Engine & Automation ... https://github.com/ocornut/imgui_test_engine (test suite, test engine to automate your apps)
// - Web version of the Demo .... https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html (w/ source code browser)
// For first-time users having issues compiling/linking/running/loading fonts:
// For FIRST-TIME users having issues compiling/linking/running:
// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.
// Everything else should be asked in 'Issues'! We are building a database of cross-linked knowledge there.
// EVERYTHING ELSE should be asked in 'Issues'! We are building a database of cross-linked knowledge there.
// Since 1.92, we encourage font loading questions to also be posted in 'Issues'.
// Library Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
#define IMGUI_VERSION "1.92.1"
#define IMGUI_VERSION_NUM 19210
#define IMGUI_VERSION "1.92.5"
#define IMGUI_VERSION_NUM 19250
#define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000
#define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198
@@ -89,19 +90,24 @@ Index of this file:
#endif
// Helper Macros
// (note: compiling with NDEBUG will usually strip out assert() to nothing, which is NOT recommended because we use asserts to notify of programmer mistakes.)
#ifndef IM_ASSERT
#include <assert.h>
#define IM_ASSERT(_EXPR) assert(_EXPR) // You can override the default assert handler by editing imconfig.h
#endif
#define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR) / sizeof(*(_ARR)))) // Size of a static C-style array. Don't use on pointers!
#define IM_UNUSED(_VAR) ((void)(_VAR)) // Used to silence "unused variable warnings". Often useful as asserts may be stripped out from final builds.
#define IM_STRINGIFY_HELPER(_EXPR) #_EXPR
#define IM_STRINGIFY(_EXPR) IM_STRINGIFY_HELPER(_EXPR) // Preprocessor idiom to stringify e.g. an integer or a macro.
// Check that version and structures layouts are matching between compiled imgui code and caller. Read comments above DebugCheckVersionAndDataLayout() for details.
#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx))
// Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions.
// (MSVC provides an equivalent mechanism via SAL Annotations but it would require the macros in a different
// location. e.g. #include <sal.h> + void myprintf(_Printf_format_string_ const char* format, ...))
// (MSVC provides an equivalent mechanism via SAL Annotations but it requires the macros in a different
// location. e.g. #include <sal.h> + void myprintf(_Printf_format_string_ const char* format, ...),
// and only works when using Code Analysis, rather than just normal compiling).
// (see https://github.com/ocornut/imgui/issues/8871 for a patch to enable this for MSVC's Code Analysis)
#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) && !defined(__clang__)
#define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1)))
#define IM_FMTLIST(FMT) __attribute__((format(gnu_printf, FMT, 0)))
@@ -209,9 +215,9 @@ struct ImGuiViewport; // A Platform Window (always only one in 'ma
// Enumerations
// - We don't use strongly typed enums much because they add constraints (can't extend in private code, can't store typed in bit fields, extra casting on iteration)
// - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists!
// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
// - In Visual Studio: Ctrl+Comma ("Edit.GoToAll") can follow symbols inside comments, whereas Ctrl+F12 ("Edit.GoToImplementation") cannot.
// - In Visual Studio w/ Visual Assist installed: Alt+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
// - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments.
enum ImGuiDir : int; // -> enum ImGuiDir // Enum: A cardinal direction (Left, Right, Up, Down)
enum ImGuiKey : int; // -> enum ImGuiKey // Enum: A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value)
enum ImGuiMouseSource : int; // -> enum ImGuiMouseSource // Enum; A mouse input source identifier (Mouse, TouchScreen, Pen)
@@ -226,11 +232,12 @@ typedef int ImGuiTableBgTarget; // -> enum ImGuiTableBgTarget_ // Enum: A
// Flags (declared as int to allow using as flags without overhead, and to not pollute the top of this file)
// - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists!
// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
// - In Visual Studio: Ctrl+Comma ("Edit.GoToAll") can follow symbols inside comments, whereas Ctrl+F12 ("Edit.GoToImplementation") cannot.
// - In Visual Studio w/ Visual Assist installed: Alt+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
// - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments.
typedef int ImDrawFlags; // -> enum ImDrawFlags_ // Flags: for ImDrawList functions
typedef int ImDrawListFlags; // -> enum ImDrawListFlags_ // Flags: for ImDrawList instance
typedef int ImDrawTextFlags; // -> enum ImDrawTextFlags_ // Internal, do not use!
typedef int ImFontFlags; // -> enum ImFontFlags_ // Flags: for ImFont
typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: for ImFontAtlas
typedef int ImGuiBackendFlags; // -> enum ImGuiBackendFlags_ // Flags: for io.BackendFlags
@@ -246,6 +253,7 @@ typedef int ImGuiInputFlags; // -> enum ImGuiInputFlags_ // Flags: f
typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: for InputText(), InputTextMultiline()
typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag(), shared by all items
typedef int ImGuiKeyChord; // -> ImGuiKey | ImGuiMod_XXX // Flags: for IsKeyChordPressed(), Shortcut() etc. an ImGuiKey optionally OR-ed with one or more ImGuiMod_XXX values.
typedef int ImGuiListClipperFlags; // -> enum ImGuiListClipperFlags_// Flags: for ImGuiListClipper
typedef int ImGuiPopupFlags; // -> enum ImGuiPopupFlags_ // Flags: for OpenPopup*(), BeginPopupContext*(), IsPopupOpen()
typedef int ImGuiMultiSelectFlags; // -> enum ImGuiMultiSelectFlags_// Flags: for BeginMultiSelect()
typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable()
@@ -317,7 +325,7 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE
// - When a Rendered Backend creates a texture, it store its native identifier into a ImTextureID value.
// (e.g. Used by DX11 backend to a `ID3D11ShaderResourceView*`; Used by OpenGL backends to store `GLuint`;
// Used by SDLGPU backend to store a `SDL_GPUTextureSamplerBinding*`, etc.).
// - User may submit their own textures to e.g. ImGui::Image() function by passing the same type.
// - User may submit their own textures to e.g. ImGui::Image() function by passing this value.
// - During the rendering loop, the Renderer Backend retrieve the ImTextureID, which stored inside a
// ImTextureRef, which is stored inside a ImDrawCmd.
// - Compile-time type configuration:
@@ -326,7 +334,7 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE
// - You may decide to store a higher-level structure containing texture, sampler, shader etc. with various
// constructors if you like. You will need to implement ==/!= operators.
// History:
// - In v1.91.4 (2024/10/08): the default type for ImTextureID was changed from 'void*' to 'ImU64'. This allowed backends requirig 64-bit worth of data to build on 32-bit architectures. Use intermediary intptr_t cast and read FAQ if you have casting warnings.
// - In v1.91.4 (2024/10/08): the default type for ImTextureID was changed from 'void*' to 'ImU64'. This allowed backends requiring 64-bit worth of data to build on 32-bit architectures. Use intermediary intptr_t cast and read FAQ if you have casting warnings.
// - In v1.92.0 (2025/06/11): added ImTextureRef which carry either a ImTextureID either a pointer to internal texture atlas. All user facing functions taking ImTextureID changed to ImTextureRef
#ifndef ImTextureID
typedef ImU64 ImTextureID; // Default: store up to 64-bits (any pointer or integer). A majority of backends are ok with that.
@@ -337,15 +345,17 @@ typedef ImU64 ImTextureID; // Default: store up to 64-bits (any pointer or
#define ImTextureID_Invalid ((ImTextureID)0)
#endif
// ImTextureRef = higher-level identifier for a texture.
// ImTextureRef = higher-level identifier for a texture. Store a ImTextureID _or_ a ImTextureData*.
// The identifier is valid even before the texture has been uploaded to the GPU/graphics system.
// This is what gets passed to functions such as `ImGui::Image()`, `ImDrawList::AddImage()`.
// This is what gets stored in draw commands (`ImDrawCmd`) to identify a texture during rendering.
// - When a texture is created by user code (e.g. custom images), we directly stores the low-level ImTextureID.
// Because of this, when displaying your own texture you are likely to ever only manage ImTextureID values on your side.
// - When a texture is created by the backend, we stores a ImTextureData* which becomes an indirection
// to extract the ImTextureID value during rendering, after texture upload has happened.
// - There is no constructor to create a ImTextureID from a ImTextureData* as we don't expect this
// to be useful to the end-user, and it would be erroneously called by many legacy code.
// - To create a ImTextureRef from a ImTextureData you can use ImTextureData::GetTexRef().
// We intentionally do not provide an ImTextureRef constructor for this: we don't expect this
// to be frequently useful to the end-user, and it would be erroneously called by many legacy code.
// - If you want to bind the current atlas when using custom rectangle, you can use io.Fonts->TexRef.
// - Binding generators for languages such as C (which don't have constructors), should provide a helper, e.g.
// inline ImTextureRef ImTextureRefFromID(ImTextureID tex_id) { ImTextureRef tex_ref = { ._TexData = NULL, .TexID = tex_id }; return tex_ref; }
@@ -653,13 +663,13 @@ namespace ImGui
IMGUI_API bool Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items = -1);
// Widgets: Drag Sliders
// - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp.
// - Ctrl+Click on any drag box to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp.
// - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every function, note that a 'float v[X]' function argument is the same as 'float* v',
// the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x
// - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc.
// - Format string may also be set to NULL or use the default format ("%f" or "%d").
// - Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For keyboard/gamepad navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision).
// - Use v_min < v_max to clamp edits to given limits. Note that CTRL+Click manual input can override those limits if ImGuiSliderFlags_AlwaysClamp is not used.
// - Use v_min < v_max to clamp edits to given limits. Note that Ctrl+Click manual input can override those limits if ImGuiSliderFlags_AlwaysClamp is not used.
// - Use v_max = FLT_MAX / INT_MAX etc to avoid clamping to a maximum, same with v_min = -FLT_MAX / INT_MIN to avoid clamping to a minimum.
// - We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them.
// - Legacy: Pre-1.78 there are DragXXX() function signatures that take a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument.
@@ -678,7 +688,7 @@ namespace ImGui
IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0);
// Widgets: Regular Sliders
// - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp.
// - Ctrl+Click on any slider to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp.
// - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc.
// - Format string may also be set to NULL or use the default format ("%f" or "%d").
// - Legacy: Pre-1.78 there are SliderXXX() function signatures that take a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument.
@@ -699,7 +709,7 @@ namespace ImGui
IMGUI_API bool VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0);
// Widgets: Input with Keyboard
// - If you want to use InputText() with std::string or any custom dynamic string type, see misc/cpp/imgui_stdlib.h and comments in imgui_demo.cpp.
// - If you want to use InputText() with std::string or any custom dynamic string type, use the wrapper in misc/cpp/imgui_stdlib.h/.cpp!
// - Most of the ImGuiInputTextFlags flags are only useful for InputText() and not for InputFloatX, InputIntX, InputDouble etc.
IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
IMGUI_API bool InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
@@ -729,7 +739,7 @@ namespace ImGui
// Widgets: Trees
// - TreeNode functions return true when the node is open, in which case you need to also call TreePop() when you are finished displaying the tree node contents.
IMGUI_API bool TreeNode(const char* label);
IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...) IM_FMTARGS(2); // helper variation to easily decorelate the id from the displayed string. Read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet().
IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...) IM_FMTARGS(2); // helper variation to easily decorrelate the id from the displayed string. Read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet().
IMGUI_API bool TreeNode(const void* ptr_id, const char* fmt, ...) IM_FMTARGS(2); // "
IMGUI_API bool TreeNodeV(const char* str_id, const char* fmt, va_list args) IM_FMTLIST(2);
IMGUI_API bool TreeNodeV(const void* ptr_id, const char* fmt, va_list args) IM_FMTLIST(2);
@@ -754,7 +764,7 @@ namespace ImGui
IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool* p_selected" point to the selection state (read-write), as a convenient helper.
// Multi-selection system for Selectable(), Checkbox(), TreeNode() functions [BETA]
// - This enables standard multi-selection/range-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc.) in a way that also allow a clipper to be used.
// - This enables standard multi-selection/range-selection idioms (Ctrl+Mouse/Keyboard, Shift+Mouse/Keyboard, etc.) in a way that also allow a clipper to be used.
// - ImGuiSelectionUserData is often used to store your item index within the current view (but may store something else).
// - Read comments near ImGuiMultiSelectIO for instructions/details and see 'Demo->Widgets->Selection State & Multi-Select' for demo.
// - TreeNode() is technically supported but... using this correctly is more complicated. You need some sort of linear/random access to your tree,
@@ -914,7 +924,7 @@ namespace ImGui
IMGUI_API ImGuiTableSortSpecs* TableGetSortSpecs(); // get latest sort specs for the table (NULL if not sorting). Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable().
IMGUI_API int TableGetColumnCount(); // return number of columns (value passed to BeginTable)
IMGUI_API int TableGetColumnIndex(); // return current column index.
IMGUI_API int TableGetRowIndex(); // return current row index.
IMGUI_API int TableGetRowIndex(); // return current row index (header rows are accounted for)
IMGUI_API const char* TableGetColumnName(int column_n = -1); // return "" if column didn't have a name declared by TableSetupColumn(). Pass -1 to use current column.
IMGUI_API ImGuiTableColumnFlags TableGetColumnFlags(int column_n = -1); // return column flags so you can query their Enabled/Visible/Sorted/Hovered status flags. Pass -1 to use current column.
IMGUI_API void TableSetColumnEnabled(int column_n, bool v);// change user accessible enabled/disabled state of a column. Set to false to hide the column. User can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody)
@@ -967,7 +977,7 @@ namespace ImGui
// Disabling [BETA API]
// - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors)
// - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)
// - Tooltips windows by exception are opted out of disabling.
// - Tooltips windows are automatically opted out of disabling. Note that IsItemHovered() by default returns false on disabled items, unless using ImGuiHoveredFlags_AllowWhenDisabled.
// - BeginDisabled(false)/EndDisabled() essentially does nothing but is provided to facilitate use of boolean expressions (as a micro-optimization: if you have tens of thousands of BeginDisabled(false)/EndDisabled() pairs, you might want to reformulate your code to avoid making those calls)
IMGUI_API void BeginDisabled(bool disabled = true);
IMGUI_API void EndDisabled();
@@ -1051,8 +1061,8 @@ namespace ImGui
// Inputs Utilities: Shortcut Testing & Routing [BETA]
// - ImGuiKeyChord = a ImGuiKey + optional ImGuiMod_Alt/ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Super.
// ImGuiKey_C // Accepted by functions taking ImGuiKey or ImGuiKeyChord arguments)
// ImGuiMod_Ctrl | ImGuiKey_C // Accepted by functions taking ImGuiKeyChord arguments)
// ImGuiKey_C // Accepted by functions taking ImGuiKey or ImGuiKeyChord arguments
// ImGuiMod_Ctrl | ImGuiKey_C // Accepted by functions taking ImGuiKeyChord arguments
// only ImGuiMod_XXX values are legal to combine with an ImGuiKey. You CANNOT combine two ImGuiKey values.
// - The general idea is that several callers may register interest in a shortcut, and only one owner gets it.
// Parent -> call Shortcut(Ctrl+S) // When Parent is focused, Parent gets the shortcut.
@@ -1159,7 +1169,7 @@ enum ImGuiWindowFlags_
ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14, // Always show vertical scrollbar (even if ContentSize.y < Size.y)
ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15, // Always show horizontal scrollbar (even if ContentSize.x < Size.x)
ImGuiWindowFlags_NoNavInputs = 1 << 16, // No keyboard/gamepad navigation within the window
ImGuiWindowFlags_NoNavFocus = 1 << 17, // No focusing toward this window with keyboard/gamepad navigation (e.g. skipped by CTRL+TAB)
ImGuiWindowFlags_NoNavFocus = 1 << 17, // No focusing toward this window with keyboard/gamepad navigation (e.g. skipped by Ctrl+Tab)
ImGuiWindowFlags_UnsavedDocument = 1 << 18, // Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.
ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus,
ImGuiWindowFlags_NoDecoration = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse,
@@ -1175,13 +1185,13 @@ enum ImGuiWindowFlags_
// Obsolete names
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
ImGuiWindowFlags_NavFlattened = 1 << 29, // Obsoleted in 1.90.9: Use ImGuiChildFlags_NavFlattened in BeginChild() call.
ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 30, // Obsoleted in 1.90.0: Use ImGuiChildFlags_AlwaysUseWindowPadding in BeginChild() call.
//ImGuiWindowFlags_NavFlattened = 1 << 29, // Obsoleted in 1.90.9: moved to ImGuiChildFlags. BeginChild(name, size, 0, ImGuiWindowFlags_NavFlattened) --> BeginChild(name, size, ImGuiChildFlags_NavFlattened, 0)
//ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 30, // Obsoleted in 1.90.0: moved to ImGuiChildFlags. BeginChild(name, size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding) --> BeginChild(name, size, ImGuiChildFlags_AlwaysUseWindowPadding, 0)
#endif
};
// Flags for ImGui::BeginChild()
// (Legacy: bit 0 must always correspond to ImGuiChildFlags_Borders to be backward compatible with old API using 'bool border = false'.
// (Legacy: bit 0 must always correspond to ImGuiChildFlags_Borders to be backward compatible with old API using 'bool border = false'.)
// About using AutoResizeX/AutoResizeY flags:
// - May be combined with SetNextWindowSizeConstraints() to set a min/max size for each axis (see "Demo->Child->Auto-resize with Constraints").
// - Size measurement for a given axis is only performed when the child window is within visible boundaries, or is just appearing.
@@ -1204,7 +1214,7 @@ enum ImGuiChildFlags_
// Obsolete names
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
ImGuiChildFlags_Border = ImGuiChildFlags_Borders, // Renamed in 1.91.1 (August 2024) for consistency.
//ImGuiChildFlags_Border = ImGuiChildFlags_Borders, // Renamed in 1.91.1 (August 2024) for consistency.
#endif
};
@@ -1260,6 +1270,15 @@ enum ImGuiInputTextFlags_
ImGuiInputTextFlags_CallbackResize = 1 << 22, // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this)
ImGuiInputTextFlags_CallbackEdit = 1 << 23, // Callback on any edit. Note that InputText() already returns true on edit + you can always use IsItemEdited(). The callback is useful to manipulate the underlying buffer while focus is active.
// Multi-line Word-Wrapping [BETA]
// - Not well tested yet. Please report any incorrect cursor movement, selection behavior etc. bug to https://github.com/ocornut/imgui/issues/3237.
// - Wrapping style is not ideal. Wrapping of long words/sections (e.g. words larger than total available width) may be particularly unpleasing.
// - Wrapping width needs to always account for the possibility of a vertical scrollbar.
// - It is much slower than regular text fields.
// Ballpark estimate of cost on my 2019 desktop PC: for a 100 KB text buffer: +~0.3 ms (Optimized) / +~1.0 ms (Debug build).
// The CPU cost is very roughly proportional to text length, so a 10 KB buffer should cost about ten times less.
ImGuiInputTextFlags_WordWrap = 1 << 24, // InputTextMultiline(): word-wrap lines that are too long.
// Obsolete names
//ImGuiInputTextFlags_AlwaysInsertMode = ImGuiInputTextFlags_AlwaysOverwrite // [renamed in 1.82] name was not matching behavior
};
@@ -1285,7 +1304,7 @@ enum ImGuiTreeNodeFlags_
ImGuiTreeNodeFlags_SpanAllColumns = 1 << 14, // Frame will span all columns of its container table (label will still fit in current column)
ImGuiTreeNodeFlags_LabelSpanAllColumns = 1 << 15, // Label will span all columns of its container table
//ImGuiTreeNodeFlags_NoScrollOnOpen = 1 << 16, // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible
ImGuiTreeNodeFlags_NavLeftJumpsToParent = 1 << 17, // Nav: left arrow moves back to parent. This is processed in TreePop() when there's an unfullfilled Left nav request remaining.
ImGuiTreeNodeFlags_NavLeftJumpsToParent = 1 << 17, // Nav: left arrow moves back to parent. This is processed in TreePop() when there's an unfulfilled Left nav request remaining.
ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog,
// [EXPERIMENTAL] Draw lines connecting TreeNode hierarchy. Discuss in GitHub issue #2920.
@@ -1297,7 +1316,7 @@ enum ImGuiTreeNodeFlags_
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
ImGuiTreeNodeFlags_NavLeftJumpsBackHere = ImGuiTreeNodeFlags_NavLeftJumpsToParent, // Renamed in 1.92.0
ImGuiTreeNodeFlags_SpanTextWidth = ImGuiTreeNodeFlags_SpanLabelWidth, // Renamed in 1.90.7
ImGuiTreeNodeFlags_AllowItemOverlap = ImGuiTreeNodeFlags_AllowOverlap, // Renamed in 1.89.7
//ImGuiTreeNodeFlags_AllowItemOverlap = ImGuiTreeNodeFlags_AllowOverlap, // Renamed in 1.89.7
#endif
};
@@ -1336,10 +1355,11 @@ enum ImGuiSelectableFlags_
ImGuiSelectableFlags_Disabled = 1 << 3, // Cannot be selected, display grayed out text
ImGuiSelectableFlags_AllowOverlap = 1 << 4, // (WIP) Hit testing to allow subsequent widgets to overlap this one
ImGuiSelectableFlags_Highlight = 1 << 5, // Make the item be displayed as if it is hovered
ImGuiSelectableFlags_SelectOnNav = 1 << 6, // Auto-select when moved into, unless Ctrl is held. Automatic when in a BeginMultiSelect() block.
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
ImGuiSelectableFlags_DontClosePopups = ImGuiSelectableFlags_NoAutoClosePopups, // Renamed in 1.91.0
ImGuiSelectableFlags_AllowItemOverlap = ImGuiSelectableFlags_AllowOverlap, // Renamed in 1.89.7
//ImGuiSelectableFlags_AllowItemOverlap = ImGuiSelectableFlags_AllowOverlap, // Renamed in 1.89.7
#endif
};
@@ -1369,10 +1389,17 @@ enum ImGuiTabBarFlags_
ImGuiTabBarFlags_NoTabListScrollingButtons = 1 << 4, // Disable scrolling buttons (apply when fitting policy is ImGuiTabBarFlags_FittingPolicyScroll)
ImGuiTabBarFlags_NoTooltip = 1 << 5, // Disable tooltips when hovering a tab
ImGuiTabBarFlags_DrawSelectedOverline = 1 << 6, // Draw selected overline markers over selected tab
ImGuiTabBarFlags_FittingPolicyResizeDown = 1 << 7, // Resize tabs when they don't fit
ImGuiTabBarFlags_FittingPolicyScroll = 1 << 8, // Add scroll buttons when tabs don't fit
ImGuiTabBarFlags_FittingPolicyMask_ = ImGuiTabBarFlags_FittingPolicyResizeDown | ImGuiTabBarFlags_FittingPolicyScroll,
ImGuiTabBarFlags_FittingPolicyDefault_ = ImGuiTabBarFlags_FittingPolicyResizeDown,
// Fitting/Resize policy
ImGuiTabBarFlags_FittingPolicyMixed = 1 << 7, // Shrink down tabs when they don't fit, until width is style.TabMinWidthShrink, then enable scrolling buttons.
ImGuiTabBarFlags_FittingPolicyShrink = 1 << 8, // Shrink down tabs when they don't fit
ImGuiTabBarFlags_FittingPolicyScroll = 1 << 9, // Enable scrolling buttons when tabs don't fit
ImGuiTabBarFlags_FittingPolicyMask_ = ImGuiTabBarFlags_FittingPolicyMixed | ImGuiTabBarFlags_FittingPolicyShrink | ImGuiTabBarFlags_FittingPolicyScroll,
ImGuiTabBarFlags_FittingPolicyDefault_ = ImGuiTabBarFlags_FittingPolicyMixed,
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
ImGuiTabBarFlags_FittingPolicyResizeDown = ImGuiTabBarFlags_FittingPolicyShrink, // Renamed in 1.92.2
#endif
};
// Flags for ImGui::BeginTabItem()
@@ -1427,7 +1454,7 @@ enum ImGuiHoveredFlags_
// Tooltips mode
// - typically used in IsItemHovered() + SetTooltip() sequence.
// - this is a shortcut to pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' where you can reconfigure desired behavior.
// e.g. 'TooltipHoveredFlagsForMouse' defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort'.
// e.g. 'HoverFlagsForTooltipMouse' defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled'.
// - for frequently actioned or hovered items providing a tooltip, you want may to use ImGuiHoveredFlags_ForTooltip (stationary + delay) so the tooltip doesn't show too often.
// - for items which main purpose is to be hovered, or items with low affordance, or in less consistent apps, prefer no delay or shorter delay.
ImGuiHoveredFlags_ForTooltip = 1 << 12, // Shortcut for standard flags when using IsItemHovered() + SetTooltip() sequence.
@@ -1459,6 +1486,7 @@ enum ImGuiDragDropFlags_
ImGuiDragDropFlags_AcceptBeforeDelivery = 1 << 10, // AcceptDragDropPayload() will returns true even before the mouse button is released. You can then call IsDelivery() to test if the payload needs to be delivered.
ImGuiDragDropFlags_AcceptNoDrawDefaultRect = 1 << 11, // Do not draw the default highlight rectangle when hovering over target.
ImGuiDragDropFlags_AcceptNoPreviewTooltip = 1 << 12, // Request hiding the BeginDragDropSource tooltip from the BeginDragDropTarget site.
ImGuiDragDropFlags_AcceptDrawAsHovered = 1 << 13, // Accepting item will render as if hovered. Useful for e.g. a Button() used as a drop target.
ImGuiDragDropFlags_AcceptPeekOnly = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect, // For peeking ahead and inspecting the payload before delivery.
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
@@ -1632,9 +1660,9 @@ enum ImGuiKey : int
ImGuiMod_Mask_ = 0xF000, // 4-bits
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
ImGuiKey_COUNT = ImGuiKey_NamedKey_END, // Obsoleted in 1.91.5 because it was extremely misleading (since named keys don't start at 0 anymore)
ImGuiKey_COUNT = ImGuiKey_NamedKey_END, // Obsoleted in 1.91.5 because it was misleading (since named keys don't start at 0 anymore)
ImGuiMod_Shortcut = ImGuiMod_Ctrl, // Removed in 1.90.7, you can now simply use ImGuiMod_Ctrl
ImGuiKey_ModCtrl = ImGuiMod_Ctrl, ImGuiKey_ModShift = ImGuiMod_Shift, ImGuiKey_ModAlt = ImGuiMod_Alt, ImGuiKey_ModSuper = ImGuiMod_Super, // Renamed in 1.89
//ImGuiKey_ModCtrl = ImGuiMod_Ctrl, ImGuiKey_ModShift = ImGuiMod_Shift, ImGuiKey_ModAlt = ImGuiMod_Alt, ImGuiKey_ModSuper = ImGuiMod_Super, // Renamed in 1.89
//ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter, // Renamed in 1.87
#endif
};
@@ -1656,7 +1684,7 @@ enum ImGuiInputFlags_
ImGuiInputFlags_RouteAlways = 1 << 13, // Do not register route, poll keys directly.
// - Routing options
ImGuiInputFlags_RouteOverFocused = 1 << 14, // Option: global route: higher priority than focused route (unless active item in focused route).
ImGuiInputFlags_RouteOverActive = 1 << 15, // Option: global route: higher priority than active item. Unlikely you need to use that: will interfere with every active items, e.g. CTRL+A registered by InputText will be overridden by this. May not be fully honored as user/internal code is likely to always assume they can access keys when active.
ImGuiInputFlags_RouteOverActive = 1 << 15, // Option: global route: higher priority than active item. Unlikely you need to use that: will interfere with every active items, e.g. Ctrl+A registered by InputText will be overridden by this. May not be fully honored as user/internal code is likely to always assume they can access keys when active.
ImGuiInputFlags_RouteUnlessBgFocused = 1 << 16, // Option: global route: will not be applied if underlying background/void is focused (== no Dear ImGui windows are focused). Useful for overlay applications.
ImGuiInputFlags_RouteFromRootWindow = 1 << 17, // Option: route evaluated from the point of view of root window rather than current window.
@@ -1751,10 +1779,12 @@ enum ImGuiCol_
ImGuiCol_TextLink, // Hyperlink color
ImGuiCol_TextSelectedBg, // Selected text inside an InputText
ImGuiCol_TreeLines, // Tree node hierarchy outlines when using ImGuiTreeNodeFlags_DrawLines
ImGuiCol_DragDropTarget, // Rectangle highlighting a drop target
ImGuiCol_DragDropTarget, // Rectangle border highlighting a drop target
ImGuiCol_DragDropTargetBg, // Rectangle background highlighting a drop target
ImGuiCol_UnsavedMarker, // Unsaved Document marker (in window title and tabs)
ImGuiCol_NavCursor, // Color of keyboard/gamepad navigation cursor/rectangle, when visible
ImGuiCol_NavWindowingHighlight, // Highlight window when using CTRL+TAB
ImGuiCol_NavWindowingDimBg, // Darken/colorize entire screen behind the CTRL+TAB window list, when active
ImGuiCol_NavWindowingHighlight, // Highlight window when using Ctrl+Tab
ImGuiCol_NavWindowingDimBg, // Darken/colorize entire screen behind the Ctrl+Tab window list, when active
ImGuiCol_ModalWindowDimBg, // Darken/colorize entire screen behind a modal window, when one is active
ImGuiCol_COUNT,
@@ -1770,9 +1800,9 @@ enum ImGuiCol_
// - The enum only refers to fields of ImGuiStyle which makes sense to be pushed/popped inside UI code.
// During initialization or between frames, feel free to just poke into ImGuiStyle directly.
// - Tip: Use your programming IDE navigation facilities on the names in the _second column_ below to find the actual members and their description.
// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
// - In Visual Studio: Ctrl+Comma ("Edit.GoToAll") can follow symbols inside comments, whereas Ctrl+F12 ("Edit.GoToImplementation") cannot.
// - In Visual Studio w/ Visual Assist installed: Alt+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
// - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments.
// - When changing this enum, you need to update the associated internal table GStyleVarInfo[] accordingly. This is where we link enum values to members offset/type.
enum ImGuiStyleVar_
{
@@ -1797,11 +1827,14 @@ enum ImGuiStyleVar_
ImGuiStyleVar_CellPadding, // ImVec2 CellPadding
ImGuiStyleVar_ScrollbarSize, // float ScrollbarSize
ImGuiStyleVar_ScrollbarRounding, // float ScrollbarRounding
ImGuiStyleVar_ScrollbarPadding, // float ScrollbarPadding
ImGuiStyleVar_GrabMinSize, // float GrabMinSize
ImGuiStyleVar_GrabRounding, // float GrabRounding
ImGuiStyleVar_ImageBorderSize, // float ImageBorderSize
ImGuiStyleVar_TabRounding, // float TabRounding
ImGuiStyleVar_TabBorderSize, // float TabBorderSize
ImGuiStyleVar_TabMinWidthBase, // float TabMinWidthBase
ImGuiStyleVar_TabMinWidthShrink, // float TabMinWidthShrink
ImGuiStyleVar_TabBarBorderSize, // float TabBarBorderSize
ImGuiStyleVar_TabBarOverlineSize, // float TabBarOverlineSize
ImGuiStyleVar_TableAngledHeadersAngle, // float TableAngledHeadersAngle
@@ -1876,7 +1909,7 @@ enum ImGuiColorEditFlags_
// Obsolete names
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
ImGuiColorEditFlags_AlphaPreview = 0, // [Removed in 1.91.8] This is the default now. Will display a checkerboard unless ImGuiColorEditFlags_AlphaNoBg is set.
ImGuiColorEditFlags_AlphaPreview = 0, // Removed in 1.91.8. This is the default now. Will display a checkerboard unless ImGuiColorEditFlags_AlphaNoBg is set.
#endif
//ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex // [renamed in 1.69]
};
@@ -1889,9 +1922,9 @@ enum ImGuiSliderFlags_
ImGuiSliderFlags_None = 0,
ImGuiSliderFlags_Logarithmic = 1 << 5, // Make the widget logarithmic (linear otherwise). Consider using ImGuiSliderFlags_NoRoundToFormat with this if using a format-string with small amount of digits.
ImGuiSliderFlags_NoRoundToFormat = 1 << 6, // Disable rounding underlying value to match precision of the display format string (e.g. %.3f values are rounded to those 3 digits).
ImGuiSliderFlags_NoInput = 1 << 7, // Disable CTRL+Click or Enter key allowing to input text directly into the widget.
ImGuiSliderFlags_NoInput = 1 << 7, // Disable Ctrl+Click or Enter key allowing to input text directly into the widget.
ImGuiSliderFlags_WrapAround = 1 << 8, // Enable wrapping around from max to min and from min to max. Only supported by DragXXX() functions for now.
ImGuiSliderFlags_ClampOnInput = 1 << 9, // Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds.
ImGuiSliderFlags_ClampOnInput = 1 << 9, // Clamp value to min/max bounds when input manually with Ctrl+Click. By default Ctrl+Click allows going out of bounds.
ImGuiSliderFlags_ClampZeroRange = 1 << 10, // Clamp even if min==max==0.0f. Otherwise due to legacy reason DragXXX functions don't clamp with those values. When your clamping limits are dynamic you almost always want to use it.
ImGuiSliderFlags_NoSpeedTweaks = 1 << 11, // Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic.
ImGuiSliderFlags_AlwaysClamp = ImGuiSliderFlags_ClampOnInput | ImGuiSliderFlags_ClampZeroRange,
@@ -2171,7 +2204,7 @@ struct ImVector
// Constructors, destructor
inline ImVector() { Size = Capacity = 0; Data = NULL; }
inline ImVector(const ImVector<T>& src) { Size = Capacity = 0; Data = NULL; operator=(src); }
inline ImVector<T>& operator=(const ImVector<T>& src) { clear(); resize(src.Size); if (src.Data) memcpy(Data, src.Data, (size_t)Size * sizeof(T)); return *this; }
inline ImVector<T>& operator=(const ImVector<T>& src) { clear(); resize(src.Size); if (Data && src.Data) memcpy(Data, src.Data, (size_t)Size * sizeof(T)); return *this; }
inline ~ImVector() { if (Data) IM_FREE(Data); } // Important: does not destruct anything
inline void clear() { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } } // Important: does not destruct anything
@@ -2261,12 +2294,15 @@ struct ImGuiStyle
float ColumnsMinSpacing; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
float ScrollbarSize; // Width of the vertical scrollbar, Height of the horizontal scrollbar.
float ScrollbarRounding; // Radius of grab corners for scrollbar.
float ScrollbarPadding; // Padding of scrollbar grab within its frame (same for both axes).
float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar.
float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
float LogSliderDeadzone; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
float ImageBorderSize; // Thickness of border around Image() calls.
float TabRounding; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
float TabBorderSize; // Thickness of border around tabs.
float TabMinWidthBase; // Minimum tab width, to make tabs larger than their contents. TabBar buttons are not affected.
float TabMinWidthShrink; // Minimum tab width after shrinking, when using ImGuiTabBarFlags_FittingPolicyMixed policy.
float TabCloseButtonMinWidthSelected; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width.
float TabCloseButtonMinWidthUnselected; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. FLT_MAX: never show close button when unselected.
float TabBarBorderSize; // Thickness of tab-bar separator, which takes on the tab active color to denote focus.
@@ -2276,6 +2312,9 @@ struct ImGuiStyle
ImGuiTreeNodeFlags TreeLinesFlags; // Default way to draw lines connecting TreeNode hierarchy. ImGuiTreeNodeFlags_DrawLinesNone or ImGuiTreeNodeFlags_DrawLinesFull or ImGuiTreeNodeFlags_DrawLinesToNodes.
float TreeLinesSize; // Thickness of outlines when using ImGuiTreeNodeFlags_DrawLines.
float TreeLinesRounding; // Radius of lines connecting child nodes to the vertical line.
float DragDropTargetRounding; // Radius of the drag and drop target frame.
float DragDropTargetBorderSize; // Thickness of the drag and drop target border.
float DragDropTargetPadding; // Size to expand the drag and drop target from actual target item size.
ImGuiDir ColorButtonPosition; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered).
ImVec2 SelectableTextAlign; // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.
@@ -2357,7 +2396,7 @@ struct ImGuiIO
// Font system
ImFontAtlas*Fonts; // <auto> // Font atlas: load, rasterize and pack one or more fonts into a single texture.
ImFont* FontDefault; // = NULL // Font to use on NewFrame(). Use NULL to uses Fonts->Fonts[0].
bool FontAllowUserScaling; // = false // [OBSOLETE] Allow user scaling text of individual window with CTRL+Wheel.
bool FontAllowUserScaling; // = false // Allow user scaling text of individual window with Ctrl+Wheel.
// Keyboard/Gamepad Navigation options
bool ConfigNavSwapGamepadButtons; // = false // Swap Activate<>Cancel (A<>B) buttons, matching typical "Nintendo/Japanese style" gamepad layout.
@@ -2378,7 +2417,7 @@ struct ImGuiIO
bool ConfigDragClickToInputText; // = false // [BETA] Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving). Not desirable on devices without a keyboard.
bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires ImGuiBackendFlags_HasMouseCursors for better mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag)
bool ConfigWindowsMoveFromTitleBarOnly; // = false // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar.
bool ConfigWindowsCopyContentsWithCtrlC; // = false // [EXPERIMENTAL] CTRL+C copy the contents of focused window into the clipboard. Experimental because: (1) has known issues with nested Begin/End pairs (2) text output quality varies (3) text output is in submission order rather than spatial order.
bool ConfigWindowsCopyContentsWithCtrlC; // = false // [EXPERIMENTAL] Ctrl+C copy the contents of focused window into the clipboard. Experimental because: (1) has known issues with nested Begin/End pairs (2) text output quality varies (3) text output is in submission order rather than spatial order.
bool ConfigScrollbarScrollByPage; // = true // Enable scrolling page by page when clicking outside the scrollbar grab. When disabled, always scroll to clicked location. When enabled, Shift+Click scrolls to clicked location.
float ConfigMemoryCompactTimer; // = 60.0f // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable.
@@ -2416,7 +2455,7 @@ struct ImGuiIO
// Option to enable various debug tools showing buttons that will call the IM_DEBUG_BREAK() macro.
// - The Item Picker tool will be available regardless of this being enabled, in order to maximize its discoverability.
// - Requires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.
// e.g. io.ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent() on Win32, or refer to ImOsIsDebuggerPresent() imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version).
// e.g. io.ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent() on Win32, or refer to ImOsIsDebuggerPresent() imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version.
bool ConfigDebugIsDebuggerPresent; // = false // Enable various tools calling IM_DEBUG_BREAK().
// Tools to detect code submitting items with conflicting/duplicate IDs
@@ -2475,9 +2514,6 @@ struct ImGuiIO
IMGUI_API void ClearEventsQueue(); // Clear all incoming events.
IMGUI_API void ClearInputKeys(); // Clear current keyboard/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons.
IMGUI_API void ClearInputMouse(); // Clear current mouse state.
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
IMGUI_API void ClearInputCharacters(); // [Obsoleted in 1.89.8] Clear the current frame text input buffer. Now included within ClearInputKeys().
#endif
//------------------------------------------------------------------
// Output - Updated by NewFrame() or EndFrame()/Render()
@@ -2510,16 +2546,16 @@ struct ImGuiIO
// (reading from those variables is fair game, as they are extremely unlikely to be moving anywhere)
ImVec2 MousePos; // Mouse position, in pixels. Set to ImVec2(-FLT_MAX, -FLT_MAX) if mouse is unavailable (on another screen, etc.)
bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Other buttons allow us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API.
float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. >0 scrolls Up, <0 scrolls Down. Hold SHIFT to turn vertical scroll into horizontal scroll.
float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. >0 scrolls Up, <0 scrolls Down. Hold Shift to turn vertical scroll into horizontal scroll.
float MouseWheelH; // Mouse wheel Horizontal. >0 scrolls Left, <0 scrolls Right. Most users don't have a mouse with a horizontal wheel, may not be filled by all backends.
ImGuiMouseSource MouseSource; // Mouse actual input peripheral (Mouse/TouchScreen/Pen).
bool KeyCtrl; // Keyboard modifier down: Control
bool KeyCtrl; // Keyboard modifier down: Ctrl (non-macOS), Cmd (macOS)
bool KeyShift; // Keyboard modifier down: Shift
bool KeyAlt; // Keyboard modifier down: Alt
bool KeySuper; // Keyboard modifier down: Cmd/Super/Windows
bool KeySuper; // Keyboard modifier down: Windows/Super (non-macOS), Ctrl (macOS)
// Other state maintained from data above + IO function calls
ImGuiKeyChord KeyMods; // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags. Read-only, updated by NewFrame()
ImGuiKeyChord KeyMods; // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags). Read-only, updated by NewFrame()
ImGuiKeyData KeysData[ImGuiKey_NamedKey_COUNT];// Key state for all known keys. MUST use 'key - ImGuiKey_NamedKey_BEGIN' as index. Use IsKeyXXX() functions to access this.
bool WantCaptureMouseUnlessPopupClose; // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup.
ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid)
@@ -2533,8 +2569,8 @@ struct ImGuiIO
double MouseReleasedTime[5]; // Time of last released (rarely used! but useful to handle delayed single-click when trying to disambiguate them from double-click).
bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds.
bool MouseDownOwnedUnlessPopupClose[5]; // Track if button was clicked inside a dear imgui window.
bool MouseWheelRequestAxisSwap; // On a non-Mac system, holding SHIFT requests WheelY to perform the equivalent of a WheelX event. On a Mac system this is already enforced by the system.
bool MouseCtrlLeftAsRightClick; // (OSX) Set to true when the current click was a Ctrl+click that spawned a simulated right click
bool MouseWheelRequestAxisSwap; // On a non-Mac system, holding Shift requests WheelY to perform the equivalent of a WheelX event. On a Mac system this is already enforced by the system.
bool MouseCtrlLeftAsRightClick; // (OSX) Set to true when the current click was a Ctrl+Click that spawned a simulated right click
float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked)
float MouseDownDurationPrev[5]; // Previous time the mouse button has been down
float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point (used for moving thresholds)
@@ -2562,6 +2598,8 @@ struct ImGuiIO
const char* (*GetClipboardTextFn)(void* user_data);
void (*SetClipboardTextFn)(void* user_data, const char* text);
void* ClipboardUserData;
//void ClearInputCharacters() { InputQueueCharacters.resize(0); } // [Obsoleted in 1.89.8] Clear the current frame text input buffer. Now included within ClearInputKeys(). Removed this as it is ambiguous/misleading and generally incorrect to use with the existence of a higher-level input queue.
#endif
IMGUI_API ImGuiIO();
@@ -2596,10 +2634,10 @@ struct ImGuiInputTextCallbackData
ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only // [Completion,History]
char* Buf; // Text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer!
int BufTextLen; // Text length (in bytes) // Read-write // [Resize,Completion,History,Always] Exclude zero-terminator storage. In C land: == strlen(some_text), in C++ land: string.length()
int BufSize; // Buffer size (in bytes) = capacity+1 // Read-only // [Resize,Completion,History,Always] Include zero-terminator storage. In C land == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1
int BufSize; // Buffer size (in bytes) = capacity+1 // Read-only // [Resize,Completion,History,Always] Include zero-terminator storage. In C land: == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1
bool BufDirty; // Set if you modify Buf/BufTextLen! // Write // [Completion,History,Always]
int CursorPos; // // Read-write // [Completion,History,Always]
int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection)
int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection
int SelectionEnd; // // Read-write // [Completion,History,Always]
// Helper functions for text manipulation.
@@ -2768,6 +2806,13 @@ struct ImGuiStorage
#endif
};
// Flags for ImGuiListClipper (currently not fully exposed in function calls: a future refactor will likely add this to ImGuiListClipper::Begin function equivalent)
enum ImGuiListClipperFlags_
{
ImGuiListClipperFlags_None = 0,
ImGuiListClipperFlags_NoSetTableRowCounters = 1 << 0, // [Internal] Disabled modifying table row counters. Avoid assumption that 1 clipper item == 1 table row.
};
// Helper: Manually clip large list of items.
// If you have lots evenly spaced items and you have random access to the list, you can perform coarse
// clipping based on visibility to only submit items that are in view.
@@ -2798,6 +2843,7 @@ struct ImGuiListClipper
double StartPosY; // [Internal] Cursor position at the time of Begin() or after table frozen rows are all processed
double StartSeekOffsetY; // [Internal] Account for frozen rows in a table and initial loss of precision in very large windows.
void* TempData; // [Internal] Internal data
ImGuiListClipperFlags Flags; // [Internal] Flags, currently not yet well exposed.
// items_count: Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step, and you can call SeekCursorForItem() manually if you need)
// items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing().
@@ -2818,7 +2864,7 @@ struct ImGuiListClipper
IMGUI_API void SeekCursorForItem(int item_index);
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
inline void IncludeRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.9]
//inline void IncludeRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.9]
//inline void ForceDisplayRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.6]
//inline ImGuiListClipper(int items_count, float items_height = -1.0f) { memset(this, 0, sizeof(*this)); ItemsCount = -1; Begin(items_count, items_height); } // [removed in 1.79]
#endif
@@ -2912,7 +2958,7 @@ struct ImColor
// Multi-selection system
// Documentation at: https://github.com/ocornut/imgui/wiki/Multi-Select
// - Refer to 'Demo->Widgets->Selection State & Multi-Select' for demos using this.
// - This system implements standard multi-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc)
// - This system implements standard multi-selection idioms (Ctrl+Mouse/Keyboard, Shift+Mouse/Keyboard, etc)
// with support for clipper (skipping non-visible items), box-select and many other details.
// - Selectable(), Checkbox() are supported but custom widgets may use it as well.
// - TreeNode() is technically supported but... using this correctly is more complicated: you need some sort of linear/random access to your tree,
@@ -2950,7 +2996,7 @@ enum ImGuiMultiSelectFlags_
{
ImGuiMultiSelectFlags_None = 0,
ImGuiMultiSelectFlags_SingleSelect = 1 << 0, // Disable selecting more than one item. This is available to allow single-selection code to share same code/logic if desired. It essentially disables the main purpose of BeginMultiSelect() tho!
ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable CTRL+A shortcut to select all.
ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable Ctrl+A shortcut to select all.
ImGuiMultiSelectFlags_NoRangeSelect = 1 << 2, // Disable Shift+selection mouse/keyboard support (useful for unordered 2D selection). With BoxSelect is also ensure contiguous SetRange requests are not combined into one. This allows not handling interpolation in SetRange requests.
ImGuiMultiSelectFlags_NoAutoSelect = 1 << 3, // Disable selecting items when navigating (useful for e.g. supporting range-select in a list of checkboxes).
ImGuiMultiSelectFlags_NoAutoClear = 1 << 4, // Disable clearing selection when navigating or selecting another one (generally used with ImGuiMultiSelectFlags_NoAutoSelect. useful for e.g. supporting range-select in a list of checkboxes).
@@ -2966,6 +3012,7 @@ enum ImGuiMultiSelectFlags_
ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 14, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection.
//ImGuiMultiSelectFlags_RangeSelect2d = 1 << 15, // Shift+Selection uses 2d geometry instead of linear sequence, so possible to use Shift+up/down to select vertically in grid. Analogous to what BoxSelect does.
ImGuiMultiSelectFlags_NavWrapX = 1 << 16, // [Temporary] Enable navigation wrapping on X axis. Provided as a convenience because we don't have a design for the general Nav API for this yet. When the more general feature be public we may obsolete this flag in favor of new one.
ImGuiMultiSelectFlags_NoSelectOnRightClick = 1 << 17, // Disable default right-click processing, which selects item on mouse down, and is designed for context-menus.
};
// Main IO structure returned by BeginMultiSelect()/EndMultiSelect().
@@ -3108,7 +3155,7 @@ struct ImDrawCmd
// Since 1.83: returns ImTextureID associated with this draw call. Warning: DO NOT assume this is always same as 'TextureId' (we will change this function for an upcoming feature)
// Since 1.92: removed ImDrawCmd::TextureId field, the getter function must be used!
inline ImTextureID GetTexID() const; // == (TexRef._TexData ? TexRef._TexData->TexID : TexRef._TexID
inline ImTextureID GetTexID() const; // == (TexRef._TexData ? TexRef._TexData->TexID : TexRef._TexID)
};
// Vertex layout
@@ -3304,7 +3351,7 @@ struct ImDrawList
// Advanced: Miscellaneous
IMGUI_API void AddDrawCmd(); // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible
IMGUI_API ImDrawList* CloneOutput() const; // Create a clone of the CmdBuffer/IdxBuffer/VtxBuffer.
IMGUI_API ImDrawList* CloneOutput() const; // Create a clone of the CmdBuffer/IdxBuffer/VtxBuffer. For multi-threaded rendering, consider using `imgui_threaded_rendering` from https://github.com/ocornut/imgui_club instead.
// Advanced: Channels
// - Use to split render into layers. By switching channels to can render out-of-order (e.g. submit FG primitives before BG primitives)
@@ -3330,8 +3377,8 @@ struct ImDrawList
// Obsolete names
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
inline void PushTextureID(ImTextureRef tex_ref) { PushTexture(tex_ref); } // RENAMED in 1.92.x
inline void PopTextureID() { PopTexture(); } // RENAMED in 1.92.x
inline void PushTextureID(ImTextureRef tex_ref) { PushTexture(tex_ref); } // RENAMED in 1.92.0
inline void PopTextureID() { PopTexture(); } // RENAMED in 1.92.0
#endif
//inline void AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f) { AddEllipse(center, ImVec2(radius_x, radius_y), col, rot, num_segments, thickness); } // OBSOLETED in 1.90.5 (Mar 2024)
//inline void AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0) { AddEllipseFilled(center, ImVec2(radius_x, radius_y), col, rot, num_segments); } // OBSOLETED in 1.90.5 (Mar 2024)
@@ -3360,7 +3407,7 @@ struct ImDrawList
struct ImDrawData
{
bool Valid; // Only valid after Render() is called and before the next NewFrame() is called.
int CmdListsCount; // Number of ImDrawList* to render. (== CmdLists.Size). Exists for legacy reason.
int CmdListsCount; // == CmdLists.Size. (OBSOLETE: exists for legacy reasons). Number of ImDrawList* to render.
int TotalIdxCount; // For convenience, sum of all ImDrawList's IdxBuffer.Size
int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size
ImVector<ImDrawList*> CmdLists; // Array of ImDrawList* to render. The ImDrawLists are owned by ImGuiContext and only pointed to from here.
@@ -3368,7 +3415,7 @@ struct ImDrawData
ImVec2 DisplaySize; // Size of the viewport to render (== GetMainViewport()->Size for the main viewport, == io.DisplaySize in most single-viewport applications)
ImVec2 FramebufferScale; // Amount of pixels for each unit of DisplaySize. Copied from viewport->FramebufferScale (== io.DisplayFramebufferScale for main viewport). Generally (1,1) on normal display, (2,2) on OSX with Retina display.
ImGuiViewport* OwnerViewport; // Viewport carrying the ImDrawData instance, might be of use to the renderer (generally not).
ImVector<ImTextureData*>* Textures; // List of textures to update. Most of the times the list is shared by all ImDrawData, has only 1 texture and it doesn't need any update. This almost always points to ImGui::GetPlatformIO().Textures[]. May be overriden or set to NULL if you want to manually update textures.
ImVector<ImTextureData*>* Textures; // List of textures to update. Most of the times the list is shared by all ImDrawData, has only 1 texture and it doesn't need any update. This almost always points to ImGui::GetPlatformIO().Textures[]. May be overridden or set to NULL if you want to manually update textures.
// Functions
ImDrawData() { Clear(); }
@@ -3425,7 +3472,7 @@ struct ImTextureRect
struct ImTextureData
{
//------------------------------------------ core / backend ---------------------------------------
int UniqueID; // w - // Sequential index to facilitate identifying a texture when debugging/printing. Unique per atlas.
int UniqueID; // w - // [DEBUG] Sequential index to facilitate identifying a texture when debugging/printing. Unique per atlas.
ImTextureStatus Status; // rw rw // ImTextureStatus_OK/_WantCreate/_WantUpdates/_WantDestroy. Always use SetStatus() to modify!
void* BackendUserData; // - rw // Convenience storage for backend. Some backends may have enough with TexID.
ImTextureID TexID; // r w // Backend-specific texture identifier. Always use SetTexID() to modify! The identifier will stored in ImDrawCmd::GetTexID() and passed to backend's RenderDrawData function.
@@ -3443,7 +3490,7 @@ struct ImTextureData
bool WantDestroyNextFrame; // rw - // [Internal] Queued to set ImTextureStatus_WantDestroy next frame. May still be used in the current frame.
// Functions
ImTextureData() { memset(this, 0, sizeof(*this)); TexID = ImTextureID_Invalid; }
ImTextureData() { memset(this, 0, sizeof(*this)); Status = ImTextureStatus_Destroyed; TexID = ImTextureID_Invalid; }
~ImTextureData() { DestroyPixels(); }
IMGUI_API void Create(ImTextureFormat format, int w, int h);
IMGUI_API void DestroyPixels();
@@ -3455,8 +3502,10 @@ struct ImTextureData
ImTextureID GetTexID() const { return TexID; }
// Called by Renderer backend
void SetTexID(ImTextureID tex_id) { TexID = tex_id; } // Call after creating or destroying the texture. Never modify TexID directly!
void SetStatus(ImTextureStatus status) { Status = status; } // Call after honoring a request. Never modify Status directly!
// - Call SetTexID() and SetStatus() after honoring texture requests. Never modify TexID and Status directly!
// - A backend may decide to destroy a texture that we did not request to destroy, which is fine (e.g. freeing resources), but we immediately set the texture back in _WantCreate mode.
void SetTexID(ImTextureID tex_id) { TexID = tex_id; }
void SetStatus(ImTextureStatus status) { Status = status; if (status == ImTextureStatus_Destroyed && !WantDestroyNextFrame) Status = ImTextureStatus_WantCreate; }
};
//-----------------------------------------------------------------------------
@@ -3470,7 +3519,7 @@ struct ImFontConfig
char Name[40]; // <auto> // Name (strictly to ease debugging, hence limited size buffer)
void* FontData; // // TTF/OTF data
int FontDataSize; // // TTF/OTF data size
bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself).
bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the owner ImFontAtlas (will delete memory itself).
// Options
bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights.
@@ -3591,7 +3640,7 @@ struct ImFontAtlas
IMGUI_API ImFont* AddFontFromMemoryCompressedBase85TTF(const char* compressed_font_data_base85, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 parameter.
IMGUI_API void RemoveFont(ImFont* font);
IMGUI_API void Clear(); // Clear everything (input fonts, output glyphs/textures)
IMGUI_API void Clear(); // Clear everything (input fonts, output glyphs/textures).
IMGUI_API void CompactCache(); // Compact cached glyphs and texture.
IMGUI_API void SetFontLoader(const ImFontLoader* font_loader); // Change font loader at runtime.
@@ -3605,7 +3654,7 @@ struct ImFontAtlas
// - User is in charge of copying the pixels into graphics memory (e.g. create a texture with your engine). Then store your texture handle with SetTexID().
// - The pitch is always = Width * BytesPerPixels (1 or 4)
// - Building in RGBA32 format is provided for convenience and compatibility, but note that unless you manually manipulate or copy color data into
// the texture (e.g. when using the AddCustomRect*** api), then the RGB pixels emitted will always be white (~75% of memory/bandwidth waste.
// the texture (e.g. when using the AddCustomRect*** api), then the RGB pixels emitted will always be white (~75% of memory/bandwidth waste).
// - From 1.92 with backends supporting ImGuiBackendFlags_RendererHasTextures:
// - Calling Build(), GetTexDataAsAlpha8(), GetTexDataAsRGBA32() is not needed.
// - In backend: replace calls to ImFontAtlas::SetTexID() with calls to ImTextureData::SetTexID() after honoring texture creation.
@@ -3644,7 +3693,7 @@ struct ImFontAtlas
// Register and retrieve custom rectangles
// - You can request arbitrary rectangles to be packed into the atlas, for your own purpose.
// - Since 1.92.X, packing is done immediately in the function call (previously packing was done during the Build call)
// - Since 1.92.0, packing is done immediately in the function call (previously packing was done during the Build call)
// - You can render your pixels into the texture right after calling the AddCustomRect() functions.
// - VERY IMPORTANT:
// - Texture may be created/resized at any time when calling ImGui or ImFontAtlas functions.
@@ -3683,7 +3732,7 @@ struct ImFontAtlas
#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
ImTextureRef TexRef; // Latest texture identifier == TexData->GetTexRef().
#else
union { ImTextureRef TexRef; ImTextureRef TexID; }; // Latest texture identifier == TexData->GetTexRef(). // RENAMED TexID to TexRef in 1.92.x
union { ImTextureRef TexRef; ImTextureRef TexID; }; // Latest texture identifier == TexData->GetTexRef(). // RENAMED TexID to TexRef in 1.92.0.
#endif
ImTextureData* TexData; // Latest texture.
@@ -3713,15 +3762,15 @@ struct ImFontAtlas
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
// Legacy: You can request your rectangles to be mapped as font glyph (given a font + Unicode point), so you can render e.g. custom colorful icons and use them as regular glyphs. --> Prefer using a custom ImFontLoader.
ImFontAtlasRect TempRect; // For old GetCustomRectByIndex() API
inline ImFontAtlasRectId AddCustomRectRegular(int w, int h) { return AddCustomRect(w, h); } // RENAMED in 1.92.X
inline const ImFontAtlasRect* GetCustomRectByIndex(ImFontAtlasRectId id) { return GetCustomRect(id, &TempRect) ? &TempRect : NULL; } // OBSOLETED in 1.92.X
inline void CalcCustomRectUV(const ImFontAtlasRect* r, ImVec2* out_uv_min, ImVec2* out_uv_max) const { *out_uv_min = r->uv0; *out_uv_max = r->uv1; } // OBSOLETED in 1.92.X
IMGUI_API ImFontAtlasRectId AddCustomRectFontGlyph(ImFont* font, ImWchar codepoint, int w, int h, float advance_x, const ImVec2& offset = ImVec2(0, 0)); // OBSOLETED in 1.92.X: Use custom ImFontLoader in ImFontConfig
IMGUI_API ImFontAtlasRectId AddCustomRectFontGlyphForSize(ImFont* font, float font_size, ImWchar codepoint, int w, int h, float advance_x, const ImVec2& offset = ImVec2(0, 0)); // ADDED AND OBSOLETED in 1.92.X
inline ImFontAtlasRectId AddCustomRectRegular(int w, int h) { return AddCustomRect(w, h); } // RENAMED in 1.92.0
inline const ImFontAtlasRect* GetCustomRectByIndex(ImFontAtlasRectId id) { return GetCustomRect(id, &TempRect) ? &TempRect : NULL; } // OBSOLETED in 1.92.0
inline void CalcCustomRectUV(const ImFontAtlasRect* r, ImVec2* out_uv_min, ImVec2* out_uv_max) const { *out_uv_min = r->uv0; *out_uv_max = r->uv1; } // OBSOLETED in 1.92.0
IMGUI_API ImFontAtlasRectId AddCustomRectFontGlyph(ImFont* font, ImWchar codepoint, int w, int h, float advance_x, const ImVec2& offset = ImVec2(0, 0)); // OBSOLETED in 1.92.0: Use custom ImFontLoader in ImFontConfig
IMGUI_API ImFontAtlasRectId AddCustomRectFontGlyphForSize(ImFont* font, float font_size, ImWchar codepoint, int w, int h, float advance_x, const ImVec2& offset = ImVec2(0, 0)); // ADDED AND OBSOLETED in 1.92.0
#endif
//unsigned int FontBuilderFlags; // OBSOLETED in 1.92.X: Renamed to FontLoaderFlags.
//int TexDesiredWidth; // OBSOLETED in 1.92.X: Force texture width before calling Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height)
//typedef ImFontAtlasRect ImFontAtlasCustomRect; // OBSOLETED in 1.92.X
//unsigned int FontBuilderFlags; // OBSOLETED in 1.92.0: Renamed to FontLoaderFlags.
//int TexDesiredWidth; // OBSOLETED in 1.92.0: Force texture width before calling Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height.
//typedef ImFontAtlasRect ImFontAtlasCustomRect; // OBSOLETED in 1.92.0
//typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+
//typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+
};
@@ -3745,10 +3794,11 @@ struct ImFontBaked
float Ascent, Descent; // 4+4 // out // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] (unscaled)
unsigned int MetricsTotalSurface:26;// 3 // out // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs)
unsigned int WantDestroy:1; // 0 // // Queued for destroy
unsigned int LockLoadingFallback:1; // 0 // //
int LastUsedFrame; // 4 // // Record of that time this was bounds
ImGuiID BakedId; // 4 //
ImFont* ContainerFont; // 4-8 // in // Parent font
unsigned int LoadNoFallback:1; // 0 // // Disable loading fallback in lower-level calls.
unsigned int LoadNoRenderOnLayout:1;// 0 // // Enable a two-steps mode where CalcTextSize() calls will load AdvanceX *without* rendering/packing glyphs. Only advantagous if you know that the glyph is unlikely to actually be rendered, otherwise it is slower because we'd do one query on the first CalcTextSize and one query on the first Draw.
int LastUsedFrame; // 4 // // Record of that time this was bounds
ImGuiID BakedId; // 4 // // Unique ID for this baked storage
ImFont* OwnerFont; // 4-8 // in // Parent font
void* FontLoaderDatas; // 4-8 // // Font loader opaque storage (per baked font * sources): single contiguous buffer allocated by imgui, passed to loader.
// Functions
@@ -3772,14 +3822,14 @@ enum ImFontFlags_
// Font runtime data and rendering
// - ImFontAtlas automatically loads a default embedded font for you if you didn't load one manually.
// - Since 1.92.X a font may be rendered as any size! Therefore a font doesn't have one specific size.
// - Since 1.92.0 a font may be rendered as any size! Therefore a font doesn't have one specific size.
// - Use 'font->GetFontBaked(size)' to retrieve the ImFontBaked* corresponding to a given size.
// - If you used g.Font + g.FontSize (which is frequent from the ImGui layer), you can use g.FontBaked as a shortcut, as g.FontBaked == g.Font->GetFontBaked(g.FontSize).
struct ImFont
{
// [Internal] Members: Hot ~12-20 bytes
ImFontBaked* LastBaked; // 4-8 // Cache last bound baked. NEVER USE DIRECTLY. Use GetFontBaked().
ImFontAtlas* ContainerAtlas; // 4-8 // What we have been loaded into.
ImFontAtlas* OwnerAtlas; // 4-8 // What we have been loaded into.
ImFontFlags Flags; // 4 // Font flags.
float CurrentRasterizerDensity; // Current rasterizer density. This is a varying state of the font.
@@ -3787,7 +3837,7 @@ struct ImFont
// Conceptually Sources[] is the list of font sources merged to create this font.
ImGuiID FontId; // Unique identifier for the font
float LegacySize; // 4 // in // Font size passed to AddFont(). Use for old code calling PushFont() expecting to use that size. (use ImGui::GetFontBaked() to get font baked at current bound size).
ImVector<ImFontConfig*> Sources; // 16 // in // List of sources. Pointers within ContainerAtlas->Sources[]
ImVector<ImFontConfig*> Sources; // 16 // in // List of sources. Pointers within OwnerAtlas->Sources[]
ImWchar EllipsisChar; // 2-4 // out // Character used for ellipsis rendering ('...').
ImWchar FallbackChar; // 2-4 // out // Character used if a glyph isn't found (U+FFFD, '?')
ImU8 Used8kPagesMap[(IM_UNICODE_CODEPOINT_MAX+1)/8192/8]; // 1 bytes if ImWchar=ImWchar16, 16 bytes if ImWchar==ImWchar32. Store 1-bit for each block of 4K codepoints that has one active glyph. This is mainly used to facilitate iterations across all used codepoints.
@@ -3801,17 +3851,17 @@ struct ImFont
IMGUI_API ImFont();
IMGUI_API ~ImFont();
IMGUI_API bool IsGlyphInFont(ImWchar c);
bool IsLoaded() const { return ContainerAtlas != NULL; }
bool IsLoaded() const { return OwnerAtlas != NULL; }
const char* GetDebugName() const { return Sources.Size ? Sources[0]->Name : "<unknown>"; } // Fill ImFontConfig::Name.
// [Internal] Don't use!
// 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable.
// 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable.
IMGUI_API ImFontBaked* GetFontBaked(float font_size, float density = -1.0f); // Get or create baked data for given size
IMGUI_API ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** remaining = NULL); // utf8
IMGUI_API ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** out_remaining = NULL);
IMGUI_API const char* CalcWordWrapPosition(float size, const char* text, const char* text_end, float wrap_width);
IMGUI_API void RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c, const ImVec4* cpu_fine_clip = NULL);
IMGUI_API void RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, bool cpu_fine_clip = false);
IMGUI_API void RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, ImDrawTextFlags flags = 0);
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
inline const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) { return CalcWordWrapPosition(LegacySize * scale, text, text_end, wrap_width); }
#endif
@@ -3901,7 +3951,7 @@ struct ImGuiPlatformIO
void* Platform_ClipboardUserData;
// Optional: Open link/folder/file in OS Shell
// (default to use ShellExecuteW() on Windows, system() on Linux/Mac)
// (default to use ShellExecuteW() on Windows, system() on Linux/Mac. expected to return false on failure, but some platforms may always return true)
bool (*Platform_OpenInShellFn)(ImGuiContext* ctx, const char* path);
void* Platform_OpenInShellUserData;
@@ -3933,6 +3983,13 @@ struct ImGuiPlatformIO
// Textures list (the list is updated by calling ImGui::EndFrame or ImGui::Render)
// The ImGui_ImplXXXX_RenderDrawData() function of each backend generally access this via ImDrawData::Textures which points to this. The array is available here mostly because backends will want to destroy textures on shutdown.
ImVector<ImTextureData*> Textures; // List of textures used by Dear ImGui (most often 1) + contents of external texture list is automatically appended into this.
//------------------------------------------------------------------
// Functions
//------------------------------------------------------------------
IMGUI_API void ClearPlatformHandlers(); // Clear all Platform_XXX fields. Typically called on Platform Backend shutdown.
IMGUI_API void ClearRendererHandlers(); // Clear all Renderer_XXX fields. Typically called on Renderer Backend shutdown.
};
// (Optional) Support for IME (Input Method Editor) via the platform_io.Platform_SetImeDataFn() function. Handler is called during EndFrame().
@@ -3957,30 +4014,30 @@ struct ImGuiPlatformImeData
namespace ImGui
{
// OBSOLETED in 1.92.0 (from June 2025)
static inline void PushFont(ImFont* font) { PushFont(font, font ? font->LegacySize : 0.0f); }
inline void PushFont(ImFont* font) { PushFont(font, font ? font->LegacySize : 0.0f); }
IMGUI_API void SetWindowFontScale(float scale); // Set font scale factor for current window. Prefer using PushFont(NULL, style.FontSizeBase * factor) or use style.FontScaleMain to scale all windows.
// OBSOLETED in 1.91.9 (from February 2025)
IMGUI_API void Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col); // <-- 'border_col' was removed in favor of ImGuiCol_ImageBorder. If you use 'tint_col', use ImageWithBg() instead.
// OBSOLETED in 1.91.0 (from July 2024)
static inline void PushButtonRepeat(bool repeat) { PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); }
static inline void PopButtonRepeat() { PopItemFlag(); }
static inline void PushTabStop(bool tab_stop) { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); }
static inline void PopTabStop() { PopItemFlag(); }
inline void PushButtonRepeat(bool repeat) { PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); }
inline void PopButtonRepeat() { PopItemFlag(); }
inline void PushTabStop(bool tab_stop) { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); }
inline void PopTabStop() { PopItemFlag(); }
IMGUI_API ImVec2 GetContentRegionMax(); // Content boundaries max (e.g. window boundaries including scrolling, or current column boundaries). You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()!
IMGUI_API ImVec2 GetWindowContentRegionMin(); // Content boundaries min for the window (roughly (0,0)-Scroll), in window-local coordinates. You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()!
IMGUI_API ImVec2 GetWindowContentRegionMax(); // Content boundaries max for the window (roughly (0,0)+Size-Scroll), in window-local coordinates. You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()!
// OBSOLETED in 1.90.0 (from September 2023)
static inline bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags window_flags = 0) { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, window_flags); }
static inline void EndChildFrame() { EndChild(); }
//static inline bool BeginChild(const char* str_id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags){ return BeginChild(str_id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders
//static inline bool BeginChild(ImGuiID id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags) { return BeginChild(id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders
static inline void ShowStackToolWindow(bool* p_open = NULL) { ShowIDStackToolWindow(p_open); }
inline bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags window_flags = 0) { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, window_flags); }
inline void EndChildFrame() { EndChild(); }
//inline bool BeginChild(const char* str_id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags){ return BeginChild(str_id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders
//inline bool BeginChild(ImGuiID id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags) { return BeginChild(id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders
inline void ShowStackToolWindow(bool* p_open = NULL) { ShowIDStackToolWindow(p_open); }
IMGUI_API bool Combo(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int popup_max_height_in_items = -1);
IMGUI_API bool ListBox(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int height_in_items = -1);
// OBSOLETED in 1.89.7 (from June 2023)
IMGUI_API void SetItemAllowOverlap(); // Use SetNextItemAllowOverlap() before item.
// Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE)
// OBSOLETED in 1.89.7 (from June 2023)
//IMGUI_API void SetItemAllowOverlap(); // Use SetNextItemAllowOverlap() _before_ item.
//-- OBSOLETED in 1.89.4 (from March 2023)
//static inline void PushAllowKeyboardFocus(bool tab_stop) { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); }
//static inline void PopAllowKeyboardFocus() { PopItemFlag(); }
@@ -4046,7 +4103,7 @@ namespace ImGui
//static inline void SetScrollPosHere() { SetScrollHere(); } // OBSOLETED in 1.42
}
//-- OBSOLETED in 1.92.x: ImFontAtlasCustomRect becomes ImTextureRect
//-- OBSOLETED in 1.92.0: ImFontAtlasCustomRect becomes ImTextureRect
// - ImFontAtlasCustomRect::X,Y --> ImTextureRect::x,y
// - ImFontAtlasCustomRect::Width,Height --> ImTextureRect::w,h
// - ImFontAtlasCustomRect::GlyphColored --> if you need to write to this, instead you can write to 'font->Glyphs.back()->Colored' after calling AddCustomRectFontGlyph()
@@ -4082,7 +4139,7 @@ typedef ImFontAtlasRect ImFontAtlasCustomRect;
//};
// RENAMED and MERGED both ImGuiKey_ModXXX and ImGuiModFlags_XXX into ImGuiMod_XXX (from September 2022)
// RENAMED ImGuiKeyModFlags -> ImGuiModFlags in 1.88 (from April 2022). Exceptionally commented out ahead of obscolescence schedule to reduce confusion and because they were not meant to be used in the first place.
// RENAMED ImGuiKeyModFlags -> ImGuiModFlags in 1.88 (from April 2022). Exceptionally commented out ahead of obsolescence schedule to reduce confusion and because they were not meant to be used in the first place.
//typedef ImGuiKeyChord ImGuiModFlags; // == int. We generally use ImGuiKeyChord to mean "a ImGuiKey or-ed with any number of ImGuiMod_XXX value", so you may store mods in there.
//enum ImGuiModFlags_ { ImGuiModFlags_None = 0, ImGuiModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiModFlags_Shift = ImGuiMod_Shift, ImGuiModFlags_Alt = ImGuiMod_Alt, ImGuiModFlags_Super = ImGuiMod_Super };
//typedef ImGuiKeyChord ImGuiKeyModFlags; // == int
Regular → Executable
+161 -104
View File
@@ -1,4 +1,4 @@
// dear imgui, v1.92.1
// dear imgui, v1.92.5
// (demo code)
// Help:
@@ -59,9 +59,9 @@
// Because we can't assume anything about your support of maths operators, we cannot use them in imgui_demo.cpp.
// Navigating this file:
// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
// - In Visual Studio: Ctrl+Comma ("Edit.GoToAll") can follow symbols inside comments, whereas Ctrl+F12 ("Edit.GoToImplementation") cannot.
// - In Visual Studio w/ Visual Assist installed: Alt+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
// - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments.
// - You can search/grep for all sections listed in the index to find the section.
/*
@@ -288,7 +288,7 @@ extern ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback;
extern void* GImGuiDemoMarkerCallbackUserData;
ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback = NULL;
void* GImGuiDemoMarkerCallbackUserData = NULL;
#define IMGUI_DEMO_MARKER(section) do { if (GImGuiDemoMarkerCallback != NULL) GImGuiDemoMarkerCallback(__FILE__, __LINE__, section, GImGuiDemoMarkerCallbackUserData); } while (0)
#define IMGUI_DEMO_MARKER(section) do { if (GImGuiDemoMarkerCallback != NULL) GImGuiDemoMarkerCallback("imgui_demo.cpp", __LINE__, section, GImGuiDemoMarkerCallbackUserData); } while (0)
//-----------------------------------------------------------------------------
// [SECTION] Demo Window / ShowDemoWindow()
@@ -510,7 +510,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires ImGuiBackendFlags_HasMouseCursors for better mouse cursor feedback.");
ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly);
ImGui::Checkbox("io.ConfigWindowsCopyContentsWithCtrlC", &io.ConfigWindowsCopyContentsWithCtrlC); // [EXPERIMENTAL]
ImGui::SameLine(); HelpMarker("*EXPERIMENTAL* CTRL+C copy the contents of focused window into the clipboard.\n\nExperimental because:\n- (1) has known issues with nested Begin/End pairs.\n- (2) text output quality varies.\n- (3) text output is in submission order rather than spatial order.");
ImGui::SameLine(); HelpMarker("*EXPERIMENTAL* Ctrl+C copy the contents of focused window into the clipboard.\n\nExperimental because:\n- (1) has known issues with nested Begin/End pairs.\n- (2) text output quality varies.\n- (3) text output is in submission order rather than spatial order.");
ImGui::Checkbox("io.ConfigScrollbarScrollByPage", &io.ConfigScrollbarScrollByPage);
ImGui::SameLine(); HelpMarker("Enable scrolling page by page when clicking outside the scrollbar grab.\nWhen disabled, always scroll to clicked location.\nWhen enabled, Shift+Click scrolls to clicked location.");
@@ -887,19 +887,20 @@ static void DemoWindowWidgetsBasic()
ImGui::SeparatorText("Inputs");
{
// To wire InputText() with std::string or any other custom string type,
// see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file.
// If you want to use InputText() with std::string or any custom dynamic string type:
// - For std::string: use the wrapper in misc/cpp/imgui_stdlib.h/.cpp
// - Otherwise, see the 'Dear ImGui Demo->Widgets->Text Input->Resize Callback' for using ImGuiInputTextFlags_CallbackResize.
IMGUI_DEMO_MARKER("Widgets/Basic/InputText");
static char str0[128] = "Hello, world!";
ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0));
ImGui::SameLine(); HelpMarker(
"USER:\n"
"Hold SHIFT or use mouse to select text.\n"
"CTRL+Left/Right to word jump.\n"
"CTRL+A or Double-Click to select all.\n"
"CTRL+X,CTRL+C,CTRL+V for clipboard.\n"
"CTRL+Z to undo, CTRL+Y/CTRL+SHIFT+Z to redo.\n"
"ESCAPE to revert.\n\n"
"Hold Shift or use mouse to select text.\n"
"Ctrl+Left/Right to word jump.\n"
"Ctrl+A or Double-Click to select all.\n"
"Ctrl+X,Ctrl+C,Ctrl+V for clipboard.\n"
"Ctrl+Z to undo, Ctrl+Y/Ctrl+Shift+Z to redo.\n"
"Escape to revert.\n\n"
"PROGRAMMER:\n"
"You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() "
"to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated "
@@ -936,8 +937,8 @@ static void DemoWindowWidgetsBasic()
ImGui::DragInt("drag int", &i1, 1);
ImGui::SameLine(); HelpMarker(
"Click and drag to edit value.\n"
"Hold SHIFT/ALT for faster/slower edit.\n"
"Double-click or CTRL+click to input value.");
"Hold Shift/Alt for faster/slower edit.\n"
"Double-Click or Ctrl+Click to input value.");
ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%", ImGuiSliderFlags_AlwaysClamp);
ImGui::DragInt("drag int wrap 100..200", &i3, 1, 100, 200, "%d", ImGuiSliderFlags_WrapAround);
@@ -953,7 +954,7 @@ static void DemoWindowWidgetsBasic()
IMGUI_DEMO_MARKER("Widgets/Basic/SliderInt, SliderFloat");
static int i1 = 0;
ImGui::SliderInt("slider int", &i1, -1, 3);
ImGui::SameLine(); HelpMarker("CTRL+click to input value.");
ImGui::SameLine(); HelpMarker("Ctrl+Click to input value.");
static float f1 = 0.123f, f2 = 0.0f;
ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f");
@@ -971,7 +972,7 @@ static void DemoWindowWidgetsBasic()
static int elem = Element_Fire;
const char* elems_names[Element_COUNT] = { "Fire", "Earth", "Air", "Water" };
const char* elem_name = (elem >= 0 && elem < Element_COUNT) ? elems_names[elem] : "Unknown";
ImGui::SliderInt("slider enum", &elem, 0, Element_COUNT - 1, elem_name); // Use ImGuiSliderFlags_NoInput flag to disable CTRL+Click here.
ImGui::SliderInt("slider enum", &elem, 0, Element_COUNT - 1, elem_name); // Use ImGuiSliderFlags_NoInput flag to disable Ctrl+Click here.
ImGui::SameLine(); HelpMarker("Using the format string parameter to display a name instead of the underlying integer.");
}
@@ -985,8 +986,8 @@ static void DemoWindowWidgetsBasic()
ImGui::SameLine(); HelpMarker(
"Click on the color square to open a color picker.\n"
"Click and hold to use drag and drop.\n"
"Right-click on the color square to show options.\n"
"CTRL+click on individual component to input value.\n");
"Right-Click on the color square to show options.\n"
"Ctrl+Click on individual component to input value.\n");
ImGui::ColorEdit4("color 2", col2);
}
@@ -1104,7 +1105,7 @@ static void DemoWindowWidgetsColorAndPickers()
ImGui::Text("Color widget:");
ImGui::SameLine(); HelpMarker(
"Click on the color square to open a color picker.\n"
"CTRL+click on individual component to input value.\n");
"Ctrl+Click on individual component to input value.\n");
ImGui::ColorEdit3("MyColor##1", (float*)&color, base_flags);
IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (HSV, with Alpha)");
@@ -1436,7 +1437,7 @@ static void DemoWindowWidgetsDataTypes()
ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp);
ImGui::SameLine(); HelpMarker(
"As with every widget in dear imgui, we never modify values unless there is a user interaction.\n"
"You can override the clamping limits by using CTRL+Click to input a value.");
"You can override the clamping limits by using Ctrl+Click to input a value.");
ImGui::DragScalar("drag s8", ImGuiDataType_S8, &s8_v, drag_speed, drag_clamp ? &s8_zero : NULL, drag_clamp ? &s8_fifty : NULL);
ImGui::DragScalar("drag u8", ImGuiDataType_U8, &u8_v, drag_speed, drag_clamp ? &u8_zero : NULL, drag_clamp ? &u8_fifty : NULL, "%u ms");
ImGui::DragScalar("drag s16", ImGuiDataType_S16, &s16_v, drag_speed, drag_clamp ? &s16_zero : NULL, drag_clamp ? &s16_fifty : NULL);
@@ -1696,7 +1697,7 @@ static void DemoWindowWidgetsDragsAndSliders()
static ImGuiSliderFlags flags = ImGuiSliderFlags_None;
ImGui::CheckboxFlags("ImGuiSliderFlags_AlwaysClamp", &flags, ImGuiSliderFlags_AlwaysClamp);
ImGui::CheckboxFlags("ImGuiSliderFlags_ClampOnInput", &flags, ImGuiSliderFlags_ClampOnInput);
ImGui::SameLine(); HelpMarker("Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds.");
ImGui::SameLine(); HelpMarker("Clamp value to min/max bounds when input manually with Ctrl+Click. By default Ctrl+Click allows going out of bounds.");
ImGui::CheckboxFlags("ImGuiSliderFlags_ClampZeroRange", &flags, ImGuiSliderFlags_ClampZeroRange);
ImGui::SameLine(); HelpMarker("Clamp even if min==max==0.0f. Otherwise DragXXX functions don't clamp.");
ImGui::CheckboxFlags("ImGuiSliderFlags_Logarithmic", &flags, ImGuiSliderFlags_Logarithmic);
@@ -1704,7 +1705,7 @@ static void DemoWindowWidgetsDragsAndSliders()
ImGui::CheckboxFlags("ImGuiSliderFlags_NoRoundToFormat", &flags, ImGuiSliderFlags_NoRoundToFormat);
ImGui::SameLine(); HelpMarker("Disable rounding underlying value to match precision of the format string (e.g. %.3f values are rounded to those 3 digits).");
ImGui::CheckboxFlags("ImGuiSliderFlags_NoInput", &flags, ImGuiSliderFlags_NoInput);
ImGui::SameLine(); HelpMarker("Disable CTRL+Click or Enter key allowing to input text directly into the widget.");
ImGui::SameLine(); HelpMarker("Disable Ctrl+Click or Enter key allowing to input text directly into the widget.");
ImGui::CheckboxFlags("ImGuiSliderFlags_NoSpeedTweaks", &flags, ImGuiSliderFlags_NoSpeedTweaks);
ImGui::SameLine(); HelpMarker("Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic.");
ImGui::CheckboxFlags("ImGuiSliderFlags_WrapAround", &flags, ImGuiSliderFlags_WrapAround);
@@ -2170,7 +2171,7 @@ static void DemoWindowWidgetsQueryingStatuses()
);
ImGui::BulletText(
"with Hovering Delay or Stationary test:\n"
"IsItemHovered() = = %d\n"
"IsItemHovered() = %d\n"
"IsItemHovered(_Stationary) = %d\n"
"IsItemHovered(_DelayShort) = %d\n"
"IsItemHovered(_DelayNormal) = %d\n"
@@ -2647,6 +2648,10 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d
{
HelpMarker("Selections can be built using Selectable(), TreeNode() or other widgets. Selection state is owned by application code/data.");
ImGui::BulletText("Wiki page:");
ImGui::SameLine();
ImGui::TextLinkOpenURL("imgui/wiki/Multi-Select", "https://github.com/ocornut/imgui/wiki/Multi-Select");
// Without any fancy API: manage single-selection yourself.
IMGUI_DEMO_MARKER("Widgets/Selection State/Single-Select");
if (ImGui::TreeNode("Single-Select"))
@@ -2663,11 +2668,11 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d
}
// Demonstrate implementation a most-basic form of multi-selection manually
// This doesn't support the SHIFT modifier which requires BeginMultiSelect()!
// This doesn't support the Shift modifier which requires BeginMultiSelect()!
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (manual/simplified, without BeginMultiSelect)");
if (ImGui::TreeNode("Multi-Select (manual/simplified, without BeginMultiSelect)"))
{
HelpMarker("Hold CTRL and click to select multiple items.");
HelpMarker("Hold Ctrl and Click to select multiple items.");
static bool selection[5] = { false, false, false, false, false };
for (int n = 0; n < 5; n++)
{
@@ -2675,7 +2680,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d
sprintf(buf, "Object %d", n);
if (ImGui::Selectable(buf, selection[n]))
{
if (!ImGui::GetIO().KeyCtrl) // Clear selection when CTRL is not held
if (!ImGui::GetIO().KeyCtrl) // Clear selection when Ctrl is not held
memset(selection, 0, sizeof(selection));
selection[n] ^= 1; // Toggle current item
}
@@ -2684,7 +2689,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d
}
// Demonstrate handling proper multi-selection using the BeginMultiSelect/EndMultiSelect API.
// SHIFT+Click w/ CTRL and other standard features are supported.
// Shift+Click w/ Ctrl and other standard features are supported.
// We use the ImGuiSelectionBasicStorage helper which you may freely reimplement.
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select");
if (ImGui::TreeNode("Multi-Select"))
@@ -2693,7 +2698,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d
ImGui::BulletText("Keyboard navigation (arrows, page up/down, home/end, space).");
ImGui::BulletText("Ctrl modifier to preserve and toggle selection.");
ImGui::BulletText("Shift modifier for range selection.");
ImGui::BulletText("CTRL+A to select all.");
ImGui::BulletText("Ctrl+A to select all.");
ImGui::BulletText("Escape to clear selection.");
ImGui::BulletText("Click and drag to box-select.");
ImGui::Text("Tip: Use 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen.");
@@ -3180,6 +3185,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect);
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear);
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClearOnReselect", &flags, ImGuiMultiSelectFlags_NoAutoClearOnReselect);
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectOnRightClick", &flags, ImGuiMultiSelectFlags_NoSelectOnRightClick);
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect1d", &flags, ImGuiMultiSelectFlags_BoxSelect1d);
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d);
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelectNoScroll", &flags, ImGuiMultiSelectFlags_BoxSelectNoScroll);
@@ -3229,7 +3235,7 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d
ImGui::BeginTable("##Split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.70f);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.30f);
//ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacingY, 0.0f);
//ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, 0.0f);
}
ImGuiListClipper clipper;
@@ -3379,6 +3385,18 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d
// [SECTION] DemoWindowWidgetsTabs()
//-----------------------------------------------------------------------------
static void EditTabBarFittingPolicyFlags(ImGuiTabBarFlags* p_flags)
{
if ((*p_flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0)
*p_flags |= ImGuiTabBarFlags_FittingPolicyDefault_;
if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyMixed", p_flags, ImGuiTabBarFlags_FittingPolicyMixed))
*p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyMixed);
if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyShrink", p_flags, ImGuiTabBarFlags_FittingPolicyShrink))
*p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyShrink);
if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", p_flags, ImGuiTabBarFlags_FittingPolicyScroll))
*p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll);
}
static void DemoWindowWidgetsTabs()
{
IMGUI_DEMO_MARKER("Widgets/Tabs");
@@ -3421,12 +3439,7 @@ static void DemoWindowWidgetsTabs()
ImGui::CheckboxFlags("ImGuiTabBarFlags_TabListPopupButton", &tab_bar_flags, ImGuiTabBarFlags_TabListPopupButton);
ImGui::CheckboxFlags("ImGuiTabBarFlags_NoCloseWithMiddleMouseButton", &tab_bar_flags, ImGuiTabBarFlags_NoCloseWithMiddleMouseButton);
ImGui::CheckboxFlags("ImGuiTabBarFlags_DrawSelectedOverline", &tab_bar_flags, ImGuiTabBarFlags_DrawSelectedOverline);
if ((tab_bar_flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0)
tab_bar_flags |= ImGuiTabBarFlags_FittingPolicyDefault_;
if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyResizeDown", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyResizeDown))
tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyResizeDown);
if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyScroll))
tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll);
EditTabBarFittingPolicyFlags(&tab_bar_flags);
// Tab Bar
ImGui::AlignTextToFramePadding();
@@ -3475,12 +3488,8 @@ static void DemoWindowWidgetsTabs()
ImGui::Checkbox("Show Trailing TabItemButton()", &show_trailing_button);
// Expose some other flags which are useful to showcase how they interact with Leading/Trailing tabs
static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyResizeDown;
ImGui::CheckboxFlags("ImGuiTabBarFlags_TabListPopupButton", &tab_bar_flags, ImGuiTabBarFlags_TabListPopupButton);
if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyResizeDown", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyResizeDown))
tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyResizeDown);
if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyScroll))
tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll);
static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyShrink;
EditTabBarFittingPolicyFlags(&tab_bar_flags);
if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags))
{
@@ -3688,8 +3697,10 @@ static void DemoWindowWidgetsTextInput()
IMGUI_DEMO_MARKER("Widgets/Text Input/Multi-line Text Input");
if (ImGui::TreeNode("Multi-line Text Input"))
{
// Note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize
// and the code in misc/cpp/imgui_stdlib.h for how to setup InputText() for dynamically resizing strings.
// WE ARE USING A FIXED-SIZE BUFFER FOR SIMPLICITY HERE.
// If you want to use InputText() with std::string or any custom dynamic string type:
// - For std::string: use the wrapper in misc/cpp/imgui_stdlib.h/.cpp
// - Otherwise, see the 'Dear ImGui Demo->Widgets->Text Input->Resize Callback' for using ImGuiInputTextFlags_CallbackResize.
static char text[1024 * 16] =
"/*\n"
" The Pentium F00F bug, shorthand for F0 0F C7 C8,\n"
@@ -3705,6 +3716,8 @@ static void DemoWindowWidgetsTextInput()
static ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput;
HelpMarker("You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example. (This is not demonstrated in imgui_demo.cpp because we don't want to include <string> in here)");
ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly);
ImGui::CheckboxFlags("ImGuiInputTextFlags_WordWrap", &flags, ImGuiInputTextFlags_WordWrap);
ImGui::SameLine(); HelpMarker("Feature is currently in Beta. Please read comments in imgui.h");
ImGui::CheckboxFlags("ImGuiInputTextFlags_AllowTabInput", &flags, ImGuiInputTextFlags_AllowTabInput);
ImGui::SameLine(); HelpMarker("When _AllowTabInput is set, passing through the widget with Tabbing doesn't automatically activate it, in order to also cycling through subsequent widgets.");
ImGui::CheckboxFlags("ImGuiInputTextFlags_CtrlEnterForNewLine", &flags, ImGuiInputTextFlags_CtrlEnterForNewLine);
@@ -3852,10 +3865,13 @@ static void DemoWindowWidgetsTextInput()
// For this demo we are using ImVector as a string container.
// Note that because we need to store a terminating zero character, our size/capacity are 1 more
// than usually reported by a typical string class.
static ImGuiInputTextFlags flags = ImGuiInputTextFlags_None;
ImGui::CheckboxFlags("ImGuiInputTextFlags_WordWrap", &flags, ImGuiInputTextFlags_WordWrap);
static ImVector<char> my_str;
if (my_str.empty())
my_str.push_back(0);
Funcs::MyInputTextMultiline("##MyStr", &my_str, ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16));
Funcs::MyInputTextMultiline("##MyStr", &my_str, ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags);
ImGui::Text("Data: %p\nSize: %d\nCapacity: %d", (void*)my_str.begin(), my_str.size(), my_str.capacity());
ImGui::TreePop();
}
@@ -4056,7 +4072,7 @@ static void DemoWindowWidgetsTreeNodes()
{
HelpMarker(
"This is a more typical looking tree with selectable nodes.\n"
"Click to select, CTRL+Click to toggle, click on arrows or double-click to open.");
"Click to select, Ctrl+Click to toggle, click on arrows or double-click to open.");
static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth;
static bool align_label_with_current_x_position = false;
static bool test_drag_and_drop = false;
@@ -4143,7 +4159,7 @@ static void DemoWindowWidgetsTreeNodes()
// Update selection state
// (process outside of tree loop to avoid visual inconsistencies during the clicking frame)
if (ImGui::GetIO().KeyCtrl)
selection_mask ^= (1 << node_clicked); // CTRL+click to toggle
selection_mask ^= (1 << node_clicked); // Ctrl+Click to toggle
else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, may want to preserve selection when clicking on item that is part of the selection
selection_mask = (1 << node_clicked); // Click to single-select
}
@@ -4787,15 +4803,18 @@ static void DemoWindowLayout()
ImGui::Checkbox("Decoration", &enable_extra_decorations);
ImGui::PushItemWidth(ImGui::GetFontSize() * 10);
enable_track |= ImGui::DragInt("##item", &track_item, 0.25f, 0, 99, "Item = %d");
ImGui::SameLine();
ImGui::Checkbox("Track", &enable_track);
ImGui::PushItemWidth(100);
ImGui::SameLine(140); enable_track |= ImGui::DragInt("##item", &track_item, 0.25f, 0, 99, "Item = %d");
bool scroll_to_off = ImGui::Button("Scroll Offset");
ImGui::SameLine(140); scroll_to_off |= ImGui::DragFloat("##off", &scroll_to_off_px, 1.00f, 0, FLT_MAX, "+%.0f px");
bool scroll_to_off = ImGui::DragFloat("##off", &scroll_to_off_px, 1.00f, 0, FLT_MAX, "+%.0f px");
ImGui::SameLine();
scroll_to_off |= ImGui::Button("Scroll Offset");
bool scroll_to_pos = ImGui::Button("Scroll To Pos");
ImGui::SameLine(140); scroll_to_pos |= ImGui::DragFloat("##pos", &scroll_to_pos_px, 1.00f, -10, FLT_MAX, "X/Y = %.0f px");
bool scroll_to_pos = ImGui::DragFloat("##pos", &scroll_to_pos_px, 1.00f, -10, FLT_MAX, "X/Y = %.0f px");
ImGui::SameLine();
scroll_to_pos |= ImGui::Button("Scroll To Pos");
ImGui::PopItemWidth();
if (scroll_to_off || scroll_to_pos)
@@ -5179,7 +5198,7 @@ static void DemoWindowPopups()
// Typical use for regular windows:
// bool my_tool_is_active = false; if (ImGui::Button("Open")) my_tool_is_active = true; [...] if (my_tool_is_active) Begin("My Tool", &my_tool_is_active) { [...] } End();
// Typical use for popups:
// if (ImGui::Button("Open")) ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup") { [...] EndPopup(); }
// if (ImGui::Button("Open")) ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup")) { [...] EndPopup(); }
// With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state.
// This may be a bit confusing at first but it should quickly make sense. Follow on the examples below.
@@ -5304,7 +5323,7 @@ static void DemoWindowPopups()
if (ImGui::BeginPopupContextItem()) // <-- use last item id as popup id
{
selected = n;
ImGui::Text("This a popup for \"%s\"!", names[n]);
ImGui::Text("This is a popup for \"%s\"!", names[n]);
if (ImGui::Button("Close"))
ImGui::CloseCurrentPopup();
ImGui::EndPopup();
@@ -5447,7 +5466,7 @@ static void DemoWindowPopups()
ImGui::TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!");
ImGui::Separator();
ImGui::MenuItem("Menu item", "CTRL+M");
ImGui::MenuItem("Menu item", "Ctrl+M");
if (ImGui::BeginMenu("Menu inside a regular window"))
{
ShowExampleMenuFile();
@@ -5765,7 +5784,7 @@ static void DemoWindowTables()
ImGui::SameLine(); ImGui::RadioButton("Text", &contents_type, CT_Text);
ImGui::SameLine(); ImGui::RadioButton("FillButton", &contents_type, CT_FillButton);
ImGui::Checkbox("Display headers", &display_headers);
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers");
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers)");
PopStyleCompact();
if (ImGui::BeginTable("table1", 3, flags))
@@ -7012,7 +7031,7 @@ static void DemoWindowTables()
// [2.3] Right-click in columns to open another custom popup
HelpMarker(
"Demonstrate mixing table context menu (over header), item context button (over button) "
"and custom per-colunm context menu (over column body).");
"and custom per-column context menu (over column body).");
ImGuiTableFlags flags2 = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders;
if (ImGui::BeginTable("table_context_menu_2", COLUMNS_COUNT, flags2))
{
@@ -7258,7 +7277,7 @@ static void DemoWindowTables()
ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH);
ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterH", &flags, ImGuiTableFlags_BordersOuterH);
ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerH", &flags, ImGuiTableFlags_BordersInnerH);
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers");
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers)");
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)");
ImGui::TreePop();
}
@@ -7878,16 +7897,16 @@ static void DemoWindowInputs()
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 0.0f, 1.0f, 0.1f));
ImGui::BeginChild("WindowA", ImVec2(-FLT_MIN, line_height * 14), true);
ImGui::Text("Press CTRL+A and see who receives it!");
ImGui::Text("Press Ctrl+A and see who receives it!");
ImGui::Separator();
// 1: Window polling for CTRL+A
// 1: Window polling for Ctrl+A
ImGui::Text("(in WindowA)");
ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
// 2: InputText also polling for CTRL+A: it always uses _RouteFocused internally (gets priority when active)
// 2: InputText also polling for Ctrl+A: it always uses _RouteFocused internally (gets priority when active)
// (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h)
//char str[16] = "Press CTRL+A";
//char str[16] = "Press Ctrl+A";
//ImGui::Spacing();
//ImGui::InputText("InputTextB", str, IM_ARRAYSIZE(str), ImGuiInputTextFlags_ReadOnly);
//ImGuiID item_id = ImGui::GetItemID();
@@ -7900,7 +7919,7 @@ static void DemoWindowInputs()
ImGui::Text("IsWindowFocused: %d", ImGui::IsWindowFocused());
ImGui::EndChild();
// 4: Child window polling for CTRL+A. It is deeper than WindowA and gets priority when focused.
// 4: Child window polling for Ctrl+A. It is deeper than WindowA and gets priority when focused.
ImGui::BeginChild("ChildE", ImVec2(-FLT_MIN, line_height * 4), true);
ImGui::Text("(in ChildE: using same Shortcut)");
ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
@@ -7957,7 +7976,7 @@ static void DemoWindowInputs()
IMGUI_DEMO_MARKER("Inputs & Focus/Tabbing");
if (ImGui::TreeNode("Tabbing"))
{
ImGui::Text("Use TAB/SHIFT+TAB to cycle through keyboard editable fields.");
ImGui::Text("Use Tab/Shift+Tab to cycle through keyboard editable fields.");
static char buf[32] = "hello";
ImGui::InputText("1", buf, IM_ARRAYSIZE(buf));
ImGui::InputText("2", buf, IM_ARRAYSIZE(buf));
@@ -8096,6 +8115,9 @@ void ImGui::ShowAboutWindow(bool* p_open)
ImGui::Separator();
ImGui::Text("sizeof(size_t): %d, sizeof(ImDrawIdx): %d, sizeof(ImDrawVert): %d", (int)sizeof(size_t), (int)sizeof(ImDrawIdx), (int)sizeof(ImDrawVert));
ImGui::Text("define: __cplusplus=%d", (int)__cplusplus);
#ifdef IMGUI_ENABLE_TEST_ENGINE
ImGui::Text("define: IMGUI_ENABLE_TEST_ENGINE");
#endif
#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_FUNCTIONS");
#endif
@@ -8163,6 +8185,25 @@ void ImGui::ShowAboutWindow(bool* p_open)
ImGui::Text("define: __EMSCRIPTEN__");
ImGui::Text("Emscripten: %d.%d.%d", __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__);
#endif
#ifdef NDEBUG
ImGui::Text("define: NDEBUG");
#endif
// Heuristic to detect no-op IM_ASSERT() macros
// - This is designed so people opening bug reports would convey and notice that they have disabled asserts for Dear ImGui code.
// - 16 is > strlen("((void)(_EXPR))") which we suggested in our imconfig.h template as a possible way to disable.
int assert_runs_expression = 0;
IM_ASSERT(++assert_runs_expression);
int assert_expand_len = (int)strlen(IM_STRINGIFY((IM_ASSERT(true))));
bool assert_maybe_disabled = (!assert_runs_expression || assert_expand_len <= 16);
ImGui::Text("IM_ASSERT: runs expression: %s. expand size: %s%s",
assert_runs_expression ? "OK" : "KO", (assert_expand_len > 16) ? "OK" : "KO", assert_maybe_disabled ? " (MAYBE DISABLED?!)" : "");
if (assert_maybe_disabled)
{
ImGui::SameLine();
HelpMarker("IM_ASSERT() calls assert() by default. Compiling with NDEBUG will usually strip out assert() to nothing, which is NOT recommended because we use asserts to notify of programmer mistakes!");
}
ImGui::Separator();
ImGui::Text("io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL");
ImGui::Text("io.BackendRendererName: %s", io.BackendRendererName ? io.BackendRendererName : "NULL");
@@ -8218,22 +8259,34 @@ void ImGui::ShowAboutWindow(bool* p_open)
//-----------------------------------------------------------------------------
// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options.
// Here we use the simplified Combo() api that packs items into a single literal string.
// Useful for quick combo boxes where the choices are known locally.
bool ImGui::ShowStyleSelector(const char* label)
{
// FIXME: This is a bit tricky to get right as style are functions, they don't register a name nor the fact that one is active.
// So we keep track of last active one among our limited selection.
static int style_idx = -1;
if (ImGui::Combo(label, &style_idx, "Dark\0Light\0Classic\0"))
const char* style_names[] = { "Dark", "Light", "Classic" };
bool ret = false;
if (ImGui::BeginCombo(label, (style_idx >= 0 && style_idx < IM_ARRAYSIZE(style_names)) ? style_names[style_idx] : ""))
{
switch (style_idx)
for (int n = 0; n < IM_ARRAYSIZE(style_names); n++)
{
case 0: ImGui::StyleColorsDark(); break;
case 1: ImGui::StyleColorsLight(); break;
case 2: ImGui::StyleColorsClassic(); break;
if (ImGui::Selectable(style_names[n], style_idx == n, ImGuiSelectableFlags_SelectOnNav))
{
style_idx = n;
ret = true;
switch (style_idx)
{
case 0: ImGui::StyleColorsDark(); break;
case 1: ImGui::StyleColorsLight(); break;
case 2: ImGui::StyleColorsClassic(); break;
}
}
else if (style_idx == n)
ImGui::SetItemDefaultFocus();
}
return true;
ImGui::EndCombo();
}
return false;
return ret;
}
static const char* GetTreeLinesFlagsName(ImGuiTreeNodeFlags flags)
@@ -8319,7 +8372,6 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f");
SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f");
SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f");
SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f");
SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f");
SeparatorText("Borders");
@@ -8333,16 +8385,22 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 12.0f, "%.0f");
SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f");
SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f, "%.0f");
SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f");
SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f");
SeparatorText("Scrollbar");
SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f");
SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f");
SliderFloat("ScrollbarPadding", &style.ScrollbarPadding, 0.0f, 10.0f, "%.0f");
SeparatorText("Tabs");
SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f");
SliderFloat("TabBarBorderSize", &style.TabBarBorderSize, 0.0f, 2.0f, "%.0f");
SliderFloat("TabBarOverlineSize", &style.TabBarOverlineSize, 0.0f, 3.0f, "%.0f");
SameLine(); HelpMarker("Overline is only drawn over the selected tab when ImGuiTabBarFlags_DrawSelectedOverline is set.");
DragFloat("TabCloseButtonMinWidthSelected", &style.TabCloseButtonMinWidthSelected, 0.1f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthSelected < 0.0f) ? "%.0f (Always)" : "%.0f");
DragFloat("TabCloseButtonMinWidthUnselected", &style.TabCloseButtonMinWidthUnselected, 0.1f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthUnselected < 0.0f) ? "%.0f (Always)" : "%.0f");
DragFloat("TabMinWidthBase", &style.TabMinWidthBase, 0.5f, 1.0f, 500.0f, "%.0f");
DragFloat("TabMinWidthShrink", &style.TabMinWidthShrink, 0.5f, 1.0f, 500.0f, "%0.f");
DragFloat("TabCloseButtonMinWidthSelected", &style.TabCloseButtonMinWidthSelected, 0.5f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthSelected < 0.0f) ? "%.0f (Always)" : "%.0f");
DragFloat("TabCloseButtonMinWidthUnselected", &style.TabCloseButtonMinWidthUnselected, 0.5f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthUnselected < 0.0f) ? "%.0f (Always)" : "%.0f");
SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f");
SeparatorText("Tables");
@@ -8481,7 +8539,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
ShowFontAtlas(atlas);
// Post-baking font scaling. Note that this is NOT the nice way of scaling fonts, read below.
// (we enforce hard clamping manually as by default DragFloat/SliderFloat allows CTRL+Click text to get out of bounds).
// (we enforce hard clamping manually as by default DragFloat/SliderFloat allows Ctrl+Click text to get out of bounds).
/*
SeparatorText("Legacy Scaling");
const float MIN_SCALE = 0.3f;
@@ -8583,27 +8641,27 @@ void ImGui::ShowUserGuide()
ImGuiIO& io = GetIO();
BulletText("Double-click on title bar to collapse window.");
BulletText(
"Click and drag on lower corner to resize window\n"
"(double-click to auto fit window to its contents).");
BulletText("CTRL+Click on a slider or drag box to input value as text.");
BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields.");
BulletText("CTRL+Tab to select a window.");
"Click and drag on lower corner or border to resize window.\n"
"(double-click to auto fit window to its contents)");
BulletText("Ctrl+Click on a slider or drag box to input value as text.");
BulletText("Tab/Shift+Tab to cycle through keyboard editable fields.");
BulletText("Ctrl+Tab/Ctrl+Shift+Tab to focus windows.");
if (io.FontAllowUserScaling)
BulletText("CTRL+Mouse Wheel to zoom window contents.");
BulletText("Ctrl+Mouse Wheel to zoom window contents.");
BulletText("While inputting text:\n");
Indent();
BulletText("CTRL+Left/Right to word jump.");
BulletText("CTRL+A or double-click to select all.");
BulletText("CTRL+X/C/V to use clipboard cut/copy/paste.");
BulletText("CTRL+Z to undo, CTRL+Y/CTRL+SHIFT+Z to redo.");
BulletText("ESCAPE to revert.");
BulletText("Ctrl+Left/Right to word jump.");
BulletText("Ctrl+A or double-click to select all.");
BulletText("Ctrl+X/C/V to use clipboard cut/copy/paste.");
BulletText("Ctrl+Z to undo, Ctrl+Y/Ctrl+Shift+Z to redo.");
BulletText("Escape to revert.");
Unindent();
BulletText("With keyboard navigation enabled:");
Indent();
BulletText("Arrow keys to navigate.");
BulletText("Arrow keys or Home/End/PageUp/PageDown to navigate.");
BulletText("Space to activate a widget.");
BulletText("Return to input text into a widget.");
BulletText("Escape to deactivate a widget, close popup, exit child window.");
BulletText("Escape to deactivate a widget, close popup,\nexit a child window or the menu layer, clear focus.");
BulletText("Alt to jump to the menu layer of a window.");
Unindent();
}
@@ -8630,12 +8688,12 @@ static void ShowExampleAppMainMenuBar()
}
if (ImGui::BeginMenu("Edit"))
{
if (ImGui::MenuItem("Undo", "CTRL+Z")) {}
if (ImGui::MenuItem("Redo", "CTRL+Y", false, false)) {} // Disabled item
if (ImGui::MenuItem("Undo", "Ctrl+Z")) {}
if (ImGui::MenuItem("Redo", "Ctrl+Y", false, false)) {} // Disabled item
ImGui::Separator();
if (ImGui::MenuItem("Cut", "CTRL+X")) {}
if (ImGui::MenuItem("Copy", "CTRL+C")) {}
if (ImGui::MenuItem("Paste", "CTRL+V")) {}
if (ImGui::MenuItem("Cut", "Ctrl+X")) {}
if (ImGui::MenuItem("Copy", "Ctrl+C")) {}
if (ImGui::MenuItem("Paste", "Ctrl+V")) {}
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
@@ -9277,10 +9335,9 @@ static void ShowExampleAppLayout(bool* p_open)
ImGui::BeginChild("left pane", ImVec2(150, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX);
for (int i = 0; i < 100; i++)
{
// FIXME: Good candidate to use ImGuiSelectableFlags_SelectOnNav
char label[128];
sprintf(label, "MyObject %d", i);
if (ImGui::Selectable(label, selected == i))
if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SelectOnNav))
selected = i;
}
ImGui::EndChild();
@@ -9632,7 +9689,7 @@ static void ShowExampleAppConstrainedResize(bool* p_open)
IMGUI_DEMO_MARKER("Examples/Constrained Resizing window");
if (ImGui::GetIO().KeyShift)
{
// Display a dummy viewport (in your real app you would likely use ImageButton() to display a texture.
// Display a dummy viewport (in your real app you would likely use ImageButton() to display a texture)
ImVec2 avail_size = ImGui::GetContentRegionAvail();
ImVec2 pos = ImGui::GetCursorScreenPos();
ImGui::ColorButton("viewport", ImVec4(0.5f, 0.2f, 0.5f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, avail_size);
@@ -9641,7 +9698,7 @@ static void ShowExampleAppConstrainedResize(bool* p_open)
}
else
{
ImGui::Text("(Hold SHIFT to display a dummy viewport)");
ImGui::Text("(Hold Shift to display a dummy viewport)");
if (ImGui::Button("Set 200x200")) { ImGui::SetWindowSize(ImVec2(200, 200)); } ImGui::SameLine();
if (ImGui::Button("Set 500x500")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine();
if (ImGui::Button("Set 800x200")) { ImGui::SetWindowSize(ImVec2(800, 200)); }
@@ -10578,7 +10635,7 @@ struct ExampleAssetsBrowser
ImGui::SeparatorText("Layout");
ImGui::SliderFloat("Icon Size", &IconSize, 16.0f, 128.0f, "%.0f");
ImGui::SameLine(); HelpMarker("Use CTRL+Wheel to zoom");
ImGui::SameLine(); HelpMarker("Use Ctrl+Wheel to zoom");
ImGui::SliderInt("Icon Spacing", &IconSpacing, 0, 32);
ImGui::SliderInt("Icon Hit Spacing", &IconHitSpacing, 0, 32);
ImGui::Checkbox("Stretch Spacing", &StretchSpacing);
@@ -10770,7 +10827,7 @@ struct ExampleAssetsBrowser
if (want_delete)
Selection.ApplyDeletionPostLoop(ms_io, Items, item_curr_idx_to_focus);
// Zooming with CTRL+Wheel
// Zooming with Ctrl+Wheel
if (ImGui::IsWindowAppearing())
ZoomWheelAccum = 0.0f;
if (ImGui::IsWindowHovered() && io.MouseWheel != 0.0f && ImGui::IsKeyDown(ImGuiMod_Ctrl) && ImGui::IsAnyItemActive() == false)
Regular → Executable
+165 -120
View File
@@ -1,4 +1,4 @@
// dear imgui, v1.92.1
// dear imgui, v1.92.5
// (drawing and font code)
/*
@@ -48,7 +48,7 @@ Index of this file:
#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer)
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
#endif
// Clang/GCC warnings with -Weverything
@@ -240,6 +240,8 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
colors[ImGuiCol_TreeLines] = colors[ImGuiCol_Border];
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_DragDropTargetBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_UnsavedMarker] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
colors[ImGuiCol_NavCursor] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
@@ -305,6 +307,8 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst)
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
colors[ImGuiCol_TreeLines] = colors[ImGuiCol_Border];
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_DragDropTargetBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_UnsavedMarker] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
colors[ImGuiCol_NavCursor] = colors[ImGuiCol_HeaderHovered];
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
@@ -371,6 +375,8 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
colors[ImGuiCol_TreeLines] = colors[ImGuiCol_Border];
colors[ImGuiCol_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
colors[ImGuiCol_DragDropTargetBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_UnsavedMarker] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
colors[ImGuiCol_NavCursor] = colors[ImGuiCol_HeaderHovered];
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(0.70f, 0.70f, 0.70f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.20f);
@@ -478,9 +484,10 @@ void ImDrawList::_ClearFreeMemory()
_Splitter.ClearFreeMemory();
}
// Note: For multi-threaded rendering, consider using `imgui_threaded_rendering` from https://github.com/ocornut/imgui_club
ImDrawList* ImDrawList::CloneOutput() const
{
ImDrawList* dst = IM_NEW(ImDrawList(_Data));
ImDrawList* dst = IM_NEW(ImDrawList(NULL));
dst->CmdBuffer = CmdBuffer;
dst->IdxBuffer = IdxBuffer;
dst->VtxBuffer = VtxBuffer;
@@ -517,6 +524,7 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* userdata, size_t use
{
IM_ASSERT_PARANOID(CmdBuffer.Size > 0);
ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
IM_ASSERT(callback != NULL);
IM_ASSERT(curr_cmd->UserCallback == NULL);
if (curr_cmd->ElemCount != 0)
{
@@ -819,7 +827,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTex) && (integer_thickness < IM_DRAWLIST_TEX_LINES_WIDTH_MAX) && (fractional_thickness <= 0.00001f) && (AA_SIZE == 1.0f);
// We should never hit this, because NewFrame() doesn't set ImDrawListFlags_AntiAliasedLinesUseTex unless ImFontAtlasFlags_NoBakedLines is off
IM_ASSERT_PARANOID(!use_texture || !(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines));
IM_ASSERT_PARANOID(!use_texture || !(_Data->Font->OwnerAtlas->Flags & ImFontAtlasFlags_NoBakedLines));
const int idx_count = use_texture ? (count * 6) : (thick_line ? count * 18 : count * 12);
const int vtx_count = use_texture ? (points_count * 2) : (thick_line ? points_count * 4 : points_count * 3);
@@ -1717,7 +1725,7 @@ void ImDrawList::AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32
clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z);
clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w);
}
font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end, wrap_width, cpu_fine_clip_rect != NULL);
font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end, wrap_width, (cpu_fine_clip_rect != NULL) ? ImDrawTextFlags_CpuFineClip : ImDrawTextFlags_None);
}
void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end)
@@ -2312,17 +2320,16 @@ void ImDrawData::DeIndexAllBuffers()
{
ImVector<ImDrawVert> new_vtx_buffer;
TotalVtxCount = TotalIdxCount = 0;
for (int i = 0; i < CmdListsCount; i++)
for (ImDrawList* draw_list : CmdLists)
{
ImDrawList* cmd_list = CmdLists[i];
if (cmd_list->IdxBuffer.empty())
if (draw_list->IdxBuffer.empty())
continue;
new_vtx_buffer.resize(cmd_list->IdxBuffer.Size);
for (int j = 0; j < cmd_list->IdxBuffer.Size; j++)
new_vtx_buffer[j] = cmd_list->VtxBuffer[cmd_list->IdxBuffer[j]];
cmd_list->VtxBuffer.swap(new_vtx_buffer);
cmd_list->IdxBuffer.resize(0);
TotalVtxCount += cmd_list->VtxBuffer.Size;
new_vtx_buffer.resize(draw_list->IdxBuffer.Size);
for (int j = 0; j < draw_list->IdxBuffer.Size; j++)
new_vtx_buffer[j] = draw_list->VtxBuffer[draw_list->IdxBuffer[j]];
draw_list->VtxBuffer.swap(new_vtx_buffer);
draw_list->IdxBuffer.resize(0);
TotalVtxCount += draw_list->VtxBuffer.Size;
}
}
@@ -2457,8 +2464,10 @@ const char* ImTextureDataGetFormatName(ImTextureFormat format)
void ImTextureData::Create(ImTextureFormat format, int w, int h)
{
IM_ASSERT(Status == ImTextureStatus_Destroyed);
DestroyPixels();
Format = format;
Status = ImTextureStatus_WantCreate;
Width = w;
Height = h;
BytesPerPixel = ImTextureDataGetFormatBytesPerPixel(format);
@@ -2630,6 +2639,7 @@ ImFontAtlas::ImFontAtlas()
TexMinHeight = 128;
TexMaxWidth = 8192;
TexMaxHeight = 8192;
TexRef._TexID = ImTextureID_Invalid;
RendererHasTextures = false; // Assumed false by default, as apps can call e.g Atlas::Build() after backend init and before ImGui can update.
TexNextUniqueID = 1;
FontNextUniqueID = 1;
@@ -2646,6 +2656,7 @@ ImFontAtlas::~ImFontAtlas()
TexData = NULL;
}
// If you call this mid-frame, you would need to add new font and bind them!
void ImFontAtlas::Clear()
{
bool backup_renderer_has_textures = RendererHasTextures;
@@ -2696,6 +2707,8 @@ void ImFontAtlas::ClearFonts()
{
// FIXME-NEWATLAS: Illegal to remove currently bound font.
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
for (ImFont* font : Fonts)
ImFontAtlasBuildNotifySetFont(this, font, NULL);
ImFontAtlasBuildDestroy(this);
ClearInputData();
Fonts.clear_delete();
@@ -2739,10 +2752,9 @@ void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool rendere
if (atlas->Builder == NULL) // This will only happen if fonts were not already loaded.
ImFontAtlasBuildMain(atlas);
}
else // Legacy backend
{
// Legacy backend
if (!atlas->RendererHasTextures)
IM_ASSERT_USER_ERROR(atlas->TexIsBuilt, "Backend does not support ImGuiBackendFlags_RendererHasTextures, and font atlas is not built! Update backend OR make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8().");
}
if (atlas->TexIsBuilt && atlas->Builder->PreloadedAllGlyphsRanges)
IM_ASSERT_USER_ERROR(atlas->RendererHasTextures == false, "Called ImFontAtlas::Build() before ImGuiBackendFlags_RendererHasTextures got set! With new backends: you don't need to call Build().");
@@ -2787,20 +2799,29 @@ void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool rendere
if (tex->Status == ImTextureStatus_WantCreate && atlas->RendererHasTextures)
IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture's TexID/BackendUserData but did not update Status to OK.");
// Request destroy
// - Keep bool to true in order to differentiate a planned destroy vs a destroy decided by the backend.
// - We don't destroy pixels right away, as backend may have an in-flight copy from RAM.
if (tex->WantDestroyNextFrame && tex->Status != ImTextureStatus_Destroyed && tex->Status != ImTextureStatus_WantDestroy)
{
IM_ASSERT(tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates);
tex->Status = ImTextureStatus_WantDestroy;
}
// If a texture has never reached the backend, they don't need to know about it.
// (note: backends between 1.92.0 and 1.92.4 could set an already destroyed texture to ImTextureStatus_WantDestroy
// when invalidating graphics objects twice, which would previously remove it from the list and crash.)
if (tex->Status == ImTextureStatus_WantDestroy && tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL)
tex->Status = ImTextureStatus_Destroyed;
// Process texture being destroyed
if (tex->Status == ImTextureStatus_Destroyed)
{
IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture Status to Destroyed but did not clear TexID/BackendUserData!");
if (tex->WantDestroyNextFrame)
remove_from_list = true; // Destroy was scheduled by us
else
tex->Status = ImTextureStatus_WantCreate; // Destroy was done was backend (e.g. freed resources mid-run)
}
else if (tex->WantDestroyNextFrame && tex->Status != ImTextureStatus_WantDestroy)
{
// Request destroy. Keep bool as it allows us to keep track of things.
// We don't destroy pixels right away, as backend may have an in-flight copy from RAM.
IM_ASSERT(tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates);
tex->Status = ImTextureStatus_WantDestroy;
tex->Status = ImTextureStatus_WantCreate; // Destroy was done was backend: recreate it (e.g. freed resources mid-run)
}
// The backend may need defer destroying by a few frames, to handle texture used by previous in-flight rendering.
@@ -2808,13 +2829,10 @@ void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool rendere
if (tex->Status == ImTextureStatus_WantDestroy)
tex->UnusedFrames++;
// If a texture has never reached the backend, they don't need to know about it.
if (tex->Status == ImTextureStatus_WantDestroy && tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL)
remove_from_list = true;
// Destroy and remove
if (remove_from_list)
{
IM_ASSERT(atlas->TexData != tex);
tex->DestroyPixels();
IM_DELETE(tex);
atlas->TexList.erase(atlas->TexList.begin() + tex_n);
@@ -2954,7 +2972,7 @@ void ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex,
tex->UsedRect.h = (unsigned short)(ImMax(tex->UsedRect.y + tex->UsedRect.h, req.y + req.h) - tex->UsedRect.y);
atlas->TexIsBuilt = false;
// No need to queue if status is _WantCreate
// No need to queue if status is == ImTextureStatus_WantCreate
if (tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantUpdates)
{
tex->Status = ImTextureStatus_WantUpdates;
@@ -3023,7 +3041,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in)
else
{
IM_ASSERT(Fonts.Size > 0 && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font.
font = Fonts.back();
font = font_cfg_in->DstFont ? font_cfg_in->DstFont : Fonts.back();
}
// Add to list
@@ -3094,6 +3112,7 @@ static const char* GetDefaultCompressedFontDataTTF(int* out_size);
#endif
// Load embedded ProggyClean.ttf at size 13, disable oversampling
// If you want a similar font which may be better scaled, consider using ProggyVector from the same author!
ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
{
#ifndef IMGUI_DISABLE_DEFAULT_FONT
@@ -3112,9 +3131,7 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
int ttf_compressed_size = 0;
const char* ttf_compressed = GetDefaultCompressedFontDataTTF(&ttf_compressed_size);
const ImWchar* glyph_ranges = font_cfg.GlyphRanges != NULL ? font_cfg.GlyphRanges : GetGlyphRangesDefault();
ImFont* font = AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, font_cfg.SizePixels, &font_cfg, glyph_ranges);
return font;
return AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, font_cfg.SizePixels, &font_cfg);
#else
IM_ASSERT(0 && "AddFontDefault() disabled in this build.");
IM_UNUSED(font_cfg_template);
@@ -3186,7 +3203,7 @@ ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(const char* compressed
// On font removal we need to remove references (otherwise we could queue removal?)
// We allow old_font == new_font which forces updating all values (e.g. sizes)
static void ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font, ImFont* new_font)
void ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font, ImFont* new_font)
{
for (ImDrawListSharedData* shared_data : atlas->DrawListSharedDatas)
{
@@ -3231,7 +3248,7 @@ void ImFontAtlas::RemoveFont(ImFont* font)
ImFontAtlasBuildUpdatePointers(this);
font->ContainerAtlas = NULL;
font->OwnerAtlas = NULL;
IM_DELETE(font);
// Notify external systems
@@ -3361,11 +3378,7 @@ void ImFontAtlasBuildMain(ImFontAtlas* atlas)
{
IM_ASSERT(!atlas->Locked && "Cannot modify a locked ImFontAtlas!");
if (atlas->TexData && atlas->TexData->Format != atlas->TexDesiredFormat)
{
ImVec2i new_tex_size = ImFontAtlasTextureGetSizeEstimate(atlas);
ImFontAtlasBuildDestroy(atlas);
ImFontAtlasTextureAdd(atlas, new_tex_size.x, new_tex_size.y);
}
ImFontAtlasBuildClear(atlas);
if (atlas->Builder == NULL)
ImFontAtlasBuildInit(atlas);
@@ -3613,7 +3626,7 @@ void ImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas, ImFont* font, ImFontConf
{
font->ClearOutputData();
//font->FontSize = src->SizePixels;
font->ContainerAtlas = atlas;
font->OwnerAtlas = atlas;
IM_ASSERT(font->Sources[0] == src);
}
atlas->TexIsBuilt = false; // For legacy backends
@@ -3623,6 +3636,11 @@ void ImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas, ImFont* font, ImFontConf
void ImFontAtlasFontDestroySourceData(ImFontAtlas* atlas, ImFontConfig* src)
{
IM_UNUSED(atlas);
// IF YOU GET A CRASH IN THE IM_FREE() CALL HERE AND USED AddFontFromMemoryTTF():
// - DUE TO LEGACY REASON AddFontFromMemoryTTF() TRANSFERS MEMORY OWNERSHIP BY DEFAULT.
// - IT WILL THEREFORE CRASH WHEN PASSED DATA WHICH MAY NOT BE FREEED BY IMGUI.
// - USE `ImFontConfig font_cfg; font_cfg.FontDataOwnedByAtlas = false; io.Fonts->AddFontFromMemoryTTF(....., &cfg);` to disable passing ownership/
// WE WILL ADDRESS THIS IN A FUTURE REWORK OF THE API.
if (src->FontDataOwnedByAtlas)
IM_FREE(src->FontData);
src->FontData = NULL;
@@ -3636,7 +3654,7 @@ void ImFontAtlasFontDestroySourceData(ImFontAtlas* atlas, ImFontConfig* src)
// FIXME-NEWATLAS: This borrows too much from FontLoader's FontLoadGlyph() handlers and suggest that we should add further helpers.
static ImFontGlyph* ImFontAtlasBuildSetupFontBakedEllipsis(ImFontAtlas* atlas, ImFontBaked* baked)
{
ImFont* font = baked->ContainerFont;
ImFont* font = baked->OwnerFont;
IM_ASSERT(font->EllipsisChar != 0);
const ImFontGlyph* dot_glyph = baked->FindGlyphNoFallback((ImWchar)'.');
@@ -3682,7 +3700,7 @@ static void ImFontAtlasBuildSetupFontBakedFallback(ImFontBaked* baked)
{
IM_ASSERT(baked->FallbackGlyphIndex == -1);
IM_ASSERT(baked->FallbackAdvanceX == 0.0f);
ImFont* font = baked->ContainerFont;
ImFont* font = baked->OwnerFont;
ImFontGlyph* fallback_glyph = NULL;
if (font->FallbackChar != 0)
fallback_glyph = baked->FindGlyphNoFallback(font->FallbackChar);
@@ -3692,7 +3710,7 @@ static void ImFontAtlasBuildSetupFontBakedFallback(ImFontBaked* baked)
ImFontGlyph glyph;
glyph.Codepoint = 0;
glyph.AdvanceX = space_glyph ? space_glyph->AdvanceX : IM_ROUND(baked->Size * 0.40f);
fallback_glyph = ImFontAtlasBakedAddFontGlyph(font->ContainerAtlas, baked, NULL, &glyph);
fallback_glyph = ImFontAtlasBakedAddFontGlyph(font->OwnerAtlas, baked, NULL, &glyph);
}
baked->FallbackGlyphIndex = baked->Glyphs.index_from_ptr(fallback_glyph); // Storing index avoid need to update pointer on growth and simplify inner loop code
baked->FallbackAdvanceX = fallback_glyph->AdvanceX;
@@ -3773,7 +3791,7 @@ ImFontBaked* ImFontAtlasBakedAdd(ImFontAtlas* atlas, ImFont* font, float font_si
baked->Size = font_size;
baked->RasterizerDensity = font_rasterizer_density;
baked->BakedId = baked_id;
baked->ContainerFont = font;
baked->OwnerFont = font;
baked->LastUsedFrame = atlas->Builder->FrameCount;
// Initialize backend data
@@ -3808,7 +3826,7 @@ ImFontBaked* ImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas, ImFont* font, f
for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++)
{
ImFontBaked* baked = &builder->BakedPool[baked_n];
if (baked->ContainerFont != font || baked->WantDestroy)
if (baked->OwnerFont != font || baked->WantDestroy)
continue;
if (step_n == 0 && baked->RasterizerDensity != font_rasterizer_density) // First try with same density
continue;
@@ -3864,7 +3882,7 @@ void ImFontAtlasFontDiscardBakes(ImFontAtlas* atlas, ImFont* font, int unused_fr
ImFontBaked* baked = &builder->BakedPool[baked_n];
if (baked->LastUsedFrame + unused_frames > atlas->Builder->FrameCount)
continue;
if (baked->ContainerFont != font || baked->WantDestroy)
if (baked->OwnerFont != font || baked->WantDestroy)
continue;
ImFontAtlasBakedDiscard(atlas, font, baked);
}
@@ -3879,9 +3897,9 @@ void ImFontAtlasBuildDiscardBakes(ImFontAtlas* atlas, int unused_frames)
ImFontBaked* baked = &builder->BakedPool[baked_n];
if (baked->LastUsedFrame + unused_frames > atlas->Builder->FrameCount)
continue;
if (baked->WantDestroy || (baked->ContainerFont->Flags & ImFontFlags_LockBakedSizes))
if (baked->WantDestroy || (baked->OwnerFont->Flags & ImFontFlags_LockBakedSizes))
continue;
ImFontAtlasBakedDiscard(atlas, baked->ContainerFont, baked);
ImFontAtlasBakedDiscard(atlas, baked->OwnerFont, baked);
}
}
@@ -3902,6 +3920,11 @@ void ImFontAtlasRemoveDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedDat
void ImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atlas, ImTextureRef old_tex, ImTextureRef new_tex)
{
for (ImDrawListSharedData* shared_data : atlas->DrawListSharedDatas)
{
// If Context 2 uses font owned by Context 1 which already called EndFrame()/Render(), we don't want to mess with draw commands for Context 1
if (shared_data->Context && !shared_data->Context->WithinFrameScope)
continue;
for (ImDrawList* draw_list : shared_data->DrawLists)
{
// Replace in command-buffer
@@ -3915,6 +3938,7 @@ void ImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atlas, ImTextureRef old_tex
if (stacked_tex == old_tex)
stacked_tex = new_tex;
}
}
}
// Update texture coordinates in all draw list shared context
@@ -3971,7 +3995,6 @@ ImTextureData* ImFontAtlasTextureAdd(ImFontAtlas* atlas, int w, int h)
}
new_tex->Create(atlas->TexDesiredFormat, w, h);
new_tex->Status = ImTextureStatus_WantCreate;
atlas->TexIsBuilt = false;
ImFontAtlasBuildSetTexture(atlas, new_tex);
@@ -4296,6 +4319,8 @@ void ImFontAtlasPackDiscardRect(ImFontAtlas* atlas, ImFontAtlasRectId id)
index_entry->IsUsed = false;
index_entry->TargetIndex = builder->RectsIndexFreeListStart;
index_entry->Generation++;
if (index_entry->Generation == 0)
index_entry->Generation++; // Keep non-zero on overflow
const int pack_padding = atlas->TexGlyphPadding;
builder->RectsIndexFreeListStart = index_idx;
@@ -4375,6 +4400,7 @@ ImTextureRect* ImFontAtlasPackGetRectSafe(ImFontAtlas* atlas, ImFontAtlasRectId
if (atlas->Builder == NULL)
ImFontAtlasBuildInit(atlas);
ImFontAtlasBuilder* builder = (ImFontAtlasBuilder*)atlas->Builder;
IM_MSVC_WARNING_SUPPRESS(28182); // Static Analysis false positive "warning C28182: Dereferencing NULL pointer 'builder'"
if (index_idx >= builder->RectsIndex.Size)
return NULL;
ImFontAtlasRectEntry* index_entry = &builder->RectsIndex[index_idx];
@@ -4412,12 +4438,12 @@ static void ImFontAtlas_FontHookRemapCodepoint(ImFontAtlas* atlas, ImFont* font,
static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codepoint, float* only_load_advance_x)
{
ImFont* font = baked->ContainerFont;
ImFontAtlas* atlas = font->ContainerAtlas;
ImFont* font = baked->OwnerFont;
ImFontAtlas* atlas = font->OwnerAtlas;
if (atlas->Locked || (font->Flags & ImFontFlags_NoLoadGlyphs))
{
// Lazily load fallback glyph
if (baked->FallbackGlyphIndex == -1 && baked->LockLoadingFallback == 0)
if (baked->FallbackGlyphIndex == -1 && baked->LoadNoFallback == 0)
ImFontAtlasBuildSetupFontBakedFallback(baked);
return NULL;
}
@@ -4469,7 +4495,7 @@ static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codep
}
// Lazily load fallback glyph
if (baked->LockLoadingFallback)
if (baked->LoadNoFallback)
return NULL;
if (baked->FallbackGlyphIndex == -1)
ImFontAtlasBuildSetupFontBakedFallback(baked);
@@ -4483,7 +4509,7 @@ static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codep
static float ImFontBaked_BuildLoadGlyphAdvanceX(ImFontBaked* baked, ImWchar codepoint)
{
if (baked->Size >= IMGUI_FONT_SIZE_THRESHOLD_FOR_LOADADVANCEXONLYMODE)
if (baked->Size >= IMGUI_FONT_SIZE_THRESHOLD_FOR_LOADADVANCEXONLYMODE || baked->LoadNoRenderOnLayout)
{
// First load AdvanceX value used by CalcTextSize() API then load the rest when loaded by drawing API.
float only_advance_x = 0.0f;
@@ -4518,16 +4544,16 @@ void ImFontAtlasDebugLogTextureRequests(ImFontAtlas* atlas)
if (tex->Status == ImTextureStatus_WantCreate)
IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: create %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
else if (tex->Status == ImTextureStatus_WantDestroy)
IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: destroy %dx%d, texid=0x%" IM_PRIX64 ", backend_data=%p\n", tex->UniqueID, tex->Width, tex->Height, tex->TexID, tex->BackendUserData);
IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: destroy %dx%d, texid=0x%" IM_PRIX64 ", backend_data=%p\n", tex->UniqueID, tex->Width, tex->Height, IM_TEXTUREID_TO_U64(tex->TexID), tex->BackendUserData);
else if (tex->Status == ImTextureStatus_WantUpdates)
{
IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: update %d regions, texid=0x%" IM_PRIX64 ", backend_data=0x%" IM_PRIX64 "\n", tex->UniqueID, tex->Updates.Size, tex->TexID, (ImU64)(intptr_t)tex->BackendUserData);
IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: update %d regions, texid=0x%" IM_PRIX64 ", backend_data=0x%" IM_PRIX64 "\n", tex->UniqueID, tex->Updates.Size, IM_TEXTUREID_TO_U64(tex->TexID), (ImU64)(intptr_t)tex->BackendUserData);
for (const ImTextureRect& r : tex->Updates)
{
IM_UNUSED(r);
IM_ASSERT(r.x >= 0 && r.y >= 0);
IM_ASSERT(r.x + r.w <= tex->Width && r.y + r.h <= tex->Height); // In theory should subtract PackPadding but it's currently part of atlas and mid-frame change would wreck assert.
//IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: update (% 4d..%-4d)->(% 4d..%-4d), texid=0x%" IM_PRIX64 ", backend_data=0x%" IM_PRIX64 "\n", tex->UniqueID, r.x, r.y, r.x + r.w, r.y + r.h, tex->TexID, (ImU64)(intptr_t)tex->BackendUserData);
//IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: update (% 4d..%-4d)->(% 4d..%-4d), texid=0x%" IM_PRIX64 ", backend_data=0x%" IM_PRIX64 "\n", tex->UniqueID, r.x, r.y, r.x + r.w, r.y + r.h, IM_TEXTUREID_TO_U64(tex->TexID), (ImU64)(intptr_t)tex->BackendUserData);
}
}
}
@@ -4572,15 +4598,16 @@ static bool ImGui_ImplStbTrueType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig*
}
src->FontLoaderData = bd_font_data;
const float ref_size = src->DstFont->Sources[0]->SizePixels;
if (src->MergeMode && src->SizePixels == 0.0f)
src->SizePixels = src->DstFont->Sources[0]->SizePixels;
src->SizePixels = ref_size;
if (src->SizePixels >= 0.0f)
bd_font_data->ScaleFactor = stbtt_ScaleForPixelHeight(&bd_font_data->FontInfo, 1.0f);
else
bd_font_data->ScaleFactor = stbtt_ScaleForMappingEmToPixels(&bd_font_data->FontInfo, 1.0f);
if (src->MergeMode && src->SizePixels != 0.0f)
bd_font_data->ScaleFactor *= src->SizePixels / src->DstFont->Sources[0]->SizePixels; // FIXME-NEWATLAS: Should tidy up that a bit
if (src->MergeMode && src->SizePixels != 0.0f && ref_size != 0.0f)
bd_font_data->ScaleFactor *= src->SizePixels / ref_size; // FIXME-NEWATLAS: Should tidy up that a bit
return true;
}
@@ -4679,17 +4706,14 @@ static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontC
builder->TempBuffer.resize(w * h * 1);
unsigned char* bitmap_pixels = builder->TempBuffer.Data;
memset(bitmap_pixels, 0, w * h * 1);
stbtt_MakeGlyphBitmapSubpixel(&bd_font_data->FontInfo, bitmap_pixels, r->w - oversample_h + 1, r->h - oversample_v + 1, w,
scale_for_raster_x, scale_for_raster_y, 0, 0, glyph_index);
// Oversampling
// Render with oversampling
// (those functions conveniently assert if pixels are not cleared, which is another safety layer)
if (oversample_h > 1)
stbtt__h_prefilter(bitmap_pixels, r->w, r->h, r->w, oversample_h);
if (oversample_v > 1)
stbtt__v_prefilter(bitmap_pixels, r->w, r->h, r->w, oversample_v);
float sub_x, sub_y;
stbtt_MakeGlyphBitmapSubpixelPrefilter(&bd_font_data->FontInfo, bitmap_pixels, w, h, w,
scale_for_raster_x, scale_for_raster_y, 0, 0, oversample_h, oversample_v, &sub_x, &sub_y, glyph_index);
const float ref_size = baked->ContainerFont->Sources[0]->SizePixels;
const float ref_size = baked->OwnerFont->Sources[0]->SizePixels;
const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f;
float font_off_x = (src->GlyphOffset.x * offsets_scale);
float font_off_y = (src->GlyphOffset.y * offsets_scale);
@@ -4697,8 +4721,8 @@ static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontC
font_off_x = IM_ROUND(font_off_x);
if (src->PixelSnapV)
font_off_y = IM_ROUND(font_off_y);
font_off_x += stbtt__oversample_shift(oversample_h);
font_off_y += stbtt__oversample_shift(oversample_v) + IM_ROUND(baked->Ascent);
font_off_x += sub_x;
font_off_y += sub_y + IM_ROUND(baked->Ascent);
float recip_h = 1.0f / (oversample_h * rasterizer_density);
float recip_v = 1.0f / (oversample_v * rasterizer_density);
@@ -5016,7 +5040,9 @@ const ImWchar* ImFontAtlas::GetGlyphRangesVietnamese()
void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end)
{
while (text_end ? (text < text_end) : *text)
if (text_end == NULL)
text_end = text + strlen(text);
while (text < text_end)
{
unsigned int c = 0;
int c_len = ImTextCharFromUtf8(&c, text, text_end);
@@ -5084,7 +5110,7 @@ ImFont::~ImFont()
void ImFont::ClearOutputData()
{
if (ImFontAtlas* atlas = ContainerAtlas)
if (ImFontAtlas* atlas = OwnerAtlas)
ImFontAtlasFontDiscardBakes(atlas, this, 0);
FallbackChar = EllipsisChar = 0;
memset(Used8kPagesMap, 0, sizeof(Used8kPagesMap));
@@ -5129,7 +5155,7 @@ ImFontGlyph* ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked
if (src != NULL)
{
// Clamp & recenter if needed
const float ref_size = baked->ContainerFont->Sources[0]->SizePixels;
const float ref_size = baked->OwnerFont->Sources[0]->SizePixels;
const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f;
float advance_x = ImClamp(glyph->AdvanceX, src->GlyphMinAdvanceX * offsets_scale, src->GlyphMaxAdvanceX * offsets_scale);
if (advance_x != glyph->AdvanceX)
@@ -5155,7 +5181,7 @@ ImFontGlyph* ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked
baked->IndexAdvanceX[codepoint] = glyph->AdvanceX;
baked->IndexLookup[codepoint] = (ImU16)glyph_idx;
const int page_n = codepoint / 8192;
baked->ContainerFont->Used8kPagesMap[page_n >> 3] |= 1 << (page_n & 7);
baked->OwnerFont->Used8kPagesMap[page_n >> 3] |= 1 << (page_n & 7);
return glyph;
}
@@ -5167,7 +5193,7 @@ void ImFontAtlasBakedAddFontGlyphAdvancedX(ImFontAtlas* atlas, ImFontBaked* bake
if (src != NULL)
{
// Clamp & recenter if needed
const float ref_size = baked->ContainerFont->Sources[0]->SizePixels;
const float ref_size = baked->OwnerFont->Sources[0]->SizePixels;
const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f;
advance_x = ImClamp(advance_x, src->GlyphMinAdvanceX * offsets_scale, src->GlyphMaxAdvanceX * offsets_scale);
@@ -5189,7 +5215,7 @@ void ImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas, ImFontBaked* baked,
ImTextureData* tex = atlas->TexData;
IM_ASSERT(r->x + r->w <= tex->Width && r->y + r->h <= tex->Height);
ImFontAtlasTextureBlockConvert(src_pixels, src_fmt, src_pitch, (unsigned char*)tex->GetPixelsAt(r->x, r->y), tex->Format, tex->GetPitch(), r->w, r->h);
ImFontAtlasPostProcessData pp_data = { atlas, baked->ContainerFont, src, baked, glyph, tex->GetPixelsAt(r->x, r->y), tex->Format, tex->GetPitch(), r->w, r->h };
ImFontAtlasPostProcessData pp_data = { atlas, baked->OwnerFont, src, baked, glyph, tex->GetPixelsAt(r->x, r->y), tex->Format, tex->GetPitch(), r->w, r->h };
ImFontAtlasTextureBlockPostProcess(&pp_data);
ImFontAtlasTextureBlockQueueUpload(atlas, tex, r->x, r->y, r->w, r->h);
}
@@ -5225,9 +5251,9 @@ ImFontGlyph* ImFontBaked::FindGlyphNoFallback(ImWchar c)
if (i != IM_FONTGLYPH_INDEX_UNUSED)
return &Glyphs.Data[i];
}
LockLoadingFallback = true; // This is actually a rare call, not done in hot-loop, so we prioritize not adding extra cruft to ImFontBaked_BuildLoadGlyph() call sites.
LoadNoFallback = true; // This is actually a rare call, not done in hot-loop, so we prioritize not adding extra cruft to ImFontBaked_BuildLoadGlyph() call sites.
ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c, NULL);
LockLoadingFallback = false;
LoadNoFallback = false;
return glyph;
}
@@ -5247,7 +5273,7 @@ bool ImFontBaked::IsGlyphLoaded(ImWchar c)
// This is not fast query
bool ImFont::IsGlyphInFont(ImWchar c)
{
ImFontAtlas* atlas = ContainerAtlas;
ImFontAtlas* atlas = OwnerAtlas;
ImFontAtlas_FontHookRemapCodepoint(atlas, this, &c);
for (ImFontConfig* src : Sources)
{
@@ -5296,7 +5322,7 @@ ImFontBaked* ImFont::GetFontBaked(float size, float density)
if (baked && baked->Size == size && baked->RasterizerDensity == density)
return baked;
ImFontAtlas* atlas = ContainerAtlas;
ImFontAtlas* atlas = OwnerAtlas;
ImFontAtlasBuilder* builder = atlas->Builder;
baked = ImFontAtlasBakedGetOrAdd(atlas, this, size, density);
if (baked == NULL)
@@ -5317,7 +5343,7 @@ ImFontBaked* ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float fo
ImFontBaked* baked = *p_baked_in_map;
if (baked != NULL)
{
IM_ASSERT(baked->Size == font_size && baked->ContainerFont == font && baked->BakedId == baked_id);
IM_ASSERT(baked->Size == font_size && baked->OwnerFont == font && baked->BakedId == baked_id);
return baked;
}
@@ -5342,10 +5368,11 @@ ImFontBaked* ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float fo
}
// Trim trailing space and find beginning of next line
static inline const char* CalcWordWrapNextLineStartA(const char* text, const char* text_end)
const char* ImTextCalcWordWrapNextLineStart(const char* text, const char* text_end, ImDrawTextFlags flags)
{
while (text < text_end && ImCharIsBlankA(*text))
text++;
if ((flags & ImDrawTextFlags_WrapKeepBlanks) == 0)
while (text < text_end && ImCharIsBlankA(*text))
text++;
if (*text == '\n')
text++;
return text;
@@ -5354,7 +5381,7 @@ static inline const char* CalcWordWrapNextLineStartA(const char* text, const cha
// Simple word-wrapping for English, not full-featured. Please submit failing cases!
// This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end.
// FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.)
const char* ImFont::CalcWordWrapPosition(float size, const char* text, const char* text_end, float wrap_width)
const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* text, const char* text_end, float wrap_width, ImDrawTextFlags flags)
{
// For references, possible wrap point marked with ^
// "aaa bbb, ccc,ddd. eee fff. ggg!"
@@ -5368,7 +5395,7 @@ const char* ImFont::CalcWordWrapPosition(float size, const char* text, const cha
// Cut words that cannot possibly fit within one line.
// e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish"
ImFontBaked* baked = GetFontBaked(size);
ImFontBaked* baked = font->GetFontBaked(size);
const float scale = size / baked->Size;
float line_width = 0.0f;
@@ -5394,12 +5421,7 @@ const char* ImFont::CalcWordWrapPosition(float size, const char* text, const cha
if (c < 32)
{
if (c == '\n')
{
line_width = word_width = blank_width = 0.0f;
inside_word = true;
s = next_s;
continue;
}
return s; // Direct return, skip "Wrap_width is too small to fit anything" path.
if (c == '\r')
{
s = next_s;
@@ -5434,6 +5456,8 @@ const char* ImFont::CalcWordWrapPosition(float size, const char* text, const cha
{
prev_word_end = word_end;
line_width += word_width + blank_width;
if ((flags & ImDrawTextFlags_WrapKeepBlanks) && line_width <= wrap_width)
prev_word_end = s;
word_width = blank_width = 0.0f;
}
@@ -5460,14 +5484,21 @@ const char* ImFont::CalcWordWrapPosition(float size, const char* text, const cha
return s;
}
ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** remaining)
const char* ImFont::CalcWordWrapPosition(float size, const char* text, const char* text_end, float wrap_width)
{
return ImFontCalcWordWrapPositionEx(this, size, text, text_end, wrap_width, ImDrawTextFlags_None);
}
ImVec2 ImFontCalcTextSizeEx(ImFont* font, float size, float max_width, float wrap_width, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags)
{
if (!text_end)
text_end = text_begin + ImStrlen(text_begin); // FIXME-OPT: Need to avoid this.
if (!text_end_display)
text_end_display = text_end;
ImFontBaked* baked = font->GetFontBaked(size);
const float line_height = size;
ImFontBaked* baked = GetFontBaked(size);
const float scale = size / baked->Size;
const float scale = line_height / baked->Size;
ImVec2 text_size = ImVec2(0, 0);
float line_width = 0.0f;
@@ -5476,13 +5507,14 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
const char* word_wrap_eol = NULL;
const char* s = text_begin;
while (s < text_end)
while (s < text_end_display)
{
// Word-wrapping
if (word_wrap_enabled)
{
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
if (!word_wrap_eol)
word_wrap_eol = CalcWordWrapPosition(size, s, text_end, wrap_width - line_width);
word_wrap_eol = ImFontCalcWordWrapPositionEx(font, size, s, text_end, wrap_width - line_width, flags);
if (s >= word_wrap_eol)
{
@@ -5490,8 +5522,10 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
text_size.x = line_width;
text_size.y += line_height;
line_width = 0.0f;
s = ImTextCalcWordWrapNextLineStart(s, text_end, flags); // Wrapping skips upcoming blanks
if (flags & ImDrawTextFlags_StopOnNewLine)
break;
word_wrap_eol = NULL;
s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks
continue;
}
}
@@ -5504,18 +5538,17 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
else
s += ImTextCharFromUtf8(&c, s, text_end);
if (c < 32)
if (c == '\n')
{
if (c == '\n')
{
text_size.x = ImMax(text_size.x, line_width);
text_size.y += line_height;
line_width = 0.0f;
continue;
}
if (c == '\r')
continue;
text_size.x = ImMax(text_size.x, line_width);
text_size.y += line_height;
line_width = 0.0f;
if (flags & ImDrawTextFlags_StopOnNewLine)
break;
continue;
}
if (c == '\r')
continue;
// Optimized inline version of 'float char_width = GetCharAdvance((ImWchar)c);'
float char_width = (c < (unsigned int)baked->IndexAdvanceX.Size) ? baked->IndexAdvanceX.Data[c] : -1.0f;
@@ -5535,15 +5568,23 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
if (text_size.x < line_width)
text_size.x = line_width;
if (line_width > 0 || text_size.y == 0.0f)
if (out_offset != NULL)
*out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n
if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n
text_size.y += line_height;
if (remaining)
*remaining = s;
if (out_remaining != NULL)
*out_remaining = s;
return text_size;
}
ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** out_remaining)
{
return ImFontCalcTextSizeEx(this, size, max_width, wrap_width, text_begin, text_end, text_end, out_remaining, NULL, ImDrawTextFlags_None);
}
// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c, const ImVec4* cpu_fine_clip)
{
@@ -5584,7 +5625,8 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im
}
// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip)
// DO NOT CALL DIRECTLY THIS WILL CHANGE WILDLY IN 2025-2025. Use ImDrawList::AddText().
void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, ImDrawTextFlags flags)
{
// Align to be pixel perfect
begin:
@@ -5614,8 +5656,8 @@ begin:
// FIXME-OPT: This is not optimal as do first do a search for \n before calling CalcWordWrapPosition().
// If the specs for CalcWordWrapPosition() were reworked to optionally return on \n we could combine both.
// However it is still better than nothing performing the fast-forward!
s = CalcWordWrapPosition(size, s, line_end ? line_end : text_end, wrap_width);
s = CalcWordWrapNextLineStartA(s, text_end);
s = ImFontCalcWordWrapPositionEx(this, size, s, line_end ? line_end : text_end, wrap_width, flags);
s = ImTextCalcWordWrapNextLineStart(s, text_end, flags);
}
else
{
@@ -5650,6 +5692,7 @@ begin:
ImDrawIdx* idx_write = draw_list->_IdxWritePtr;
unsigned int vtx_index = draw_list->_VtxCurrentIdx;
const int cmd_count = draw_list->CmdBuffer.Size;
const bool cpu_fine_clip = (flags & ImDrawTextFlags_CpuFineClip) != 0;
const ImU32 col_untinted = col | ~IM_COL32_A_MASK;
const char* word_wrap_eol = NULL;
@@ -5660,7 +5703,7 @@ begin:
{
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
if (!word_wrap_eol)
word_wrap_eol = CalcWordWrapPosition(size, s, text_end, wrap_width - (x - origin_x));
word_wrap_eol = ImFontCalcWordWrapPositionEx(this, size, s, text_end, wrap_width - (x - origin_x), flags);
if (s >= word_wrap_eol)
{
@@ -5669,7 +5712,7 @@ begin:
if (y > clip_rect.w)
break; // break out of main loop
word_wrap_eol = NULL;
s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks
s = ImTextCalcWordWrapNextLineStart(s, text_end, flags); // Wrapping skips upcoming blanks
continue;
}
}
@@ -5837,8 +5880,9 @@ void ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir d
void ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col)
{
// FIXME-OPT: This should be baked in font.
draw_list->AddCircleFilled(pos, draw_list->_Data->FontSize * 0.20f, col, 8);
// FIXME-OPT: This should be baked in font now that it's easier.
float font_size = draw_list->_Data->FontSize;
draw_list->AddCircleFilled(pos, font_size * 0.20f, col, (font_size < 22) ? 8 : (font_size < 40) ? 12 : 0); // Hardcode optimal/nice tessellation threshold
}
void ImGui::RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz)
@@ -6121,6 +6165,7 @@ static unsigned int stb_decompress(unsigned char *output, const unsigned char *i
// Copyright (c) 2004, 2005 Tristan Grimmer
// MIT license (see License.txt in http://www.proggyfonts.net/index.php?menu=download)
// Download and more information at http://www.proggyfonts.net or http://upperboundsinteractive.com/fonts.php
// If you want a similar font which may be better scaled, consider using ProggyVector from the same author!
//-----------------------------------------------------------------------------
#ifndef IMGUI_DISABLE_DEFAULT_FONT
+184 -129
View File
@@ -1,4 +1,4 @@
// dear imgui, v1.92.1
// dear imgui, v1.92.5
// (internal structures/api)
// You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
@@ -77,8 +77,8 @@ Index of this file:
#ifdef _MSC_VER
#pragma warning (push)
#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport)
#pragma warning (disable: 26812) // The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer)
#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types
#endif
@@ -193,6 +193,7 @@ enum ImGuiLocKey : int; // -> enum ImGuiLocKey // E
typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical
// Flags
typedef int ImDrawTextFlags; // -> enum ImDrawTextFlags_ // Flags: for ImTextCalcWordWrapPositionEx()
typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later)
typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags
typedef int ImGuiFocusRequestFlags; // -> enum ImGuiFocusRequestFlags_ // Flags: for FocusWindow()
@@ -208,6 +209,7 @@ typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // F
typedef int ImGuiTextFlags; // -> enum ImGuiTextFlags_ // Flags: for TextEx()
typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // Flags: for BeginTooltipEx()
typedef int ImGuiTypingSelectFlags; // -> enum ImGuiTypingSelectFlags_ // Flags: for GetTypingSelectRequest()
typedef int ImGuiWindowBgClickFlags; // -> enum ImGuiWindowBgClickFlags_ // Flags: for overriding behavior of clicking on window background/void.
typedef int ImGuiWindowRefreshFlags; // -> enum ImGuiWindowRefreshFlags_ // Flags: for SetNextWindowRefreshPolicy()
// Table column indexing
@@ -276,10 +278,8 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
#define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255
#define IM_TRUNC(_VAL) ((float)(int)(_VAL)) // ImTrunc() is not inlined in MSVC debug builds
#define IM_ROUND(_VAL) ((float)(int)((_VAL) + 0.5f)) //
#define IM_STRINGIFY_HELPER(_X) #_X
#define IM_STRINGIFY(_X) IM_STRINGIFY_HELPER(_X) // Preprocessor idiom to stringify e.g. an integer.
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
#define IM_FLOOR IM_TRUNC
#define IM_FLOOR IM_TRUNC // [OBSOLETE] Renamed in 1.90.0 (Sept 2023)
#endif
// Hint for branch prediction
@@ -335,6 +335,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
#define IM_PRIu64 "llu"
#define IM_PRIX64 "llX"
#endif
#define IM_TEXTUREID_TO_U64(_TEXID) ((ImU64)(intptr_t)(_TEXID))
//-----------------------------------------------------------------------------
// [SECTION] Generic helpers
@@ -366,20 +367,21 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
// Helpers: Hashing
IMGUI_API ImGuiID ImHashData(const void* data, size_t data_size, ImGuiID seed = 0);
IMGUI_API ImGuiID ImHashStr(const char* data, size_t data_size = 0, ImGuiID seed = 0);
IMGUI_API const char* ImHashSkipUncontributingPrefix(const char* label);
// Helpers: Sorting
#ifndef ImQsort
static inline void ImQsort(void* base, size_t count, size_t size_of_element, int(IMGUI_CDECL *compare_func)(void const*, void const*)) { if (count > 1) qsort(base, count, size_of_element, compare_func); }
inline void ImQsort(void* base, size_t count, size_t size_of_element, int(IMGUI_CDECL *compare_func)(void const*, void const*)) { if (count > 1) qsort(base, count, size_of_element, compare_func); }
#endif
// Helpers: Color Blending
IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b);
// Helpers: Bit manipulation
static inline bool ImIsPowerOfTwo(int v) { return v != 0 && (v & (v - 1)) == 0; }
static inline bool ImIsPowerOfTwo(ImU64 v) { return v != 0 && (v & (v - 1)) == 0; }
static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }
static inline unsigned int ImCountSetBits(unsigned int v) { unsigned int count = 0; while (v > 0) { v = v & (v - 1); count++; } return count; }
inline bool ImIsPowerOfTwo(int v) { return v != 0 && (v & (v - 1)) == 0; }
inline bool ImIsPowerOfTwo(ImU64 v) { return v != 0 && (v & (v - 1)) == 0; }
inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }
inline unsigned int ImCountSetBits(unsigned int v) { unsigned int count = 0; while (v > 0) { v = v & (v - 1); count++; } return count; }
// Helpers: String
#define ImStrlen strlen
@@ -398,10 +400,10 @@ IMGUI_API const char* ImStrSkipBlank(const char* str);
IMGUI_API int ImStrlenW(const ImWchar* str); // Computer string length (ImWchar string)
IMGUI_API const char* ImStrbol(const char* buf_mid_line, const char* buf_begin); // Find beginning-of-line
IM_MSVC_RUNTIME_CHECKS_OFF
static inline char ImToUpper(char c) { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; }
static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; }
static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; }
static inline bool ImCharIsXdigitA(char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); }
inline char ImToUpper(char c) { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; }
inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; }
inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; }
inline bool ImCharIsXdigitA(char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); }
IM_MSVC_RUNTIME_CHECKS_RESTORE
// Helpers: Formatting
@@ -417,25 +419,38 @@ IMGUI_API const char* ImParseFormatSanitizeForScanning(const char* fmt_in, cha
IMGUI_API int ImParseFormatPrecision(const char* format, int default_value);
// Helpers: UTF-8 <> wchar conversions
IMGUI_API const char* ImTextCharToUtf8(char out_buf[5], unsigned int c); // return out_buf
IMGUI_API int ImTextCharToUtf8(char out_buf[5], unsigned int c); // return output UTF-8 bytes count
IMGUI_API int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count
IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // read one character. return input UTF-8 bytes count
IMGUI_API int ImTextStrFromUtf8(ImWchar* out_buf, int out_buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count
IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count)
IMGUI_API int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end); // return number of bytes to express one char in UTF-8
IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string in UTF-8
IMGUI_API const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr); // return previous UTF-8 code-point.
IMGUI_API const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_p); // return previous UTF-8 code-point.
IMGUI_API const char* ImTextFindValidUtf8CodepointEnd(const char* in_text_start, const char* in_text_end, const char* in_p); // return previous UTF-8 code-point if 'in_p' is not the end of a valid one.
IMGUI_API int ImTextCountLines(const char* in_text, const char* in_text_end); // return number of lines taken by text. trailing carriage return doesn't count as an extra line.
// Helpers: High-level text functions (DO NOT USE!!! THIS IS A MINIMAL SUBSET OF LARGER UPCOMING CHANGES)
enum ImDrawTextFlags_
{
ImDrawTextFlags_None = 0,
ImDrawTextFlags_CpuFineClip = 1 << 0, // Must be == 1/true for legacy with 'bool cpu_fine_clip' arg to RenderText()
ImDrawTextFlags_WrapKeepBlanks = 1 << 1,
ImDrawTextFlags_StopOnNewLine = 1 << 2,
};
IMGUI_API ImVec2 ImFontCalcTextSizeEx(ImFont* font, float size, float max_width, float wrap_width, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags);
IMGUI_API const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* text, const char* text_end, float wrap_width, ImDrawTextFlags flags = 0);
IMGUI_API const char* ImTextCalcWordWrapNextLineStart(const char* text, const char* text_end, ImDrawTextFlags flags = 0); // trim trailing space and find beginning of next line
// Helpers: File System
#ifdef IMGUI_DISABLE_FILE_FUNCTIONS
#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
typedef void* ImFileHandle;
static inline ImFileHandle ImFileOpen(const char*, const char*) { return NULL; }
static inline bool ImFileClose(ImFileHandle) { return false; }
static inline ImU64 ImFileGetSize(ImFileHandle) { return (ImU64)-1; }
static inline ImU64 ImFileRead(void*, ImU64, ImU64, ImFileHandle) { return 0; }
static inline ImU64 ImFileWrite(const void*, ImU64, ImU64, ImFileHandle) { return 0; }
inline ImFileHandle ImFileOpen(const char*, const char*) { return NULL; }
inline bool ImFileClose(ImFileHandle) { return false; }
inline ImU64 ImFileGetSize(ImFileHandle) { return (ImU64)-1; }
inline ImU64 ImFileRead(void*, ImU64, ImU64, ImFileHandle) { return 0; }
inline ImU64 ImFileWrite(const void*, ImU64, ImU64, ImFileHandle) { return 0; }
#endif
#ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
typedef FILE* ImFileHandle;
@@ -462,56 +477,56 @@ IM_MSVC_RUNTIME_CHECKS_OFF
#define ImAtan2(Y, X) atan2f((Y), (X))
#define ImAtof(STR) atof(STR)
#define ImCeil(X) ceilf(X)
static inline float ImPow(float x, float y) { return powf(x, y); } // DragBehaviorT/SliderBehaviorT uses ImPow with either float/double and need the precision
static inline double ImPow(double x, double y) { return pow(x, y); }
static inline float ImLog(float x) { return logf(x); } // DragBehaviorT/SliderBehaviorT uses ImLog with either float/double and need the precision
static inline double ImLog(double x) { return log(x); }
static inline int ImAbs(int x) { return x < 0 ? -x : x; }
static inline float ImAbs(float x) { return fabsf(x); }
static inline double ImAbs(double x) { return fabs(x); }
static inline float ImSign(float x) { return (x < 0.0f) ? -1.0f : (x > 0.0f) ? 1.0f : 0.0f; } // Sign operator - returns -1, 0 or 1 based on sign of argument
static inline double ImSign(double x) { return (x < 0.0) ? -1.0 : (x > 0.0) ? 1.0 : 0.0; }
inline float ImPow(float x, float y) { return powf(x, y); } // DragBehaviorT/SliderBehaviorT uses ImPow with either float/double and need the precision
inline double ImPow(double x, double y) { return pow(x, y); }
inline float ImLog(float x) { return logf(x); } // DragBehaviorT/SliderBehaviorT uses ImLog with either float/double and need the precision
inline double ImLog(double x) { return log(x); }
inline int ImAbs(int x) { return x < 0 ? -x : x; }
inline float ImAbs(float x) { return fabsf(x); }
inline double ImAbs(double x) { return fabs(x); }
inline float ImSign(float x) { return (x < 0.0f) ? -1.0f : (x > 0.0f) ? 1.0f : 0.0f; } // Sign operator - returns -1, 0 or 1 based on sign of argument
inline double ImSign(double x) { return (x < 0.0) ? -1.0 : (x > 0.0) ? 1.0 : 0.0; }
#ifdef IMGUI_ENABLE_SSE
static inline float ImRsqrt(float x) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); }
inline float ImRsqrt(float x) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); }
#else
static inline float ImRsqrt(float x) { return 1.0f / sqrtf(x); }
inline float ImRsqrt(float x) { return 1.0f / sqrtf(x); }
#endif
static inline double ImRsqrt(double x) { return 1.0 / sqrt(x); }
inline double ImRsqrt(double x) { return 1.0 / sqrt(x); }
#endif
// - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support variety of types: signed/unsigned int/long long float/double
// (Exceptionally using templates here but we could also redefine them for those types)
template<typename T> static inline T ImMin(T lhs, T rhs) { return lhs < rhs ? lhs : rhs; }
template<typename T> static inline T ImMax(T lhs, T rhs) { return lhs >= rhs ? lhs : rhs; }
template<typename T> static inline T ImClamp(T v, T mn, T mx) { return (v < mn) ? mn : (v > mx) ? mx : v; }
template<typename T> static inline T ImLerp(T a, T b, float t) { return (T)(a + (b - a) * t); }
template<typename T> static inline void ImSwap(T& a, T& b) { T tmp = a; a = b; b = tmp; }
template<typename T> static inline T ImAddClampOverflow(T a, T b, T mn, T mx) { if (b < 0 && (a < mn - b)) return mn; if (b > 0 && (a > mx - b)) return mx; return a + b; }
template<typename T> static inline T ImSubClampOverflow(T a, T b, T mn, T mx) { if (b > 0 && (a < mn + b)) return mn; if (b < 0 && (a > mx + b)) return mx; return a - b; }
template<typename T> T ImMin(T lhs, T rhs) { return lhs < rhs ? lhs : rhs; }
template<typename T> T ImMax(T lhs, T rhs) { return lhs >= rhs ? lhs : rhs; }
template<typename T> T ImClamp(T v, T mn, T mx) { return (v < mn) ? mn : (v > mx) ? mx : v; }
template<typename T> T ImLerp(T a, T b, float t) { return (T)(a + (b - a) * t); }
template<typename T> void ImSwap(T& a, T& b) { T tmp = a; a = b; b = tmp; }
template<typename T> T ImAddClampOverflow(T a, T b, T mn, T mx) { if (b < 0 && (a < mn - b)) return mn; if (b > 0 && (a > mx - b)) return mx; return a + b; }
template<typename T> T ImSubClampOverflow(T a, T b, T mn, T mx) { if (b > 0 && (a < mn + b)) return mn; if (b < 0 && (a > mx + b)) return mx; return a - b; }
// - Misc maths helpers
static inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x < rhs.x ? lhs.x : rhs.x, lhs.y < rhs.y ? lhs.y : rhs.y); }
static inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x >= rhs.x ? lhs.x : rhs.x, lhs.y >= rhs.y ? lhs.y : rhs.y); }
static inline ImVec2 ImClamp(const ImVec2& v, const ImVec2&mn, const ImVec2&mx) { return ImVec2((v.x < mn.x) ? mn.x : (v.x > mx.x) ? mx.x : v.x, (v.y < mn.y) ? mn.y : (v.y > mx.y) ? mx.y : v.y); }
static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, float t) { return ImVec2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t); }
static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); }
static inline ImVec4 ImLerp(const ImVec4& a, const ImVec4& b, float t) { return ImVec4(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t); }
static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; }
static inline float ImLengthSqr(const ImVec2& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y); }
static inline float ImLengthSqr(const ImVec4& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); }
static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; }
static inline float ImTrunc(float f) { return (float)(int)(f); }
static inline ImVec2 ImTrunc(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); }
static inline float ImFloor(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf()
static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2(ImFloor(v.x), ImFloor(v.y)); }
static inline float ImTrunc64(float f) { return (float)(ImS64)(f); }
static inline float ImRound64(float f) { return (float)(ImS64)(f + 0.5f); }
static inline int ImModPositive(int a, int b) { return (a + b) % b; }
static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; }
static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); }
static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; }
static inline float ImLinearRemapClamp(float s0, float s1, float d0, float d1, float x) { return ImSaturate((x - s0) / (s1 - s0)) * (d1 - d0) + d0; }
static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); }
static inline bool ImIsFloatAboveGuaranteedIntegerPrecision(float f) { return f <= -16777216 || f >= 16777216; }
static inline float ImExponentialMovingAverage(float avg, float sample, int n) { avg -= avg / n; avg += sample / n; return avg; }
inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x < rhs.x ? lhs.x : rhs.x, lhs.y < rhs.y ? lhs.y : rhs.y); }
inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x >= rhs.x ? lhs.x : rhs.x, lhs.y >= rhs.y ? lhs.y : rhs.y); }
inline ImVec2 ImClamp(const ImVec2& v, const ImVec2&mn, const ImVec2&mx){ return ImVec2((v.x < mn.x) ? mn.x : (v.x > mx.x) ? mx.x : v.x, (v.y < mn.y) ? mn.y : (v.y > mx.y) ? mx.y : v.y); }
inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, float t) { return ImVec2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t); }
inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); }
inline ImVec4 ImLerp(const ImVec4& a, const ImVec4& b, float t) { return ImVec4(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t); }
inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; }
inline float ImLengthSqr(const ImVec2& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y); }
inline float ImLengthSqr(const ImVec4& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); }
inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; }
inline float ImTrunc(float f) { return (float)(int)(f); }
inline ImVec2 ImTrunc(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); }
inline float ImFloor(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf()
inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2(ImFloor(v.x), ImFloor(v.y)); }
inline float ImTrunc64(float f) { return (float)(ImS64)(f); }
inline float ImRound64(float f) { return (float)(ImS64)(f + 0.5f); }
inline int ImModPositive(int a, int b) { return (a + b) % b; }
inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; }
inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); }
inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; }
inline float ImLinearRemapClamp(float s0, float s1, float d0, float d1, float x) { return ImSaturate((x - s0) / (s1 - s0)) * (d1 - d0) + d0; }
inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); }
inline bool ImIsFloatAboveGuaranteedIntegerPrecision(float f) { return f <= -16777216 || f >= 16777216; }
inline float ImExponentialMovingAverage(float avg, float sample, int n){ avg -= avg / n; avg += sample / n; return avg; }
IM_MSVC_RUNTIME_CHECKS_RESTORE
// Helpers: Geometry
@@ -590,6 +605,7 @@ struct IMGUI_API ImRect
void Floor() { Min.x = IM_TRUNC(Min.x); Min.y = IM_TRUNC(Min.y); Max.x = IM_TRUNC(Max.x); Max.y = IM_TRUNC(Max.y); }
bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; }
ImVec4 ToVec4() const { return ImVec4(Min.x, Min.y, Max.x, Max.y); }
const ImVec4& AsVec4() const { return *(const ImVec4*)&Min.x; }
};
// Helper: ImBitArray
@@ -788,13 +804,13 @@ struct ImChunkStream
// Maintain a line index for a text buffer. This is a strong candidate to be moved into the public API.
struct ImGuiTextIndex
{
ImVector<int> LineOffsets;
ImVector<int> Offsets;
int EndOffset = 0; // Because we don't own text buffer we need to maintain EndOffset (may bake in LineOffsets?)
void clear() { LineOffsets.clear(); EndOffset = 0; }
int size() { return LineOffsets.Size; }
const char* get_line_begin(const char* base, int n) { return base + LineOffsets[n]; }
const char* get_line_end(const char* base, int n) { return base + (n + 1 < LineOffsets.Size ? (LineOffsets[n + 1] - 1) : EndOffset); }
void clear() { Offsets.clear(); EndOffset = 0; }
int size() { return Offsets.Size; }
const char* get_line_begin(const char* base, int n) { return base + (Offsets.Size != 0 ? Offsets[n] : 0); }
const char* get_line_end(const char* base, int n) { return base + (n + 1 < Offsets.Size ? (Offsets[n + 1] - 1) : EndOffset); }
void append(const char* base, int old_size, int new_size);
};
@@ -1050,7 +1066,6 @@ enum ImGuiSelectableFlagsPrivate_
{
// NB: need to be in sync with last value of ImGuiSelectableFlags_
ImGuiSelectableFlags_NoHoldingActiveID = 1 << 20,
ImGuiSelectableFlags_SelectOnNav = 1 << 21, // (WIP) Auto-select when moved into. This is not exposed in public API as to handle multi-select and modifiers we will need user to explicitly control focus scope. May be replaced with a BeginSelection() API.
ImGuiSelectableFlags_SelectOnClick = 1 << 22, // Override button behavior to react on Click (default is Click+Release)
ImGuiSelectableFlags_SelectOnRelease = 1 << 23, // Override button behavior to react on Release (default is Click+Release)
ImGuiSelectableFlags_SpanAvailWidth = 1 << 24, // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus)
@@ -1158,6 +1173,7 @@ struct IMGUI_API ImGuiGroupData
ImVec2 BackupCurrLineSize;
float BackupCurrLineTextBaseOffset;
ImGuiID BackupActiveIdIsAlive;
bool BackupActiveIdHasBeenEditedThisFrame;
bool BackupDeactivatedIdIsAlive;
bool BackupHoveredIdIsAlive;
bool BackupIsSameLine;
@@ -1218,11 +1234,15 @@ struct IMGUI_API ImGuiInputTextState
ImVector<char> CallbackTextBackup; // temporary storage for callback to support automatic reconcile of undo-stack
int BufCapacity; // end-user buffer capacity (include zero terminator)
ImVec2 Scroll; // horizontal offset (managed manually) + vertical scrolling (pulled from child window's own Scroll.y)
int LineCount; // last line count (solely for debugging)
float WrapWidth; // word-wrapping width
float CursorAnim; // timer for cursor blink, reset on every user action so the cursor reappears immediately
bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!)
bool CursorCenterY; // set when we want scrolling to be centered over the cursor position (while resizing a word-wrapping field)
bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection
bool Edited; // edited this frame
bool WantReloadUserBuf; // force a reload of user buf so it may be modified externally. may be automatic in future version.
ImS8 LastMoveDirectionLR; // ImGuiDir_Left or ImGuiDir_Right. track last movement direction so when cursor cross over a word-wrapping boundaries we can display it on either line depending on last move.s
int ReloadSelectionStart;
int ReloadSelectionEnd;
@@ -1232,6 +1252,7 @@ struct IMGUI_API ImGuiInputTextState
void ClearFreeMemory() { TextA.clear(); TextToRevertTo.clear(); }
void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation
void OnCharPressed(unsigned int c);
float GetPreferredOffsetX() const;
// Cursor & Selection
void CursorAnimReset();
@@ -1262,6 +1283,12 @@ enum ImGuiWindowRefreshFlags_
// Refresh policy/frequency, Load Balancing etc.
};
enum ImGuiWindowBgClickFlags_
{
ImGuiWindowBgClickFlags_None = 0,
ImGuiWindowBgClickFlags_Move = 1 << 0, // Click on bg/void + drag to move window. Cleared by default when using io.ConfigWindowsMoveFromTitleBarOnly.
};
enum ImGuiNextWindowDataFlags_
{
ImGuiNextWindowDataFlags_None = 0,
@@ -1487,7 +1514,7 @@ enum ImGuiInputEventType
ImGuiInputEventType_COUNT
};
enum ImGuiInputSource
enum ImGuiInputSource : int
{
ImGuiInputSource_None = 0,
ImGuiInputSource_Mouse, // Note: may be Mouse or TouchScreen or Pen. See io.MouseSource to distinguish them.
@@ -1536,12 +1563,12 @@ struct ImGuiKeyRoutingData
{
ImGuiKeyRoutingIndex NextEntryIndex;
ImU16 Mods; // Technically we'd only need 4-bits but for simplify we store ImGuiMod_ values which need 16-bits.
ImU8 RoutingCurrScore; // [DEBUG] For debug display
ImU8 RoutingNextScore; // Lower is better (0: perfect score)
ImU16 RoutingCurrScore; // [DEBUG] For debug display
ImU16 RoutingNextScore; // Lower is better (0: perfect score)
ImGuiID RoutingCurr;
ImGuiID RoutingNext;
ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingCurrScore = RoutingNextScore = 255; RoutingCurr = RoutingNext = ImGuiKeyOwner_NoOwner; }
ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingCurrScore = RoutingNextScore = 0; RoutingCurr = RoutingNext = ImGuiKeyOwner_NoOwner; }
};
// Routing table: maintain a desired owner for each possible key-chord (key + mods), and setup owner in NewFrame() when mods are matching.
@@ -1650,8 +1677,9 @@ enum ImGuiActivateFlags_
ImGuiActivateFlags_PreferInput = 1 << 0, // Favor activation that requires keyboard text input (e.g. for Slider/Drag). Default for Enter key.
ImGuiActivateFlags_PreferTweak = 1 << 1, // Favor activation for tweaking with arrows or gamepad (e.g. for Slider/Drag). Default for Space key and if keyboard is not used.
ImGuiActivateFlags_TryToPreserveState = 1 << 2, // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection)
ImGuiActivateFlags_FromTabbing = 1 << 3, // Activation requested by a tabbing request
ImGuiActivateFlags_FromTabbing = 1 << 3, // Activation requested by a tabbing request (ImGuiNavMoveFlags_IsTabbing)
ImGuiActivateFlags_FromShortcut = 1 << 4, // Activation requested by an item shortcut via SetNextItemShortcut() function.
ImGuiActivateFlags_FromFocusApi = 1 << 5, // Activation requested by an api request (ImGuiNavMoveFlags_FocusApi)
};
// Early work-in-progress API for ScrollToItem()
@@ -1694,7 +1722,7 @@ enum ImGuiNavMoveFlags_
ImGuiNavMoveFlags_WrapMask_ = ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_WrapY,
ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place)
ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown)
ImGuiNavMoveFlags_ScrollToEdgeY = 1 << 6, // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword, probably unnecessary
ImGuiNavMoveFlags_ScrollToEdgeY = 1 << 6, // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword as ImGuiScrollFlags
ImGuiNavMoveFlags_Forwarded = 1 << 7,
ImGuiNavMoveFlags_DebugNoResult = 1 << 8, // Dummy scoring for debug purpose, don't apply result
ImGuiNavMoveFlags_FocusApi = 1 << 9, // Requests from focus API can land/focus/activate items even if they are marked with _NoTabStop (see NavProcessItemForTabbingRequest() for details)
@@ -2092,26 +2120,36 @@ struct ImGuiMetricsConfig
struct ImGuiStackLevelInfo
{
ImGuiID ID;
ImS8 QueryFrameCount; // >= 1: Query in progress
bool QuerySuccess; // Obtained result from DebugHookIdInfo()
ImGuiDataType DataType : 8;
char Desc[57]; // Arbitrarily sized buffer to hold a result (FIXME: could replace Results[] with a chunk stream?) FIXME: Now that we added CTRL+C this should be fixed.
ImS8 QueryFrameCount; // >= 1: Sub-query in progress
bool QuerySuccess; // Sub-query obtained result from DebugHookIdInfo()
ImS8 DataType; // ImGuiDataType
int DescOffset; // -1 or offset into parent's ResultsPathsBuf
ImGuiStackLevelInfo() { memset(this, 0, sizeof(*this)); }
ImGuiStackLevelInfo() { memset(this, 0, sizeof(*this)); DataType = -1; DescOffset = -1; }
};
struct ImGuiDebugItemPathQuery
{
ImGuiID MainID; // ID to query details for.
bool Active; // Used to disambiguate the case when ID == 0 and e.g. some code calls PushOverrideID(0).
bool Complete; // All sub-queries are finished (some may have failed).
ImS8 Step; // -1: query stack + init Results, >= 0: filling individual stack level.
ImVector<ImGuiStackLevelInfo> Results;
ImGuiTextBuffer ResultsDescBuf;
ImGuiTextBuffer ResultPathBuf;
ImGuiDebugItemPathQuery() { memset(this, 0, sizeof(*this)); }
};
// State for ID Stack tool queries
struct ImGuiIDStackTool
{
bool OptHexEncodeNonAsciiChars;
bool OptCopyToClipboardOnCtrlC;
int LastActiveFrame;
int StackLevel; // -1: query stack and resize Results, >= 0: individual stack level
ImGuiID QueryId; // ID to query details for
ImVector<ImGuiStackLevelInfo> Results;
bool CopyToClipboardOnCtrlC;
float CopyToClipboardLastTime;
ImGuiTextBuffer ResultPathBuf;
ImGuiIDStackTool() { memset(this, 0, sizeof(*this)); CopyToClipboardLastTime = -FLT_MAX; }
ImGuiIDStackTool() { memset(this, 0, sizeof(*this)); LastActiveFrame = -1; OptHexEncodeNonAsciiChars = true; CopyToClipboardLastTime = -FLT_MAX; }
};
//-----------------------------------------------------------------------------
@@ -2139,6 +2177,14 @@ struct ImGuiContextHook
struct ImGuiContext
{
bool Initialized;
bool WithinFrameScope; // Set by NewFrame(), cleared by EndFrame()
bool WithinFrameScopeWithImplicitWindow; // Set by NewFrame(), cleared by EndFrame() when the implicit debug window has been pushed
bool TestEngineHookItems; // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log()
int FrameCount;
int FrameCountEnded;
int FrameCountRendered;
double Time;
char ContextName[16]; // Storage for a context name (to facilitate debugging multi-context setups)
ImGuiIO IO;
ImGuiPlatformIO PlatformIO;
ImGuiStyle Style;
@@ -2151,17 +2197,8 @@ struct ImGuiContext
float FontRasterizerDensity; // Current font density. Used by all calls to GetFontBaked().
float CurrentDpiScale; // Current window/viewport DpiScale == CurrentViewport->DpiScale
ImDrawListSharedData DrawListSharedData;
double Time;
int FrameCount;
int FrameCountEnded;
int FrameCountRendered;
ImGuiID WithinEndChildID; // Set within EndChild()
bool WithinFrameScope; // Set by NewFrame(), cleared by EndFrame()
bool WithinFrameScopeWithImplicitWindow; // Set by NewFrame(), cleared by EndFrame() when the implicit debug window has been pushed
bool GcCompactAll; // Request full GC
bool TestEngineHookItems; // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log()
void* TestEngine; // Test engine user data
char ContextName[16]; // Storage for a context name (to facilitate debugging multi-context setups)
// Inputs
ImVector<ImGuiInputEvent> InputEventsQueue; // Input events which will be trickled/written into IO structure.
@@ -2193,7 +2230,7 @@ struct ImGuiContext
// Item/widgets state and tracking information
ImGuiID DebugDrawIdConflictsId; // Set when we detect multiple items with the same identifier
ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line]
ImGuiID DebugHookIdInfoId; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line]
ImGuiID HoveredId; // Hovered widget, filled during the frame
ImGuiID HoveredIdPreviousFrame;
int HoveredIdPreviousFrameItemCount; // Count numbers of items using the same ID as last frame's hovered id
@@ -2212,10 +2249,11 @@ struct ImGuiContext
bool ActiveIdHasBeenEditedBefore; // Was the value associated to the widget Edited over the course of the Active state.
bool ActiveIdHasBeenEditedThisFrame;
bool ActiveIdFromShortcut;
int ActiveIdMouseButton : 8;
ImS8 ActiveIdMouseButton;
ImGuiID ActiveIdDisabledId; // When clicking a disabled item we set ActiveId=window->MoveId to avoid interference with widget code. Actual item ID is stored here.
ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior)
ImGuiWindow* ActiveIdWindow;
ImGuiInputSource ActiveIdSource; // Activating source: ImGuiInputSource_Mouse OR ImGuiInputSource_Keyboard OR ImGuiInputSource_Gamepad
ImGuiWindow* ActiveIdWindow;
ImGuiID ActiveIdPreviousFrame;
ImGuiDeactivatedItemData DeactivatedItemData;
ImGuiDataTypeStorage ActiveIdValueOnActivation; // Backup of initial value at the time of activation. ONLY SET BY SPECIFIC WIDGETS: DragXXX and SliderXXX.
@@ -2245,6 +2283,7 @@ struct ImGuiContext
ImGuiLastItemData LastItemData; // Storage for last submitted item (setup by ItemAdd)
ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions
bool DebugShowGroupRects;
bool GcCompactAll; // Request full GC
// Shared stacks
ImGuiCol DebugFlashStyleColorIdx; // (Keep close to ColorStack to share cache line)
@@ -2281,7 +2320,7 @@ struct ImGuiContext
float NavHighlightActivatedTimer;
ImGuiID NavNextActivateId; // Set by ActivateItemByID(), queued until next frame.
ImGuiActivateFlags NavNextActivateFlags;
ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Mouse
ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Gamepad
ImGuiSelectionUserData NavLastValidSelectionUserData; // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data.
ImS8 NavCursorHideFrames;
//ImGuiID NavActivateInputId; // Removed in 1.89.4 (July 2023). This is now part of g.NavActivateId and sets g.NavActivateFlags |= ImGuiActivateFlags_PreferInput. See commit c9a53aa74, issue #5606.
@@ -2318,23 +2357,23 @@ struct ImGuiContext
bool NavJustMovedToIsTabbing; // Copy of ImGuiNavMoveFlags_IsTabbing. Maybe we should store whole flags.
bool NavJustMovedToHasSelectionData; // Copy of move result's ItemFlags & ImGuiItemFlags_HasSelectionUserData). Maybe we should just store ImGuiNavItemData.
// Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize)
bool ConfigNavWindowingWithGamepad; // = true. Enable CTRL+TAB by holding ImGuiKey_GamepadFaceLeft (== ImGuiKey_NavGamepadMenu). When false, the button may still be used to toggle Menu layer.
// Navigation: Windowing (Ctrl+Tab for list, or Menu button + keys or directional pads to move/resize)
bool ConfigNavWindowingWithGamepad; // = true. Enable Ctrl+Tab by holding ImGuiKey_GamepadFaceLeft (== ImGuiKey_NavGamepadMenu). When false, the button may still be used to toggle Menu layer.
ImGuiKeyChord ConfigNavWindowingKeyNext; // = ImGuiMod_Ctrl | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiKey_Tab on OS X). For reconfiguration (see #4828)
ImGuiKeyChord ConfigNavWindowingKeyPrev; // = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab on OS X)
ImGuiWindow* NavWindowingTarget; // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most!
ImGuiWindow* NavWindowingTarget; // Target window when doing Ctrl+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most!
ImGuiWindow* NavWindowingTargetAnim; // Record of last valid NavWindowingTarget until DimBgRatio and NavWindowingHighlightAlpha becomes 0.0f, so the fade-out can stay on it.
ImGuiWindow* NavWindowingListWindow; // Internal window actually listing the CTRL+Tab contents
ImGuiWindow* NavWindowingListWindow; // Internal window actually listing the Ctrl+Tab contents
float NavWindowingTimer;
float NavWindowingHighlightAlpha;
ImGuiInputSource NavWindowingInputSource;
bool NavWindowingToggleLayer;
ImGuiKey NavWindowingToggleKey;
bool NavWindowingToggleLayer; // Set while Alt or GamepadMenu is held, may be cleared by other operations, and processed when releasing the key.
ImGuiKey NavWindowingToggleKey; // Keyboard/gamepad key used when toggling to menu layer.
ImVec2 NavWindowingAccumDeltaPos;
ImVec2 NavWindowingAccumDeltaSize;
// Render
float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list)
float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and Ctrl+Tab list)
// Drag and Drop
bool DragDropActive;
@@ -2347,7 +2386,9 @@ struct ImGuiContext
ImRect DragDropTargetRect; // Store rectangle of current target candidate (we favor small targets when overlapping)
ImRect DragDropTargetClipRect; // Store ClipRect at the time of item's drawing
ImGuiID DragDropTargetId;
ImGuiDragDropFlags DragDropAcceptFlags;
ImGuiID DragDropTargetFullViewport;
ImGuiDragDropFlags DragDropAcceptFlagsCurr;
ImGuiDragDropFlags DragDropAcceptFlagsPrev;
float DragDropAcceptIdCurrRectSurface; // Target item surface (we resolve overlapping targets by prioritizing the smaller surface)
ImGuiID DragDropAcceptIdCurr; // Target item id (set at the time of accepting the payload)
ImGuiID DragDropAcceptIdPrev; // Target item id from previous frame (we need to store this to allow for overlapping drag and drop targets)
@@ -2397,10 +2438,11 @@ struct ImGuiContext
// Widget state
ImGuiInputTextState InputTextState;
ImGuiTextIndex InputTextLineIndex; // Temporary storage
ImGuiInputTextDeactivatedState InputTextDeactivatedState;
ImFontBaked InputTextPasswordFontBackupBaked;
ImFontFlags InputTextPasswordFontBackupFlags;
ImGuiID TempInputId; // Temporary text input when CTRL+clicking on a slider, etc.
ImGuiID TempInputId; // Temporary text input when using Ctrl+Click on a slider, etc.
ImGuiDataTypeStorage DataTypeZeroValue; // 0 for all data types
int BeginMenuDepth;
int BeginComboDepth;
@@ -2453,14 +2495,14 @@ struct ImGuiContext
// Capture/Logging
bool LogEnabled; // Currently capturing
bool LogLineFirstItem;
ImGuiLogFlags LogFlags; // Capture flags/type
ImGuiWindow* LogWindow;
ImFileHandle LogFile; // If != NULL log to stdout/ file
ImGuiTextBuffer LogBuffer; // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators.
const char* LogNextPrefix;
const char* LogNextPrefix; // See comment in LogSetNextTextDecoration(): doesn't copy underlying data, use carefully!
const char* LogNextSuffix;
float LogLinePosY;
bool LogLineFirstItem;
int LogDepthRef;
int LogDepthToExpand;
int LogDepthToExpandDefault; // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call.
@@ -2476,7 +2518,7 @@ struct ImGuiContext
// Debug Tools
// (some of the highly frequently used data are interleaved in other structures above: DebugBreakXXX fields, DebugHookIdInfo, DebugLocateId etc.)
int DebugDrawIdConflictsCount; // Locked count (preserved when holding CTRL)
int DebugDrawIdConflictsCount; // Locked count (preserved when holding Ctrl)
ImGuiDebugLogFlags DebugLogFlags;
ImGuiTextBuffer DebugLogBuf;
ImGuiTextIndex DebugLogIndex;
@@ -2493,6 +2535,7 @@ struct ImGuiContext
float DebugFlashStyleColorTime;
ImVec4 DebugFlashStyleColorBackup;
ImGuiMetricsConfig DebugMetricsConfig;
ImGuiDebugItemPathQuery DebugItemPathQuery;
ImGuiIDStackTool DebugIDStackTool;
ImGuiDebugAllocInfo DebugAllocInfo;
#if defined(IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS) && !defined(IMGUI_DISABLE_DEBUG_TOOLS)
@@ -2512,6 +2555,7 @@ struct ImGuiContext
char TempKeychordName[64];
ImGuiContext(ImFontAtlas* shared_font_atlas);
~ImGuiContext();
};
//-----------------------------------------------------------------------------
@@ -2629,13 +2673,14 @@ struct IMGUI_API ImGuiWindow
short BeginOrderWithinParent; // Begin() order within immediate parent window, if we are a child window. Otherwise 0.
short BeginOrderWithinContext; // Begin() order within entire imgui context. This is mostly used for debugging submission order related issues.
short FocusOrder; // Order within WindowsFocusOrder[], altered when windows are focused.
ImGuiDir AutoPosLastDirection;
ImS8 AutoFitFramesX, AutoFitFramesY;
bool AutoFitOnlyGrows;
ImGuiDir AutoPosLastDirection;
ImS8 HiddenFramesCanSkipItems; // Hide the window for N frames
ImS8 HiddenFramesCannotSkipItems; // Hide the window for N frames while allowing items to be submitted so we can measure their size
ImS8 HiddenFramesForRenderOnly; // Hide the window until frame N at Render() time only
ImS8 DisableInputsFrames; // Disable window interactions for N frames
ImGuiWindowBgClickFlags BgClickFlags : 8; // Configure behavior of click+dragging on window bg/void or over items. Default sets by io.ConfigWindowsMoveFromTitleBarOnly. If you use this please report in #3379.
ImGuiCond SetWindowPosAllowFlags : 8; // store acceptable condition flags for SetNextWindowPos() use.
ImGuiCond SetWindowSizeAllowFlags : 8; // store acceptable condition flags for SetNextWindowSize() use.
ImGuiCond SetWindowCollapsedAllowFlags : 8; // store acceptable condition flags for SetNextWindowCollapsed() use.
@@ -2675,7 +2720,7 @@ struct IMGUI_API ImGuiWindow
ImGuiWindow* RootWindowPopupTree; // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child.
ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active.
ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag.
ImGuiWindow* ParentWindowForFocusRoute; // Set to manual link a window to its logical parent so that Shortcut() chain are honoerd (e.g. Tool linked to Document)
ImGuiWindow* ParentWindowForFocusRoute; // Set to manual link a window to its logical parent so that Shortcut() chain are honored (e.g. Tool linked to Document)
ImGuiWindow* NavLastChildNavWindow; // When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.)
ImGuiID NavLastIds[ImGuiNavLayer_COUNT]; // Last known NavId for this window, per layer (0/1)
@@ -2702,7 +2747,7 @@ public:
ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight)); }
ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight; return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight); }
// [Obsolete] ImGuiWindow::CalcFontSize() was removed in 1.92.x because error-prone/misleading. You can use window->FontRefSize for a copy of g.FontSize at the time of the last Begin() call for this window.
// [OBSOLETE] ImGuiWindow::CalcFontSize() was removed in 1.92.0 because error-prone/misleading. You can use window->FontRefSize for a copy of g.FontSize at the time of the last Begin() call for this window.
//float CalcFontSize() const { ImGuiContext& g = *Ctx; return g.FontSizeBase * FontWindowScale * FontWindowScaleParents;
};
@@ -2737,7 +2782,7 @@ struct ImGuiTabItem
int LastFrameSelected; // This allows us to infer an ordered list of the last activated tabs with little maintenance
float Offset; // Position relative to beginning of tab
float Width; // Width currently displayed
float ContentWidth; // Width of label, stored during BeginTabItem() call
float ContentWidth; // Width of label + padding, stored during BeginTabItem() call (misnamed as "Content" would normally imply width of label only)
float RequestedWidth; // Width optionally requested by caller, -1.0f is unused
ImS32 NameOffset; // When Window==NULL, offset to name within parent ImGuiTabBar::TabsNames
ImS16 BeginOrder; // BeginTabItem() order, used to re-order tabs after toggling ImGuiTabBarFlags_Reorderable
@@ -2756,10 +2801,11 @@ struct IMGUI_API ImGuiTabBar
ImGuiID ID; // Zero for tab-bars used by docking
ImGuiID SelectedTabId; // Selected tab/window
ImGuiID NextSelectedTabId; // Next selected tab/window. Will also trigger a scrolling animation
ImGuiID VisibleTabId; // Can occasionally be != SelectedTabId (e.g. when previewing contents for CTRL+TAB preview)
ImGuiID VisibleTabId; // Can occasionally be != SelectedTabId (e.g. when previewing contents for Ctrl+Tab preview)
int CurrFrameVisible;
int PrevFrameVisible;
ImRect BarRect;
float BarRectPrevWidth; // Backup of previous width. When width change we enforce keep horizontal scroll on focused tab.
float CurrTabsContentsHeight;
float PrevTabsContentsHeight; // Record the height of contents submitted below the tab bar
float WidthAllTabs; // Actual width of all tabs (locked during layout)
@@ -2778,6 +2824,7 @@ struct IMGUI_API ImGuiTabBar
bool WantLayout;
bool VisibleTabWasSubmitted;
bool TabsAddedNew; // Set to true when a new tab item or button has been added to the tab bar during last frame
bool ScrollButtonEnabled;
ImS16 TabsActiveCount; // Number of tabs submitted this frame.
ImS16 LastTabItemIdx; // Index of last BeginTabItem() tab for use by EndTabItem()
float ItemSpacingY;
@@ -2994,7 +3041,7 @@ struct IMGUI_API ImGuiTable
bool IsContextPopupOpen; // Set when default context menu is open (also see: ContextPopupColumn, InstanceInteracted).
bool DisableDefaultContextMenu; // Disable default context menu. You may submit your own using TableBeginContextMenuPopup()/EndPopup()
bool IsSettingsRequestLoad;
bool IsSettingsDirty; // Set when table settings have changed and needs to be reported into ImGuiTableSetttings data.
bool IsSettingsDirty; // Set when table settings have changed and needs to be reported into ImGuiTableSettings data.
bool IsDefaultDisplayOrder; // Set when display order is unchanged from default (DisplayOrder contains 0...Count-1)
bool IsResetAllRequest;
bool IsResetDisplayOrderRequest;
@@ -3018,6 +3065,7 @@ struct IMGUI_API ImGuiTable
// sizeof() ~ 136 bytes.
struct IMGUI_API ImGuiTableTempData
{
ImGuiID WindowID; // Shortcut to g.Tables[TableIndex]->OuterWindow->ID.
int TableIndex; // Index in g.Tables.Buf[] pool
float LastTimeActive; // Last timestamp this structure was used
float AngledHeadersExtraWidth; // Used in EndTable()
@@ -3098,6 +3146,7 @@ namespace ImGui
IMGUI_API void UpdateWindowSkipRefresh(ImGuiWindow* window);
IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window);
IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy);
IMGUI_API bool IsWindowInBeginStack(ImGuiWindow* window);
IMGUI_API bool IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent);
IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below);
IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window);
@@ -3126,7 +3175,7 @@ namespace ImGui
IMGUI_API void SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags);
// Fonts, drawing
IMGUI_API void RegisterUserTexture(ImTextureData* tex); // Register external texture
IMGUI_API void RegisterUserTexture(ImTextureData* tex); // Register external texture. EXPERIMENTAL: DO NOT USE YET.
IMGUI_API void UnregisterUserTexture(ImTextureData* tex);
IMGUI_API void RegisterFontAtlas(ImFontAtlas* atlas);
IMGUI_API void UnregisterFontAtlas(ImFontAtlas* atlas);
@@ -3152,6 +3201,7 @@ namespace ImGui
IMGUI_API void UpdateHoveredWindowAndCaptureFlags(const ImVec2& mouse_pos);
IMGUI_API void FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window);
IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window);
IMGUI_API void StopMouseMovingWindow();
IMGUI_API void UpdateMouseMovingWindowNewFrame();
IMGUI_API void UpdateMouseMovingWindowEndFrame();
@@ -3223,7 +3273,8 @@ namespace ImGui
IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h);
IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x);
IMGUI_API void PushMultiItemsWidths(int components, float width_full);
IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess);
IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess, float width_min);
IMGUI_API void CalcClipRectVisibleItemsY(const ImRect& clip_rect, const ImVec2& pos, float items_height, int* out_visible_start, int* out_visible_end);
// Parameter stacks (shared)
IMGUI_API const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx);
@@ -3351,7 +3402,7 @@ namespace ImGui
// Legacy functions use ImGuiKeyOwner_Any meaning that they typically ignore ownership, unless a call to SetKeyOwner() explicitly used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease.
// - Binding generators may want to ignore those for now, or suffix them with Ex() until we decide if this gets moved into public API.
IMGUI_API bool IsKeyDown(ImGuiKey key, ImGuiID owner_id);
IMGUI_API bool IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0); // Important: when transitioning from old to new IsKeyPressed(): old API has "bool repeat = true", so would default to repeat. New API requiress explicit ImGuiInputFlags_Repeat.
IMGUI_API bool IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0); // Important: when transitioning from old to new IsKeyPressed(): old API has "bool repeat = true", so would default to repeat. New API requires explicit ImGuiInputFlags_Repeat.
IMGUI_API bool IsKeyReleased(ImGuiKey key, ImGuiID owner_id);
IMGUI_API bool IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id = 0);
IMGUI_API bool IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id);
@@ -3393,9 +3444,11 @@ namespace ImGui
// Drag and Drop
IMGUI_API bool IsDragDropActive();
IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id);
IMGUI_API bool BeginDragDropTargetViewport(ImGuiViewport* viewport, const ImRect* p_bb = NULL);
IMGUI_API void ClearDragDrop();
IMGUI_API bool IsDragDropPayloadBeingAccepted();
IMGUI_API void RenderDragDropTargetRect(const ImRect& bb, const ImRect& item_clip_rect);
IMGUI_API void RenderDragDropTargetRectForItem(const ImRect& bb);
IMGUI_API void RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb);
// Typing-Select API
// (provide Windows Explorer style "select items by typing partial name" + "cycle through items by typing same letter" feature)
@@ -3489,6 +3542,8 @@ namespace ImGui
// Tab Bars
inline ImGuiTabBar* GetCurrentTabBar() { ImGuiContext& g = *GImGui; return g.CurrentTabBar; }
IMGUI_API ImGuiTabBar* TabBarFindByID(ImGuiID id);
IMGUI_API void TabBarRemove(ImGuiTabBar* tab_bar);
IMGUI_API bool BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags);
IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id);
IMGUI_API ImGuiTabItem* TabBarFindTabByOrder(ImGuiTabBar* tab_bar, int order);
@@ -3673,7 +3728,6 @@ namespace ImGui
//inline bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0) { return TreeNodeUpdateNextOpen(id, flags); } // Renamed in 1.89
//inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // Removed in 1.87: Mapping from named key is always identity!
// Refactored focus/nav/tabbing system in 1.82 and 1.84. If you have old/custom copy-and-pasted widgets which used FocusableItemRegister():
// Refactored focus/nav/tabbing system in 1.82 and 1.84. If you have old/custom copy-and-pasted widgets which used FocusableItemRegister():
// (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool tab_focused = FocusableItemRegister(...)'
// (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool tab_focused = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0'
@@ -3731,12 +3785,12 @@ inline bool operator==(const ImTextureRef& lhs, const ImTextureRef& rhs) { re
inline bool operator!=(const ImTextureRef& lhs, const ImTextureRef& rhs) { return lhs._TexID != rhs._TexID || lhs._TexData != rhs._TexData; }
// Refer to ImFontAtlasPackGetRect() to better understand how this works.
#define ImFontAtlasRectId_IndexMask_ (0x000FFFFF) // 20-bits: index to access builder->RectsIndex[].
#define ImFontAtlasRectId_IndexMask_ (0x0007FFFF) // 20-bits signed: index to access builder->RectsIndex[].
#define ImFontAtlasRectId_GenerationMask_ (0x3FF00000) // 10-bits: entry generation, so each ID is unique and get can safely detected old identifiers.
#define ImFontAtlasRectId_GenerationShift_ (20)
inline int ImFontAtlasRectId_GetIndex(ImFontAtlasRectId id) { return id & ImFontAtlasRectId_IndexMask_; }
inline int ImFontAtlasRectId_GetGeneration(ImFontAtlasRectId id) { return (id & ImFontAtlasRectId_GenerationMask_) >> ImFontAtlasRectId_GenerationShift_; }
inline ImFontAtlasRectId ImFontAtlasRectId_Make(int index_idx, int gen_idx) { IM_ASSERT(index_idx < ImFontAtlasRectId_IndexMask_ && gen_idx < (ImFontAtlasRectId_GenerationMask_ >> ImFontAtlasRectId_GenerationShift_)); return (ImFontAtlasRectId)(index_idx | (gen_idx << ImFontAtlasRectId_GenerationShift_)); }
inline int ImFontAtlasRectId_GetIndex(ImFontAtlasRectId id) { return (id & ImFontAtlasRectId_IndexMask_); }
inline unsigned int ImFontAtlasRectId_GetGeneration(ImFontAtlasRectId id) { return (unsigned int)(id & ImFontAtlasRectId_GenerationMask_) >> ImFontAtlasRectId_GenerationShift_; }
inline ImFontAtlasRectId ImFontAtlasRectId_Make(int index_idx, int gen_idx) { IM_ASSERT(index_idx >= 0 && index_idx <= ImFontAtlasRectId_IndexMask_ && gen_idx <= (ImFontAtlasRectId_GenerationMask_ >> ImFontAtlasRectId_GenerationShift_)); return (ImFontAtlasRectId)(index_idx | (gen_idx << ImFontAtlasRectId_GenerationShift_)); }
// Packed rectangle lookup entry (we need an indirection to allow removing/reordering rectangles)
// User are returned ImFontAtlasRectId values which are meant to be persistent.
@@ -3746,7 +3800,7 @@ inline ImFontAtlasRectId ImFontAtlasRectId_Make(int index_idx, int gen_idx)
struct ImFontAtlasRectEntry
{
int TargetIndex : 20; // When Used: ImFontAtlasRectId -> into Rects[]. When unused: index to next unused RectsIndex[] slot to consume free-list.
int Generation : 10; // Increased each time the entry is reused for a new rectangle.
unsigned int Generation : 10; // Increased each time the entry is reused for a new rectangle.
unsigned int IsUsed : 1;
};
@@ -3812,6 +3866,7 @@ IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildDestroy(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildMain(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildSetupFontLoader(ImFontAtlas* atlas, const ImFontLoader* font_loader);
IMGUI_API void ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font, ImFont* new_font);
IMGUI_API void ImFontAtlasBuildUpdatePointers(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildRenderBitmapFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char);
IMGUI_API void ImFontAtlasBuildClear(ImFontAtlas* atlas); // Clear output and custom rects
@@ -3889,7 +3944,7 @@ extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiI
#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional)
#define IMGUI_TEST_ENGINE_LOG(_FMT,...) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log
#else
#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) ((void)0)
#define IMGUI_TEST_ENGINE_ITEM_ADD(_ID,_BB,_ITEM_DATA) ((void)0)
#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)g)
#endif
+13 -1
View File
@@ -1,10 +1,22 @@
// dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.)
// This is also an example of how you may wrap your own similar types.
// TL;DR; this is using the ImGuiInputTextFlags_CallbackResize facility,
// which also demonstrated in 'Dear ImGui Demo->Widgets->Text Input->Resize Callback'.
// Changelog:
// - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string
// See more C++ related extension (fmt, RAII, syntaxis sugar) on Wiki:
// Usage:
// {
// #include "misc/cpp/imgui_stdlib.h"
// #include "misc/cpp/imgui_stdlib.cpp" // <-- If you want to include implementation without messing with your project/build.
// [...]
// std::string my_string;
// ImGui::InputText("my string", &my_string);
// }
// See more C++ related extension (fmt, RAII, syntactic sugar) on Wiki:
// https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness
#include "imgui.h"
+12
View File
@@ -1,9 +1,21 @@
// dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.)
// This is also an example of how you may wrap your own similar types.
// TL;DR; this is using the ImGuiInputTextFlags_CallbackResize facility,
// which also demonstrated in 'Dear ImGui Demo->Widgets->Text Input->Resize Callback'.
// Changelog:
// - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string
// Usage:
// {
// #include "misc/cpp/imgui_stdlib.h"
// #include "misc/cpp/imgui_stdlib.cpp" // <-- If you want to include implementation without messing with your project/build.
// [...]
// std::string my_string;
// ImGui::InputText("my string", &my_string);
// }
// See more C++ related extension (fmt, RAII, syntaxis sugar) on Wiki:
// https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness
Regular → Executable
+34 -24
View File
@@ -1,4 +1,4 @@
// dear imgui, v1.92.1
// dear imgui, v1.92.5
// (tables and columns code)
/*
@@ -24,9 +24,9 @@ Index of this file:
*/
// Navigating this file:
// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
// - In Visual Studio: Ctrl+Comma ("Edit.GoToAll") can follow symbols inside comments, whereas Ctrl+F12 ("Edit.GoToImplementation") cannot.
// - In Visual Studio w/ Visual Assist installed: Alt+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
// - In VS Code, CLion, etc.: Ctrl+Click can follow symbols inside comments.
//-----------------------------------------------------------------------------
// [SECTION] Commentary
@@ -437,7 +437,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
if (table->InnerWindow->SkipItems && outer_window_is_measuring_size)
table->InnerWindow->SkipItems = false;
// When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned)
// When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned
if (instance_no == 0)
{
table->HasScrollbarYPrev = table->HasScrollbarYCurr;
@@ -451,7 +451,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
// But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable().
table->WorkRect = table->OuterRect = table->InnerRect = outer_rect;
table->HasScrollbarYPrev = table->HasScrollbarYCurr = false;
table->InnerWindow->DC.TreeDepth++; // This is designed to always linking ImGuiTreeNodeFlags_DrawLines linking accross a table
table->InnerWindow->DC.TreeDepth++; // This is designed to always linking ImGuiTreeNodeFlags_DrawLines linking across a table
}
// Push a standardized ID for both child-using and not-child-using tables
@@ -464,6 +464,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
table->HostIndentX = inner_window->DC.Indent.x;
table->HostClipRect = inner_window->ClipRect;
table->HostSkipItems = inner_window->SkipItems;
temp_data->WindowID = inner_window->ID;
temp_data->HostBackupWorkRect = inner_window->WorkRect;
temp_data->HostBackupParentWorkRect = inner_window->ParentWorkRect;
temp_data->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset;
@@ -542,7 +543,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
// Make table current
g.CurrentTable = table;
outer_window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX();
inner_window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX();
outer_window->DC.CurrentTableIdx = table_idx;
if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly.
inner_window->DC.CurrentTableIdx = table_idx;
@@ -946,7 +947,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// (e.g. TextWrapped) too much. Otherwise what tends to happen is that TextWrapped would output a very
// large height (= first frame scrollbar display very off + clipper would skip lots of items).
// This is merely making the side-effect less extreme, but doesn't properly fixes it.
// FIXME: Move this to ->WidthGiven to avoid temporary lossyless?
// FIXME: Move this to ->WidthGiven to avoid temporary lossyness?
// FIXME: This break IsPreserveWidthAuto from not flickering if the stored WidthAuto was smaller.
if (column->AutoFitQueue > 0x01 && table->IsInitializing && !column->IsPreserveWidthAuto)
column->WidthRequest = ImMax(column->WidthRequest, table->MinColumnWidth * 4.0f); // FIXME-TABLE: Another constant/scale?
@@ -1190,7 +1191,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
}
// In case the table is visible (e.g. decorations) but all columns clipped, we keep a column visible.
// Else if give no chance to a clipper-savy user to submit rows and therefore total contents height used by scrollbar.
// Else if give no chance to a clipper-savvy user to submit rows and therefore total contents height used by scrollbar.
if (has_at_least_one_column_requesting_output == false)
{
table->Columns[table->LeftMostEnabledColumn].IsRequestOutput = true;
@@ -1366,7 +1367,7 @@ void ImGui::EndTable()
ImGuiWindow* inner_window = table->InnerWindow;
ImGuiWindow* outer_window = table->OuterWindow;
ImGuiTableTempData* temp_data = table->TempData;
IM_ASSERT(inner_window == g.CurrentWindow);
IM_ASSERT(inner_window == g.CurrentWindow && inner_window->ID == temp_data->WindowID);
IM_ASSERT(outer_window == inner_window || outer_window == inner_window->ParentWindow);
if (table->IsInsideRow)
@@ -1559,7 +1560,7 @@ void ImGui::EndTable()
IM_ASSERT(g.CurrentWindow == outer_window && g.CurrentTable == table);
IM_ASSERT(g.TablesTempDataStacked > 0);
temp_data = (--g.TablesTempDataStacked > 0) ? &g.TablesTempData[g.TablesTempDataStacked - 1] : NULL;
g.CurrentTable = temp_data ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL;
g.CurrentTable = temp_data && (temp_data->WindowID == outer_window->ID) ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL;
if (g.CurrentTable)
{
g.CurrentTable->TempData = temp_data;
@@ -1825,6 +1826,11 @@ void ImGui::TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
IM_ASSERT(target != ImGuiTableBgTarget_None);
if (table == NULL)
{
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
return;
}
if (color == IM_COL32_DISABLE)
color = 0;
@@ -2049,10 +2055,11 @@ void ImGui::TableEndRow(ImGuiTable* table)
}
// End frozen rows (when we are past the last frozen row line, teleport cursor and alter clipping rectangle)
// We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark end of row and
// get the new cursor position.
// - We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark
// end of row and get the new cursor position.
if (unfreeze_rows_request)
{
IM_ASSERT(table->FreezeRowsRequest > 0);
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
table->Columns[column_n].NavLayerCurrent = table->NavLayer;
const float y0 = ImMax(table->RowPosY2 + 1, table->InnerClipRect.Min.y);
@@ -2519,7 +2526,7 @@ void ImGui::TablePopColumnChannel()
// - NoClip --> 2+D+1 channels: bg0/1 + bg2 + foreground (same clip rect == always 1 draw call)
// - Clip --> 2+D+N channels
// - FreezeRows --> 2+D+N*2 (unless scrolling value is zero)
// - FreezeRows || FreezeColunns --> 3+D+N*2 (unless scrolling value is zero)
// - FreezeRows || FreezeColumns --> 3+D+N*2 (unless scrolling value is zero)
// Where D is 1 if any column is clipped or hidden (dummy channel) otherwise 0.
void ImGui::TableSetupDrawChannels(ImGuiTable* table)
{
@@ -2812,8 +2819,13 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
continue;
// Draw in outer window so right-most column won't be clipped
// Always draw full height border when being resized/hovered, or on the delimitation of frozen column scrolling.
float draw_y2 = (is_hovered || is_resized || is_frozen_separator || (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)) == 0) ? draw_y2_body : draw_y2_head;
float draw_y2 = draw_y2_head;
if (is_frozen_separator)
draw_y2 = draw_y2_body;
else if ((table->Flags & ImGuiTableFlags_NoBordersInBodyUntilResize) != 0 && (is_hovered || is_resized))
draw_y2 = draw_y2_body;
else if ((table->Flags & (ImGuiTableFlags_NoBordersInBodyUntilResize | ImGuiTableFlags_NoBordersInBody)) == 0)
draw_y2 = draw_y2_body;
if (draw_y2 > draw_y1)
inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), TableGetColumnBorderCol(table, order_n, column_n), border_size);
}
@@ -2876,9 +2888,7 @@ ImGuiTableSortSpecs* ImGui::TableGetSortSpecs()
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL);
if (!(table->Flags & ImGuiTableFlags_Sortable))
if (table == NULL || !(table->Flags & ImGuiTableFlags_Sortable))
return NULL;
// Require layout (in case TableHeadersRow() hasn't been called) as it may alter IsSortSpecsDirty in some paths.
@@ -3411,7 +3421,7 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
// Left<>Right alignment
float line_off_curr_x = flip_label ? (label_lines - 1) * line_off_step_x : 0.0f;
float line_off_for_align_x = ImMax((((column->MaxX - column->MinX) - padding.x * 2.0f) - (label_lines * line_off_step_x)), 0.0f) * align.x;
float line_off_for_align_x = ImFloor(ImMax((((column->MaxX - column->MinX) - padding.x * 2.0f) - (label_lines * line_off_step_x)), 0.0f) * align.x);
line_off_curr_x += line_off_for_align_x - line_off_for_ascent_x;
// Register header width
@@ -3939,7 +3949,7 @@ void ImGui::TableSettingsAddSettingsHandler()
// - TableGcCompactSettings() [Internal]
//-------------------------------------------------------------------------
// Remove Table (currently only used by TestEngine)
// Remove Table data (currently only used by TestEngine)
void ImGui::TableRemove(ImGuiTable* table)
{
//IMGUI_DEBUG_PRINT("TableRemove() id=0x%08X\n", table->ID);
@@ -4018,9 +4028,9 @@ void ImGui::DebugNodeTable(ImGuiTable* table)
bool open = TreeNode(table, "Table 0x%08X (%d columns, in '%s')%s", table->ID, table->ColumnsCount, table->OuterWindow->Name, is_active ? "" : " *Inactive*");
if (!is_active) { PopStyleColor(); }
if (IsItemHovered())
GetForegroundDrawList()->AddRect(table->OuterRect.Min, table->OuterRect.Max, IM_COL32(255, 255, 0, 255));
GetForegroundDrawList(table->OuterWindow)->AddRect(table->OuterRect.Min, table->OuterRect.Max, IM_COL32(255, 255, 0, 255));
if (IsItemVisible() && table->HoveredColumnBody != -1)
GetForegroundDrawList()->AddRect(GetItemRectMin(), GetItemRectMax(), IM_COL32(255, 255, 0, 255));
GetForegroundDrawList(table->OuterWindow)->AddRect(GetItemRectMin(), GetItemRectMax(), IM_COL32(255, 255, 0, 255));
if (!open)
return;
if (table->InstanceCurrent > 0)
@@ -4074,7 +4084,7 @@ void ImGui::DebugNodeTable(ImGuiTable* table)
if (IsItemHovered())
{
ImRect r(column->MinX, table->OuterRect.Min.y, column->MaxX, table->OuterRect.Max.y);
GetForegroundDrawList()->AddRect(r.Min, r.Max, IM_COL32(255, 255, 0, 255));
GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min, r.Max, IM_COL32(255, 255, 0, 255));
}
}
if (ImGuiTableSettings* settings = TableGetBoundSettings(table))
File diff suppressed because it is too large. Load diff
Regular → Executable
View File
File mode changed.
Regular → Executable
+75 -36
View File
@@ -5,7 +5,8 @@
// - 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.
// - Changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion.
// Grep for [DEAR IMGUI] to find some 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
@@ -39,6 +40,7 @@
//
// VERSION HISTORY
//
// !!!! (2025-10-23) changed STB_TEXTEDIT_INSERTCHARS() to return inserted count (instead of 0/1 bool), allowing partial insertion.
// 1.14 (2021-07-11) page up/down, various fixes
// 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
@@ -147,7 +149,8 @@
// as manually wordwrapping for end-of-line positioning
//
// STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i
// STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*)
// STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) try to insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*)
// returns number of characters actually inserted. [DEAR IMGUI]
//
// STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key
//
@@ -181,10 +184,10 @@
//
// To support UTF-8:
//
// STB_TEXTEDIT_GETPREVCHARINDEX returns index of previous character
// STB_TEXTEDIT_GETNEXTCHARINDEX returns index of next character
// STB_TEXTEDIT_GETPREVCHARINDEX returns index of previous character
// STB_TEXTEDIT_GETNEXTCHARINDEX returns index of next character
// Do NOT define STB_TEXTEDIT_KEYTOTEXT.
// Instead, call stb_textedit_text() directly for text contents.
// Instead, call stb_textedit_text() directly for text contents.
//
// Keyboard input must be encoded as a single integer value; e.g. a character code
// and some bitflags that represent shift states. to simplify the interface, SHIFT must
@@ -260,7 +263,7 @@
//
// text: (added 2025)
// call this to directly send text input the textfield, which is required
// for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT()
// for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT()
// cannot infer text length.
//
//
@@ -427,7 +430,7 @@ typedef struct
//
// traverse the layout to locate the nearest character to a display position
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y, int* out_side_on_line)
{
StbTexteditRow r;
int n = STB_TEXTEDIT_STRINGLEN(str);
@@ -437,6 +440,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
r.x0 = r.x1 = 0;
r.ymin = r.ymax = 0;
r.num_chars = 0;
*out_side_on_line = 0;
// search rows to find one that straddles 'y'
while (i < n) {
@@ -456,7 +460,10 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
// below all text, return 'after' last character
if (i >= n)
{
*out_side_on_line = 1;
return n;
}
// check if it's before the beginning of the line
if (x < r.x0)
@@ -469,6 +476,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
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) {
*out_side_on_line = (k == 0) ? 0 : 1;
if (x < prev_x+w/2)
return k+i;
else
@@ -480,6 +488,7 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
}
// if the last character is a newline, return that. otherwise return 'after' the last character
*out_side_on_line = 1;
if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE)
return i+r.num_chars-1;
else
@@ -491,6 +500,7 @@ static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *st
{
// 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
int side_on_line;
if( state->single_line )
{
StbTexteditRow r;
@@ -498,16 +508,18 @@ static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *st
y = r.ymin;
}
state->cursor = stb_text_locate_coord(str, x, y);
state->cursor = stb_text_locate_coord(str, x, y, &side_on_line);
state->select_start = state->cursor;
state->select_end = state->cursor;
state->has_preferred_x = 0;
str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left);
}
// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
{
int p = 0;
int side_on_line;
// 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
@@ -521,8 +533,9 @@ static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *sta
if (state->select_start == state->select_end)
state->select_start = state->cursor;
p = stb_text_locate_coord(str, x, y);
p = stb_text_locate_coord(str, x, y, &side_on_line);
state->cursor = state->select_end = p;
str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left);
}
/////////////////////////////////////////////////////////////////////////////
@@ -572,6 +585,8 @@ static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING
STB_TEXTEDIT_LAYOUTROW(&r, str, i);
if (n < i + r.num_chars)
break;
if (str->LastMoveDirectionLR == ImGuiDir_Right && str->Stb->cursor > 0 && str->Stb->cursor == i + r.num_chars && STB_TEXTEDIT_GETCHAR(str, i + r.num_chars - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] Wrapping point handling
break;
if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] special handling for last line
break; // [DEAR IMGUI]
prev_start = i;
@@ -668,6 +683,35 @@ static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditSt
}
}
// [DEAR IMGUI] Extracted this function so we can more easily add support for word-wrapping.
#ifndef STB_TEXTEDIT_MOVELINESTART
static int stb_textedit_move_line_start(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor)
{
if (state->single_line)
return 0;
while (cursor > 0) {
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, cursor);
if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
break;
cursor = prev;
}
return cursor;
}
#define STB_TEXTEDIT_MOVELINESTART stb_textedit_move_line_start
#endif
#ifndef STB_TEXTEDIT_MOVELINEEND
static int stb_textedit_move_line_end(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor)
{
int n = STB_TEXTEDIT_STRINGLEN(str);
if (state->single_line)
return n;
while (cursor < n && STB_TEXTEDIT_GETCHAR(str, cursor) != STB_TEXTEDIT_NEWLINE)
cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, cursor);
return cursor;
}
#define STB_TEXTEDIT_MOVELINEEND stb_textedit_move_line_end
#endif
#ifdef STB_TEXTEDIT_IS_SPACE
static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
{
@@ -734,7 +778,8 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS
stb_textedit_clamp(str, state);
stb_textedit_delete_selection(str,state);
// try to insert the characters
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) {
len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len);
if (len) {
stb_text_makeundo_insert(state, state->cursor, len);
state->cursor += len;
state->has_preferred_x = 0;
@@ -759,13 +804,15 @@ static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* sta
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)) {
text_len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len);
if (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)) {
text_len = STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len);
if (text_len) {
stb_text_makeundo_insert(state, state->cursor, text_len);
state->cursor += text_len;
state->has_preferred_x = 0;
@@ -921,8 +968,8 @@ retry:
// [DEAR IMGUI]
// going down while being on the last line shouldn't bring us to that line end
if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE)
break;
//if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE)
// break;
// now find character position down a row
state->cursor = start;
@@ -943,6 +990,8 @@ retry:
}
stb_textedit_clamp(str, state);
if (state->cursor == find.first_char + find.length)
str->LastMoveDirectionLR = ImGuiDir_Left;
state->has_preferred_x = 1;
state->preferred_x = goal_x;
@@ -1007,6 +1056,10 @@ retry:
}
stb_textedit_clamp(str, state);
if (state->cursor == find.first_char)
str->LastMoveDirectionLR = ImGuiDir_Right;
else if (state->cursor == find.prev_first)
str->LastMoveDirectionLR = ImGuiDir_Left;
state->has_preferred_x = 1;
state->preferred_x = goal_x;
@@ -1024,7 +1077,7 @@ retry:
prev_scan = prev;
}
find.first_char = find.prev_first;
find.prev_first = prev_scan;
find.prev_first = STB_TEXTEDIT_MOVELINESTART(str, state, prev_scan);
}
break;
}
@@ -1098,10 +1151,7 @@ retry:
case STB_TEXTEDIT_K_LINESTART:
stb_textedit_clamp(str, state);
stb_textedit_move_to_first(state);
if (state->single_line)
state->cursor = 0;
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);
state->has_preferred_x = 0;
break;
@@ -1109,13 +1159,9 @@ retry:
case STB_TEXTEDIT_K_LINEEND2:
#endif
case STB_TEXTEDIT_K_LINEEND: {
int n = STB_TEXTEDIT_STRINGLEN(str);
stb_textedit_clamp(str, state);
stb_textedit_move_to_first(state);
if (state->single_line)
state->cursor = n;
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
stb_textedit_move_to_last(str, state);
state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);
state->has_preferred_x = 0;
break;
}
@@ -1126,10 +1172,7 @@ retry:
case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT:
stb_textedit_clamp(str, state);
stb_textedit_prep_selection_at_cursor(state);
if (state->single_line)
state->cursor = 0;
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);
state->select_end = state->cursor;
state->has_preferred_x = 0;
break;
@@ -1138,13 +1181,9 @@ retry:
case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT:
#endif
case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: {
int n = STB_TEXTEDIT_STRINGLEN(str);
stb_textedit_clamp(str, state);
stb_textedit_prep_selection_at_cursor(state);
if (state->single_line)
state->cursor = n;
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);
state->select_end = state->cursor;
state->has_preferred_x = 0;
break;
@@ -1319,7 +1358,7 @@ static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
// check type of recorded action:
if (u.insert_length) {
// easy case: was a deletion, so we need to insert n characters
STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length);
u.insert_length = STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length);
s->undo_char_point -= u.insert_length;
}
@@ -1370,7 +1409,7 @@ static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
if (r.insert_length) {
// easy case: need to insert n characters
STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length);
r.insert_length = STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length);
s->redo_char_point += r.insert_length;
}
Regular → Executable
View File
File mode changed.
+41 -41
View File
@@ -50,9 +50,7 @@
#endif
#include "hw/sh4/sh4_interpreter.h"
#include "hw/sh4/dyna/ngen.h"
#ifdef USE_DREAMLINK_DEVICES
#include "sdl/dreamlink.h"
#endif
#include "oslib/i18n.h"
settings_t settings;
constexpr char const *BIOS_TITLE = "Dreamcast BIOS";
@@ -587,7 +585,7 @@ void Emulator::loadGame(const char *path, LoadProgress *progress)
{
// Boot BIOS
if (!nvmem::loadFiles())
throw FlycastException("No BIOS file found in " + hostfs::getFlashSavePath("", ""));
throw FlycastException(strprintf(i18n::T("No BIOS file found in %s"), hostfs::getFlashSavePath("", "").c_str()));
gdr::initDrive("");
}
else
@@ -603,7 +601,7 @@ void Emulator::loadGame(const char *path, LoadProgress *progress)
nvmem::loadHle();
NOTICE_LOG(BOOT, "Did not load BIOS, using reios");
if (!config::UseReios && config::UseReios.isReadOnly())
os_notify("This game requires a real BIOS", 15000);
os_notify(i18n::T("This game requires a real BIOS"), 15000);
}
}
else
@@ -611,7 +609,7 @@ void Emulator::loadGame(const char *path, LoadProgress *progress)
// Content load failed. Boot the BIOS
settings.content.path.clear();
if (!nvmem::loadFiles())
throw FlycastException("This media cannot be loaded");
throw FlycastException(i18n::Ts("This media cannot be loaded"));
gdr::initDrive("");
}
}
@@ -638,11 +636,6 @@ void Emulator::loadGame(const char *path, LoadProgress *progress)
}
if (!settings.naomi.slave)
{
#ifdef USE_DREAMLINK_DEVICES
// Note: ordinarily we would like to use the 'events' system for this step.
// However, this call can change 'config::MapleExpansionDevices', and therefore needs to run before 'mcfg_CreateDevices'.
reconnectDreamLinks();
#endif
mcfg_DestroyDevices();
mcfg_CreateDevices();
if (settings.platform.isNaomi())
@@ -660,13 +653,14 @@ void Emulator::loadGame(const char *path, LoadProgress *progress)
cheatManager.reset(settings.content.gameId);
if (cheatManager.isWidescreen())
{
os_notify("Widescreen cheat activated", 2000);
os_notify(i18n::T("Widescreen cheat activated"), 2000);
config::ScreenStretching.override(134); // 4:3 -> 16:9
}
// reload settings so that all settings can be overridden
loadGameSpecificSettings();
NetworkHandshake::init();
settings.input.fastForwardMode = false;
EventManager::event(Event::Start);
if (!settings.content.path.empty())
{
#ifndef LIBRETRO
@@ -676,16 +670,15 @@ void Emulator::loadGame(const char *path, LoadProgress *progress)
dc_loadstate(config::SavestateSlot);
#endif
}
EventManager::event(Event::Start);
if (progress)
{
#ifdef GDB_SERVER
if(config::GDBWaitForConnection)
if (config::GDB && config::GDBWaitForConnection)
progress->label = "Waiting for debugger...";
else
#endif
progress->label = "Starting...";
progress->label = i18n::T("Starting...");
}
state = Loaded;
@@ -697,34 +690,40 @@ void Emulator::loadGame(const char *path, LoadProgress *progress)
void Emulator::runInternal()
{
if (singleStep)
{
getSh4Executor()->Step();
singleStep = false;
}
else if (stepRangeTo != 0)
{
while (Sh4cntx.pc >= stepRangeFrom && Sh4cntx.pc < stepRangeTo)
runner.init();
try {
if (singleStep)
{
getSh4Executor()->Step();
singleStep = false;
}
else if (stepRangeTo != 0)
{
while (Sh4cntx.pc >= stepRangeFrom && Sh4cntx.pc < stepRangeTo)
getSh4Executor()->Step();
stepRangeFrom = 0;
stepRangeTo = 0;
}
else
{
do {
resetRequested = false;
stepRangeFrom = 0;
stepRangeTo = 0;
}
else
{
do {
resetRequested = false;
getSh4Executor()->Run();
getSh4Executor()->Run();
if (resetRequested)
{
nvmem::saveFiles();
dc_reset(false);
if (!restartCpu())
resetRequested = false;
}
} while (resetRequested);
if (resetRequested)
{
nvmem::saveFiles();
dc_reset(false);
if (!restartCpu())
resetRequested = false;
}
} while (resetRequested);
}
} catch (...) {
runner.term();
throw;
}
}
@@ -955,13 +954,13 @@ void Emulator::run()
startTime = sh4_sched_now64();
renderTimeout = false;
if (!singleStep && stepRangeTo == 0)
getSh4Executor()->Start();
getSh4Executor()->Start();
try {
runInternal();
if (ggpo::active())
ggpo::nextFrame();
} catch (const std::exception& e) {
ERROR_LOG(COMMON, "Exception: %s\n", e.what());
ERROR_LOG(COMMON, "Exception: %s", e.what());
setNetworkState(false);
state = Error;
getSh4Executor()->Stop();
@@ -1078,6 +1077,7 @@ bool Emulator::render()
void Emulator::vblank()
{
EventManager::event(Event::VBlank);
runner.execTasks();
// Time out if a frame hasn't been rendered for 50 ms
if (sh4_sched_now64() - startTime <= 10000000)
return;
+7
View File
@@ -20,6 +20,7 @@
*/
#pragma once
#include "types.h"
#include "stdclass.h"
#include <atomic>
#include <future>
@@ -39,6 +40,7 @@ void dc_savestate(int index = 0, const u8 *pngData = nullptr, u32 pngSize = 0);
void dc_loadstate(int index = 0);
time_t dc_getStateCreationDate(int index);
void dc_getStateScreenshot(int index, std::vector<u8>& pngData);
bool dc_savestateAllowed();
enum class Event {
Start,
@@ -185,6 +187,10 @@ public:
Sh4Executor *getSh4Executor();
void run(std::function<void()> func) {
runner.runOnThread(func);
}
void dc_reset(bool hard); // for tests only
private:
@@ -212,6 +218,7 @@ private:
std::mutex mutex;
Sh4Executor *interpreter = nullptr;
Sh4Executor *recompiler = nullptr;
ThreadRunner runner;
};
extern Emulator emu;
+9
View File
@@ -6,6 +6,7 @@
#include "cfg/option.h"
#include "stdclass.h"
#include "serialize.h"
#include "input/maplelink.h"
MapleInputState mapleInputState[4];
extern bool maple_ddt_pending_reset;
@@ -214,6 +215,14 @@ bool maple_atomiswave_coin_chute(int slot)
static void mcfg_Create(MapleDeviceType type, u32 bus, u32 port, s32 player_num = -1)
{
MapleDevices[bus][port].reset();
if (type == MDT_SegaVMU)
{
MapleLink::Ptr link = MapleLink::GetMapleLink(bus, port);
if (link != nullptr && link->storageEnabled()) {
createMapleLinkVmu(bus, port);
return;
}
}
std::shared_ptr<maple_device> dev = maple_Create(type);
dev->Setup(bus, port, player_num);
}
+17
View File
@@ -117,6 +117,23 @@ void mcfg_DestroyDevices(bool full = true);
void mcfg_SerializeDevices(Serializer& ser);
void mcfg_DeserializeDevices(Deserializer& deser);
constexpr int maple_getPortCount(MapleDeviceType type)
{
switch (type)
{
case MDT_SegaController:
case MDT_SegaControllerXL:
return 2;
case MDT_LightGun:
case MDT_TwinStick:
case MDT_AsciiStick:
case MDT_RacingController:
return 1;
default:
return 0;
}
}
bool maple_atomiswave_coin_chute(int slot);
void push_vmu_screen(int bus_id, int bus_port, u8* buffer);
void insertRfidCard(int playerNum);
+134 -568
View File
@@ -6,8 +6,10 @@
#include "audio/audiostream.h"
#include "oslib/oslib.h"
#include "oslib/storage.h"
#include "oslib/i18n.h"
#include "hw/aica/sgc_if.h"
#include "cfg/option.h"
#include "input/maplelink.h"
#include <zlib.h>
#include <cerrno>
#include <ctime>
@@ -53,6 +55,15 @@ maple_device::~maple_device()
delete config;
}
bool maple_base::relayMapleLink()
{
auto link = MapleLink::GetMapleLink(bus_id, bus_port);
if (link == nullptr)
return true;
else
return link->send(*inMsg);
}
static inline void mutualExclusion(u32& keycode, u32 mask)
{
if ((keycode & mask) == 0)
@@ -366,11 +377,28 @@ struct maple_sega_vmu: maple_base
if (b != 0)
{
config->SetImage(lcd_data_decoded);
updateMapleLinkScreen();
break;
}
fullSaveNeeded = true;
}
void updateMapleLinkScreen()
{
auto link = MapleLink::GetMapleLink(bus_id, bus_port);
if (link == nullptr)
return;
MapleMsg msg;
msg.command = MDCF_BlockWrite;
msg.destAP = maple_port;
msg.originAP = bus_id << 6;
msg.pushData(MFID_2_LCD);
msg.pushData(0); // PT, phase, block#
msg.pushData(lcd_data);
link->send(msg);
}
virtual bool fullSave()
{
if (file == nullptr)
@@ -450,6 +478,8 @@ struct maple_sega_vmu: maple_base
{
if (file != nullptr)
std::fclose(file);
memset(lcd_data, 0, sizeof(lcd_data));
updateMapleLinkScreen();
}
u32 dma(u32 cmd) override
@@ -680,9 +710,6 @@ struct maple_sega_vmu: maple_base
return MDRE_FileError; // I/O error
}
}
else {
WARN_LOG(MAPLE, "Failed to save VMU %s data", logical_port);
}
return MDRS_DeviceReply;
}
@@ -709,6 +736,7 @@ struct maple_sega_vmu: maple_base
}
}
config->SetImage(lcd_data_decoded);
relayMapleLink();
return MDRS_DeviceReply;
}
@@ -743,14 +771,16 @@ struct maple_sega_vmu: maple_base
switch(r32())
{
case MFID_3_Clock:
{
u8 alw = r8();
u8 ald = r8();
r16(); // Alarm 2
INFO_LOG(MAPLE, "BEEP: %d/%d", alw, ald);
aica::sgc::vmuBeep(alw, ald);
}
{
u8 alw = r8();
u8 ald = r8();
r16(); // Alarm 2
INFO_LOG(MAPLE, "BEEP: %d/%d", alw, ald);
aica::sgc::vmuBeep(alw, ald);
relayMapleLink();
return MDRS_DeviceReply;
}
default:
INFO_LOG(MAPLE, "VMU: command MDCF_SetCondition -> Bad function used, returning MDRE_UnknownFunction");
@@ -1060,6 +1090,7 @@ struct maple_sega_purupuru : maple_base
//Auto-stop time
AST = dma_buffer_in[10];
AST_ms = AST * 250 + 250;
relayMapleLink();
return MDRS_DeviceReply;
@@ -1091,6 +1122,8 @@ struct maple_sega_purupuru : maple_base
else
inclination = FREQ / (1000.0 * INC * std::max(POW_POS, POW_NEG));
config->SetVibration(power, inclination, duration_ms);
relayMapleLink();
}
return MDRS_DeviceReply;
@@ -1715,7 +1748,7 @@ struct RFIDReaderWriter : maple_base
// Surprisingly recipient and sender aren't swapped in the response so we override RawDma for this reason
// vf4tuned and mushiking do care
u32 RawDma(u32* buffer_in, u32 buffer_in_len, u32* buffer_out) override
u32 RawDma(const u32 *buffer_in, u32 buffer_in_len, u32* buffer_out) override
{
u32 command=buffer_in[0] &0xFF;
//Recipient address
@@ -1838,7 +1871,7 @@ struct RFIDReaderWriter : maple_base
cardInserted = false;
w32(getStatus());
NOTICE_LOG(MAPLE, "RFID card %d unlocked", player_num);
os_notify("Card ejected", 2000);
os_notify(i18n::T("Card ejected"), 2000);
return (MapleDeviceRV)0xfe;
case 0xB1: // write to card
@@ -2109,86 +2142,74 @@ std::shared_ptr<maple_device> maple_Create(MapleDeviceType type)
return nullptr;
}
#if defined(USE_DREAMLINK_DEVICES)
#include "sdl/dreamlink.h"
#include <list>
#include <memory>
struct DreamLinkVmu : public maple_sega_vmu
struct MapleLinkVmu : public maple_sega_vmu
{
bool running = true;
std::shared_ptr<DreamLink> dreamlink;
bool useRealVmuMemory; //!< Set this to true to use physical VMU memory, false for virtual memory
std::chrono::time_point<std::chrono::system_clock> lastWriteTime;
bool mirroredBlocks[256]; //!< Set to true for block that has been loaded/written
s16 lastWriteBlock = -1;
std::list<u8> blocksToWrite;
std::mutex writeMutex;
std::condition_variable writeCv;
std::thread writeThread;
static u64 lastNotifyTime;
static u64 lastErrorNotifyTime;
DreamLinkVmu(std::shared_ptr<DreamLink> dreamlink) :
dreamlink(dreamlink),
writeThread([this](){writeEntrypoint();})
{
useRealVmuMemory = config::UsePhysicalVmuMemory && dreamlink->isPhysicalVMUMemorySupported();
}
virtual ~DreamLinkVmu() {
running = false;
// Entering lock context
{
std::unique_lock<std::mutex> lock(writeMutex);
writeCv.notify_all();
}
writeThread.join();
}
bool cachedBlocks[256]; //!< Set to true for block that has been loaded/written
void OnSetup() override
{
// All data must be re-read
memset(mirroredBlocks, 0, sizeof(mirroredBlocks));
if (useRealVmuMemory)
{
// Ensure file is not being used
if (file != nullptr)
{
std::fclose(file);
file = nullptr;
}
memset(flash_data, 0, sizeof(flash_data));
memset(lcd_data, 0, sizeof(lcd_data));
memset(cachedBlocks, 0, sizeof(cachedBlocks));
// Ensure file is not being used
if (file != nullptr) {
std::fclose(file);
file = nullptr;
}
else
{
maple_sega_vmu::OnSetup();
}
memset(flash_data, 0, sizeof(flash_data));
memset(lcd_data, 0, sizeof(lcd_data));
}
bool fullSave() override
{
if (useRealVmuMemory)
{
// Skip virtual save when using physical VMU
//DEBUG_LOG(MAPLE, "Not saving because this is a real vmu");
NOTICE_LOG(MAPLE, "Saving to physical VMU");
// Skip virtual save when using MapleLink VMU
DEBUG_LOG(MAPLE, "Full save ignored for MapleLink VMU");
return true;
}
return true;
void serialize(Serializer& ser) const override {
throw Serializer::Exception("Can't save linked VMU data");
}
void deserialize(Deserializer& deser) override
{
// Ignore the VMU data from the loaded state
u8 savedData[sizeof(flash_data)];
memcpy(savedData, flash_data, sizeof(savedData));
maple_sega_vmu::deserialize(deser);
memcpy(flash_data, savedData, sizeof(savedData));
}
MapleDeviceRV readBlock(unsigned block)
{
if (cachedBlocks[block])
return MDRS_JVSNone;
auto link = MapleLink::GetMapleLink(bus_id, bus_port);
if (link == nullptr) {
ERROR_LOG(MAPLE, "MapleLinkVmu[%s]::readBlock: MapleLink is null", logical_port);
return MDRS_JVSNone;
}
else
MapleMsg txMsg;
txMsg.command = MDCF_BlockRead;
txMsg.originAP = inMsg->originAP;
txMsg.destAP = inMsg->destAP;
txMsg.pushData(MFID_1_Storage);
txMsg.pushData<u32>(block << 24); // (BE) partition #, phase, block #
MapleMsg rxMsg;
if (link->sendReceive(txMsg, rxMsg) && rxMsg.size == 130)
{
return maple_sega_vmu::fullSave();
DEBUG_LOG(MAPLE, "MapleLinkVmu[%s]: read block %d", logical_port, block);
memcpy(&flash_data[block * 4 * 128], &rxMsg.data[8], 4 * 128);
cachedBlocks[block] = true;
}
else {
ERROR_LOG(MAPLE, "Failed to read VMU %s: I/O error", logical_port);
return MDRE_FileError; // I/O error
}
return MDRS_JVSNone;
}
u32 dma(u32 cmd) override
@@ -2196,513 +2217,58 @@ struct DreamLinkVmu : public maple_sega_vmu
// Physical VMU logic
if (dma_count_in >= 4)
{
const u32 functionId = *(u32*)dma_buffer_in;
const MapleMsg* msg = reinterpret_cast<const MapleMsg*>(dma_buffer_in - 4);
const u32 functionId = inMsg->readData<u32>(0);
if (functionId == MFID_1_Storage)
{
if (useRealVmuMemory)
switch (cmd)
{
const u64 currentTime = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count();
// Only show notification once every 6 seconds to avoid spam
if (cmd == MDCF_BlockRead &&
(currentTime - lastNotifyTime) > 4000 &&
(currentTime - lastErrorNotifyTime) > 4000)
{
// This is a read operation (loading)
os_notify("ATTENTION: Loading from a physical VMU", 6000,
"Game data is being loaded from your physical VMU");
lastNotifyTime = currentTime;
case MDCF_BlockWrite:
{
auto link = MapleLink::GetMapleLink(bus_id, bus_port);
if (link == nullptr) {
ERROR_LOG(MAPLE, "MapleLinkVmu[%s]::BlockWrite: MapleLink is null", logical_port);
return MDRE_FileError;
}
switch (cmd)
{
case MDCF_BlockWrite:
{
// Throw away function
r32();
// Save the write to RAM
u32 bph=r32();
u32 Block = lastWriteBlock = (SWAP32(bph))&0xffff;
u32 Phase = ((SWAP32(bph))>>16)&0xff;
u32 write_adr=Block*512+Phase*(512/4);
u32 write_len=r_count();
DEBUG_LOG(MAPLE, "VMU %s block write: Block %d Phase %d addr %x len %d", logical_port, Block, Phase, write_adr, write_len);
if (write_adr + write_len > sizeof(flash_data))
{
INFO_LOG(MAPLE, "Failed to write VMU %s: overflow", logical_port);
skip(write_len);
return MDRE_FileError; //invalid params
}
rptr(&flash_data[write_adr],write_len);
// All done - wait until GetLastError to queue the write
return MDRS_DeviceReply;
}
case MDCF_GetLastError:
{
mirroredBlocks[lastWriteBlock] = true;
// Entering lock context
{
std::unique_lock<std::mutex> lock(writeMutex);
if (std::find(blocksToWrite.begin(), blocksToWrite.end(), lastWriteBlock) == blocksToWrite.end())
{
blocksToWrite.push_back(lastWriteBlock);
writeCv.notify_all();
}
}
lastWriteTime = std::chrono::system_clock::now();
return MDRS_DeviceReply;
}
case MDCF_BlockRead:
{
u8 requestBlock = msg->data[7];
if (!mirroredBlocks[requestBlock]) {
// Try up to 4 times to read
bool readSuccess = false;
for (u32 i = 0; i < 4; ++i) {
if (i > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(30));
}
MapleMsg rcvMsg;
if (dreamlink->send(*msg, rcvMsg) && rcvMsg.size == 130) {
// Something read!
u8 block = rcvMsg.data[7];
memcpy(&flash_data[block * 4 * 128], &rcvMsg.data[8], 4 * 128);
mirroredBlocks[block] = true;
readSuccess = true;
break;
}
}
if (!readSuccess) {
ERROR_LOG(MAPLE, "Failed to read VMU %s: I/O error", logical_port);
return MDRE_FileError; // I/O error
}
}
break;
}
default:
// do nothing
break;
MapleMsg rxMsg;
if (!link->sendReceive(*inMsg, rxMsg)) {
ERROR_LOG(MAPLE, "Failed to write VMU %s: I/O error", logical_port);
return MDRE_FileError;
}
if (rxMsg.command != MDRS_DeviceReply)
return rxMsg.command;
cachedBlocks[inMsg->data[7]] = true;
DEBUG_LOG(MAPLE, "MapleLinkVmu[%s]: write block %d", logical_port, inMsg->data[7]);
break;
}
}
else if (functionId == MFID_2_LCD)
{
if (cmd == MDCF_BlockWrite)
case MDCF_BlockRead:
{
dreamlink->send(*msg);
u8 block = inMsg->data[7];
MapleDeviceRV rc = readBlock(block);
if (rc != MDRS_JVSNone)
return rc;
break;
}
}
else if (functionId == MFID_3_Clock)
{
if (cmd == MDCF_SetCondition)
{
dreamlink->send(*msg);
case MDCF_GetMediaInfo:
// block 255 contains the media info
readBlock(255);
break;
default:
// do nothing
break;
}
}
}
// If made it here, call base's dma to handle return value
return maple_sega_vmu::dma(cmd);
}
void copyIn(std::shared_ptr<maple_sega_vmu> other)
{
memcpy(flash_data, other->flash_data, sizeof(flash_data));
memcpy(lcd_data, other->lcd_data, sizeof(lcd_data));
memcpy(lcd_data_decoded, other->lcd_data_decoded, sizeof(lcd_data_decoded));
fullSaveNeeded = other->fullSaveNeeded;
}
void copyOut(std::shared_ptr<maple_sega_vmu> other)
{
// Never copy data to virtual VMU if physical VMU is enabled
if (!config::UsePhysicalVmuMemory && !useRealVmuMemory)
{
memcpy(other->flash_data, flash_data, sizeof(other->flash_data));
memcpy(other->lcd_data, lcd_data, sizeof(other->lcd_data));
memcpy(other->lcd_data_decoded, lcd_data_decoded, sizeof(other->lcd_data_decoded));
other->fullSaveNeeded = fullSaveNeeded;
}
}
void updateScreen()
{
MapleMsg msg;
msg.command = MDCF_BlockWrite;
msg.destAP = maple_port;
msg.originAP = bus_id << 6;
msg.size = 2 + sizeof(lcd_data) / 4;
*(u32*)&msg.data[0] = MFID_2_LCD;
*(u32*)&msg.data[4] = 0; // PT, phase, block#
memcpy(&msg.data[8], lcd_data, sizeof(lcd_data));
dreamlink->send(msg);
}
private:
//! Thread entrypoint for write operations
void writeEntrypoint()
{
while (true)
{
u8 block = 0;
// Entering lock context
{
std::unique_lock<std::mutex> lock(writeMutex);
writeCv.wait(lock, [this](){ return (!running || !blocksToWrite.empty()); });
if (!running)
{
break;
}
block = blocksToWrite.front();
blocksToWrite.pop_front();
}
const u64 currentTime = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count();
// Only show notification once every 6 seconds to avoid spam
if ((currentTime - lastNotifyTime) > 4000 && (currentTime - lastErrorNotifyTime) > 4000)
{
// This is a write operation (saving)
os_notify("ATTENTION: You are saving to a physical VMU", 6000,
"Do not disconnect the VMU or close the game");
lastNotifyTime = currentTime;
}
bool writeSuccess = true;
const u8* blockData = &flash_data[block * 4 * 128];
std::chrono::milliseconds delay(10);
const std::chrono::milliseconds delayInc(5);
// Try up to 4 times to write
for (u32 i = 0; i < 4; ++i) {
if (i > 0) {
// Slow down writes to avoid overloading the VMU
delay += delayInc;
}
writeSuccess = true;
// 4 write phases per block
for (u32 phase = 0; phase < 4; ++phase) {
MapleMsg writeMsg;
writeMsg.command = MDCF_BlockWrite;
writeMsg.destAP = (bus_id << 6) | (1 << bus_port);
writeMsg.originAP = (bus_id << 6);
writeMsg.size = 34;
writeMsg.setWord(MFID_1_Storage, 0);
const u32 locationWord = (block << 24) | (phase << 8);
writeMsg.setWord(locationWord, 1);
memcpy(&writeMsg.data[8], &blockData[phase * 128], 128);
// Delay before writing
std::this_thread::sleep_for(delay);
MapleMsg rcvMsg;
if (!dreamlink->send(writeMsg, rcvMsg) || rcvMsg.command != MDRS_DeviceReply) {
// Not acknowledged
writeSuccess = false;
break;
}
}
if (writeSuccess) {
// Delay before committing
std::this_thread::sleep_for(delay);
// Send the GetLastError command to commit the data
MapleMsg writeMsg;
writeMsg.command = MDCF_GetLastError;
writeMsg.destAP = (bus_id << 6) | (1 << bus_port);
writeMsg.originAP = (bus_id << 6);
writeMsg.size = 2;
writeMsg.setWord(MFID_1_Storage, 0);
const u32 phase = 4;
const u32 locationWord = (block << 24) | (phase << 8);
writeMsg.setWord(locationWord, 1);
MapleMsg rcvMsg;
if (dreamlink->send(writeMsg, rcvMsg) && rcvMsg.command == MDRS_DeviceReply) {
// Acknowledged
break;
}
}
// else: continue to retry
}
if (!writeSuccess) {
ERROR_LOG(MAPLE, "Failed to save VMU %s: I/O error", logical_port);
const u64 currentTime = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count();
if ((currentTime - lastErrorNotifyTime) > 4000)
{
os_notify("ATTENTION: Write to VMU failed", 6000);
lastErrorNotifyTime = currentTime;
}
}
}
}
};
u64 DreamLinkVmu::lastNotifyTime = 0;
u64 DreamLinkVmu::lastErrorNotifyTime = 0;
struct DreamLinkPurupuru : public maple_sega_purupuru
void createMapleLinkVmu(int bus, int port)
{
std::shared_ptr<DreamLink> dreamlink;
//! Number of consecutive stop conditions sent
u32 stopSendCount = 0;
DreamLinkPurupuru(std::shared_ptr<DreamLink> dreamlink) : dreamlink(dreamlink) {
}
u32 dma(u32 cmd) override
{
if (cmd == MDCF_BlockWrite || cmd == MDCF_SetCondition) {
const MapleMsg *msg = reinterpret_cast<const MapleMsg*>(dma_buffer_in - 4);
const u32 functionId = *(u32*)dma_buffer_in;
const u32 condition = *(u32*)(dma_buffer_in + 4);
if (functionId == MFID_8_Vibration && condition == 0x00000010) {
++stopSendCount;
} else {
stopSendCount = 0;
}
// Only send 2 consecutive stop commands; ignore the rest to avoid unnecessary communications
if (stopSendCount <= 2) {
dreamlink->send(*msg);
}
}
return maple_sega_purupuru::dma(cmd);
}
};
static std::list<std::shared_ptr<DreamLinkVmu>> dreamLinkVmus[2];
static std::list<std::shared_ptr<DreamLinkPurupuru>> dreamLinkPurupurus;
bool reconnectDreamLinks()
{
auto& useNetworkExpansionDevices = config::UseNetworkExpansionDevices;
bool anyNewConnection = false;
for (int i = 0; i < DreamLink::NUM_PORTS; i++)
{
const auto& dreamlink = DreamLink::activeDreamLinks[i];
if (!useNetworkExpansionDevices[i] && dreamlink && !dreamlink->isForPhysicalController())
{
// This bus is not using network expansion devices.
// Dispose of the dreamlink for the bus, unless it is for a physical controller (and therefore not managed by this setting).
dreamlink->disconnect();
DreamLink::activeDreamLinks[i] = nullptr;
}
if (dreamlink && !dreamlink->isConnected())
{
dreamlink->connect();
if (dreamlink->isConnected())
{
anyNewConnection = true;
}
}
}
return anyNewConnection;
INFO_LOG(MAPLE, "MapleLinkVmu created on %d,%d", bus, port);
auto vmu = std::make_shared<MapleLinkVmu>();
vmu->Setup(bus, port);
}
// Checks for and handles a message from server telling us to refresh the expansion devices.
void refreshDreamLinksIfNeeded()
{
bool anyNeedsRefresh = false;
for (auto& dreamlink : DreamLink::activeDreamLinks)
{
if (dreamlink && dreamlink->needsRefresh())
anyNeedsRefresh = true;
}
if (!anyNeedsRefresh)
return;
for (auto& dreamlink : DreamLink::activeDreamLinks)
{
if (dreamlink)
tearDownDreamLinkDevices(dreamlink.get());
}
maple_ReconnectDevices();
}
void createAllDreamLinkDevices()
{
for (int i = 0; i < DreamLink::NUM_PORTS; i++)
{
const auto& dreamlink = DreamLink::activeDreamLinks[i];
if (dreamlink && dreamlink->isConnected())
{
bool gameStart = false;
bool stateLoaded = false;
createDreamLinkDevices(dreamlink, gameStart, stateLoaded);
}
}
}
// Replaces certain ordinary MapleDevices entries with specialized devices that communicate thru a DreamLink.
void createDreamLinkDevices(std::shared_ptr<DreamLink> dreamlink, bool gameStart, bool stateLoaded)
{
const int bus = dreamlink->getBus();
for (int i = 0; i < 2; ++i)
{
std::shared_ptr<maple_device> dev = MapleDevices[bus][i];
if ((dreamlink->getFunctionCode(i + 1) & MFID_1_Storage))
{
bool vmuFound = false;
std::shared_ptr<DreamLinkVmu> vmu;
for (const std::shared_ptr<DreamLinkVmu>& vmuIter : dreamLinkVmus[i])
{
if (vmuIter->dreamlink.get() == dreamlink.get())
{
vmuFound = true;
vmu = vmuIter;
break;
}
}
if (gameStart || stateLoaded || !vmuFound)
{
if (!vmu)
{
vmu = std::make_shared<DreamLinkVmu>(dreamlink);
}
if (gameStart)
{
// Update useRealVmuMemory in case config changed
vmu->useRealVmuMemory = config::UsePhysicalVmuMemory && dreamlink->isPhysicalVMUMemorySupported();
}
else if (stateLoaded)
{
if (vmu->useRealVmuMemory)
{
// Disconnect from real VMU memory when a state is loaded
vmu->useRealVmuMemory = false;
os_notify("WARNING: Disconnected from physical VMU memory due to load state", 6000,
"Reconnect manually to resume using physical VMU memory");
}
}
vmu->Setup(bus, i);
if (!vmuFound && dev && dev->get_device_type() == MDT_SegaVMU && !vmu->useRealVmuMemory) {
// Only copy data from virtual VMU if Physical VMU Only is disabled
vmu->copyIn(std::static_pointer_cast<maple_sega_vmu>(dev));
if (!gameStart) {
vmu->updateScreen();
}
}
if (!vmuFound) {
dreamLinkVmus[i].push_back(vmu);
}
}
}
else if (i == 1 && ((dreamlink->getFunctionCode(i + 1) & MFID_8_Vibration)))
{
bool rumbleFound = false;
std::shared_ptr<DreamLinkPurupuru> rumble;
for (const std::shared_ptr<DreamLinkPurupuru>& purupuru : dreamLinkPurupurus)
{
if (purupuru->dreamlink.get() == dreamlink.get())
{
rumbleFound = true;
rumble = purupuru;
break;
}
}
if (gameStart || stateLoaded || !rumbleFound)
{
if (!rumble)
{
rumble = std::make_shared<DreamLinkPurupuru>(dreamlink);
}
rumble->Setup(bus, i);
if (!rumbleFound) dreamLinkPurupurus.push_back(rumble);
}
}
}
}
// Replaces DreamLink devices associated with 'dreamlink' with ordinary Maple devices.
void tearDownDreamLinkDevices(DreamLink* dreamlink)
{
const int bus = dreamlink->getBus();
for (int i = 0; i < 2; ++i)
{
for (std::list<std::shared_ptr<DreamLinkVmu>>::const_iterator iter = dreamLinkVmus[i].begin();
iter != dreamLinkVmus[i].end();)
{
if ((*iter)->dreamlink.get() == dreamlink)
{
DEBUG_LOG(MAPLE, "VMU teardown - Physical VMU: %s", (*iter)->useRealVmuMemory ? "true" : "false");
std::shared_ptr<maple_device> dev = maple_Create(MDT_SegaVMU);
dev->Setup(bus, 0);
if (!(*iter)->useRealVmuMemory)
{
(*iter)->copyOut(std::static_pointer_cast<maple_sega_vmu>(dev));
}
DEBUG_LOG(MAPLE, "VMU teardown - Copy completed");
iter = dreamLinkVmus[i].erase(iter);
break;
}
else
{
++iter;
}
}
}
std::size_t dreamLinkVmuCount = 0;
for (int i = 0; i < 2; ++i)
{
dreamLinkVmuCount += dreamLinkVmus[i].size();
}
for (std::list<std::shared_ptr<DreamLinkPurupuru>>::const_iterator iter = dreamLinkPurupurus.begin();
iter != dreamLinkPurupurus.end();)
{
if ((*iter)->dreamlink.get() == dreamlink)
{
std::shared_ptr<maple_device> dev = maple_Create(MDT_PurupuruPack);
dev->Setup(bus, 1);
iter = dreamLinkPurupurus.erase(iter);
break;
}
else
{
++iter;
}
}
}
#endif
+53 -11
View File
@@ -121,6 +121,42 @@ enum AWAVE_KEYS
AWAVE_TRIGGER_KEY = 1 << 17,
};
struct MapleMsg
{
u8 command = 0;
u8 destAP = 0;
u8 originAP = 0;
u8 size = 0;
u8 data[1020];
u32 getDataSize() const {
return size * 4;
}
template<typename T>
void pushData(const T& p) {
static_assert(sizeof(T) >= 4);
pushData(&p, sizeof(T));
}
void pushData(const void *p, u32 size)
{
u32 offset = this->size * 4;
memcpy(&data[offset], p, size);
offset += size;
this->size = (offset + 3) / 4;
}
template<typename T>
T readData(int index = 0) const
{
T v;
memcpy(&v, &data[index * sizeof(T)], sizeof(T));
return v;
}
};
static_assert(sizeof(MapleMsg) == 1024);
struct maple_device : public std::enable_shared_from_this<maple_device>
{
u8 maple_port; //raw maple port
@@ -136,7 +172,7 @@ struct maple_device : public std::enable_shared_from_this<maple_device>
virtual void OnSetup() {};
virtual ~maple_device();
virtual u32 RawDma(u32* buffer_in, u32 buffer_in_len, u32* buffer_out) = 0;
virtual u32 RawDma(const u32 *buffer_in, u32 buffer_in_len, u32 *buffer_out) = 0;
virtual void serialize(Serializer& ser) const {
ser << player_num;
@@ -181,7 +217,8 @@ struct maple_base: maple_device
u8* dma_buffer_out;
u32* dma_count_out;
u8* dma_buffer_in;
const MapleMsg *inMsg;
const u8* dma_buffer_in;
u32 dma_count_in;
void w8(u8 data) { *(u8*)dma_buffer_out = data; dma_buffer_out += 1; dma_count_out[0] += 1; }
@@ -207,9 +244,9 @@ struct maple_base: maple_device
w8(' ');
}
u8 r8() { u8 rv = *(u8*)dma_buffer_in; dma_buffer_in += 1; dma_count_in -= 1; return rv; }
u16 r16() { u16 rv = *(u16*)dma_buffer_in; dma_buffer_in += 2; dma_count_in -= 2; return rv; }
u32 r32() { u32 rv = *(u32*)dma_buffer_in; dma_buffer_in += 4; dma_count_in -= 4; return rv; }
u8 r8() { u8 rv = *dma_buffer_in; dma_buffer_in += 1; dma_count_in -= 1; return rv; }
u16 r16() { u16 rv = *(const u16 *)dma_buffer_in; dma_buffer_in += 2; dma_count_in -= 2; return rv; }
u32 r32() { u32 rv = *(const u32 *)dma_buffer_in; dma_buffer_in += 4; dma_count_in -= 4; return rv; }
void skip(u32 len) {
dma_buffer_in += len;
dma_count_in -= len;
@@ -223,20 +260,21 @@ struct maple_base: maple_device
}
u32 r_count() { return dma_count_in; }
u32 Dma(u32 Command, u32* buffer_in, u32 buffer_in_len, u32* buffer_out, u32& buffer_out_len)
u32 Dma(u32 Command, const u32* buffer_in, u32 buffer_in_len, u32* buffer_out, u32& buffer_out_len)
{
dma_buffer_out = (u8*)buffer_out;
dma_count_out = &buffer_out_len;
dma_buffer_in = (u8*)buffer_in;
dma_buffer_in = (const u8 *)buffer_in;
dma_count_in = buffer_in_len;
return dma(Command);
}
virtual u32 dma(u32 cmd) = 0;
u32 RawDma(u32* buffer_in, u32 buffer_in_len, u32* buffer_out) override
u32 RawDma(const u32* buffer_in, u32 buffer_in_len, u32* buffer_out) override
{
inMsg = (const MapleMsg *)buffer_in;
u32 command=buffer_in[0] &0xFF;
//Recipient address
u32 reci = (buffer_in[0] >> 8) & 0xFF;
@@ -253,8 +291,12 @@ struct maple_base: maple_device
return outlen + 4;
}
bool relayMapleLink();
};
void createMapleLinkVmu(int bus, int port);
class jvs_io_board;
struct maple_naomi_jamma : maple_base, SerialPort
@@ -284,13 +326,13 @@ struct maple_naomi_jamma : maple_base, SerialPort
return last_node ? 0x8E : 0x8F;
}
void send_jvs_message(u32 node_id, u32 channel, u32 length, u8 *data);
void send_jvs_messages(u32 node_id, u32 channel, bool use_repeat, u32 length, u8 *data, bool repeat_first);
void send_jvs_message(u32 node_id, u32 channel, u32 length, const u8 *data);
void send_jvs_messages(u32 node_id, u32 channel, bool use_repeat, u32 length, const u8 *data, bool repeat_first);
bool receive_jvs_messages(u32 channel);
void handle_86_subcommand();
u32 RawDma(u32* buffer_in, u32 buffer_in_len, u32* buffer_out) override;
u32 RawDma(const u32 *buffer_in, u32 buffer_in_len, u32 *buffer_out) override;
u32 dma(u32 cmd) override { return 0; }
void serialize(Serializer& ser) const override;
+4 -23
View File
@@ -8,10 +8,6 @@
#include "network/ggpo.h"
#include "hw/naomi/card_reader.h"
#ifdef USE_DREAMLINK_DEVICES
#include "sdl/dreamlink.h"
#endif
#include <memory>
enum MaplePattern
@@ -51,10 +47,6 @@ bool SDCKBOccupied;
void maple_vblank()
{
#if USE_DREAMLINK_DEVICES
refreshDreamLinksIfNeeded();
#endif
if (SB_MDEN & 1)
{
if (SB_MDTSEL == 1)
@@ -65,7 +57,7 @@ void maple_vblank()
}
else
{
DEBUG_LOG(MAPLE, "DDT vblank");
//DEBUG_LOG(MAPLE, "DDT vblank");
SB_MDST = 1;
maple_DoDma();
// if trigger reset is manual, mark it as pending
@@ -356,12 +348,10 @@ void maple_Init()
#endif
maple_schid = sh4_sched_register(0, maple_schd);
#if defined(USE_DREAMLINK_DEVICES)
registerDreamLinkEvents();
#endif
}
static u64 reconnect_time;
void maple_Reset(bool hard)
{
maple_ddt_pending_reset = false;
@@ -373,6 +363,7 @@ void maple_Reset(bool hard)
SB_MDAPRO = 0x00007F00;
SB_MMSEL = 1;
mapleDmaOut.clear();
reconnect_time = 0;
}
void maple_Term()
@@ -380,14 +371,8 @@ void maple_Term()
mcfg_DestroyDevices();
sh4_sched_unregister(maple_schid);
maple_schid = -1;
#if defined(USE_DREAMLINK_DEVICES)
unregisterDreamLinkEvents();
#endif
}
static u64 reconnect_time;
void maple_ReconnectDevices()
{
mcfg_DestroyDevices();
@@ -400,9 +385,5 @@ static void maple_handle_reconnect()
{
reconnect_time = 0;
mcfg_CreateDevices();
#if defined(USE_DREAMLINK_DEVICES)
createAllDreamLinkDevices();
#endif
}
}
+13 -9
View File
@@ -157,7 +157,7 @@ public:
}
virtual ~jvs_io_board() = default;
u32 handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_out);
u32 handle_jvs_message(const u8 *buffer_in, u32 length_in, u8 *buffer_out);
virtual void serialize(Serializer& ser) const;
virtual void deserialize(Deserializer& deser);
@@ -229,7 +229,7 @@ protected:
}
}
virtual void write_digital_out(int count, u8 *data)
virtual void write_digital_out(int count, const u8 *data)
{
u32 newOutput = digOutput;
for (int i = 0; i < count && i < 4; i++)
@@ -613,7 +613,7 @@ protected:
v[1] &= ~NAOMI_BTN2_KEY;
}
void write_digital_out(int count, u8 *data) override
void write_digital_out(int count, const u8 *data) override
{
if (count != 3)
return;
@@ -1382,7 +1382,7 @@ maple_naomi_jamma::~maple_naomi_jamma()
EEPROM = nullptr;
}
void maple_naomi_jamma::send_jvs_message(u32 node_id, u32 channel, u32 length, u8 *data)
void maple_naomi_jamma::send_jvs_message(u32 node_id, u32 channel, u32 length, const u8 *data)
{
if (node_id - 1 < io_boards.size())
{
@@ -1413,7 +1413,7 @@ void maple_naomi_jamma::send_jvs_message(u32 node_id, u32 channel, u32 length, u
}
}
void maple_naomi_jamma::send_jvs_messages(u32 node_id, u32 channel, bool use_repeat, u32 length, u8 *data, bool repeat_first)
void maple_naomi_jamma::send_jvs_messages(u32 node_id, u32 channel, bool use_repeat, u32 length, const u8 *data, bool repeat_first)
{
u8 temp_buffer[256];
if (data)
@@ -1519,7 +1519,7 @@ void maple_naomi_jamma::handle_86_subcommand()
}
}
u8 node_id = 0;
u8 *cmd = NULL;
const u8 *cmd = NULL;
u32 len = 0;
u8 channel = 0;
if (dma_count_in >= 3)
@@ -1798,7 +1798,7 @@ void maple_naomi_jamma::handle_86_subcommand()
}
}
u32 maple_naomi_jamma::RawDma(u32* buffer_in, u32 buffer_in_len, u32* buffer_out)
u32 maple_naomi_jamma::RawDma(const u32* buffer_in, u32 buffer_in_len, u32* buffer_out)
{
#ifdef DUMP_JVS
printf("JVS IN: ");
@@ -1872,7 +1872,11 @@ u32 maple_naomi_jamma::RawDma(u32* buffer_in, u32 buffer_in_len, u32* buffer_out
for (int i = 0; i < 32; i++)
jvs_repeat_request[i][0] = 0;
return MDRS_DeviceReply;
w8(MDRS_DeviceReply);
w8(0x00);
w8(0x20);
w8(0x00);
break;
}
int xfer_bytes;
if (dma_buffer_in[0] == 0xff)
@@ -2016,7 +2020,7 @@ u16 jvs_io_board::read_analog_axis(int player_num, int player_axis, bool inverte
#define JVS_OUT(b) buffer_out[length++] = b
#define JVS_STATUS1() JVS_OUT(1)
u32 jvs_io_board::handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_out)
u32 jvs_io_board::handle_jvs_message(const u8 *buffer_in, u32 length_in, u8 *buffer_out)
{
u8 jvs_cmd = buffer_in[0];
if (jvs_cmd == 0xF0) // JVS reset
+6 -1
View File
@@ -1,4 +1,9 @@
target_sources(${PROJECT_NAME} PRIVATE
internal.h
modem.cpp
modem.h
modem_regs.h)
modem_regs.h
v42.cpp
v42.h
v42bis.cpp
v42bis.h)
+114
View File
@@ -0,0 +1,114 @@
/*
Copyright 2026 flyinghead
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "types.h"
#include <deque>
namespace modem
{
class InStream
{
public:
virtual ~InStream() = default;
virtual int read() = 0;
virtual int available() = 0;
};
class OutStream
{
public:
virtual ~OutStream() = default;
virtual void write(u8 v) = 0;
};
class Transformer : public InStream, public OutStream
{
};
class PipeOut : public OutStream
{
public:
PipeOut(Transformer& transformer, OutStream& out)
: transformer(transformer), out(out)
{}
void write(u8 v) override
{
transformer.write(v);
while (transformer.available() != 0)
out.write(transformer.read());
}
private:
Transformer& transformer;
OutStream& out;
};
class PipeIn : public InStream
{
public:
PipeIn(Transformer& transformer, InStream& in)
: transformer(transformer), in(in)
{}
int read() override {
flushIn();
return transformer.read();
}
int available() override {
flushIn();
return transformer.available();
}
private:
void flushIn() {
while (in.available() != 0)
transformer.write(in.read());
}
Transformer& transformer;
InStream& in;
};
class BufferedTransformer : public Transformer
{
public:
int read() override
{
if (buffer.empty())
return -1;
int c = buffer.front();
buffer.pop_front();
return c;
}
int available() override {
return buffer.size();
}
virtual void reset() {
buffer.clear();
}
protected:
std::deque<u8> buffer;
};
}
+549 -106
View File
@@ -29,7 +29,10 @@
#include "serialize.h"
#include "cfg/option.h"
#include "stdclass.h"
#include "v42.h"
#include <cassert>
#include <map>
#include <deque>
#define MODEM_COUNTRY_RES 0
#define MODEM_COUNTRY_JAP 1
@@ -38,14 +41,16 @@
#define MODEM_MAKER_SEGA 0
#define MODEM_MAKER_ROCKWELL 1
#define MODEM_DEVICE_TYPE_336K 0
#define MODEM_TYPE_336K 0
// according to XDP
#define MODEM_TYPE_WIRELESS 2
#define LOG(...) DEBUG_LOG(MODEM, __VA_ARGS__)
const static u32 MODEM_ID[2] =
{
MODEM_COUNTRY_RES,
(MODEM_MAKER_ROCKWELL<<4) | (MODEM_DEVICE_TYPE_336K),
MODEM_COUNTRY_USA,
(MODEM_MAKER_ROCKWELL << 4) | MODEM_TYPE_336K,
};
static modemreg_t modem_regs;
@@ -77,10 +82,16 @@ enum ConnectState
HANDSHAKING,
PRE_CONNECTED,
CONNECTED,
NEGO_COMPLETE
};
static ConnectState connect_state = DISCONNECTED;
static float txFifoSize = 0.f;
static std::deque<u8> rxFifo;
static int cyclesPerByte;
static float txRxRatio = 1.f;
static void schedule_callback(int ms);
static const char *getDSPRamLabel(u32 addr);
static void update_interrupt()
{
@@ -114,14 +125,13 @@ static u32 get_masked_status(u32 reg)
modem_regs.reg1f.NSIA = 1; \
} \
} \
} while (false);
} while (false)
static void ControllerTestEnd();
static void DSPTestStart();
static void DSPTestEnd();
static u64 last_dial_time;
static bool data_sent;
#ifndef NDEBUG
static u64 last_comm_stats;
@@ -129,6 +139,201 @@ static int sent_bytes;
static int recvd_bytes;
#endif
using namespace modem;
class NetIn : public InStream
{
public:
int read() override {
return net::modbba::readModem();
}
int available() override {
return net::modbba::modemAvailable();
}
};
class NetOut : public OutStream
{
public:
void write(u8 v) override {
net::modbba::writeModem(v);
}
};
static NetIn netIn;
static NetOut netOut;
static InStream *curInput = &netIn;
static OutStream *curOutput = &netOut;
static V42Protocol v42Proto { netIn, netOut };
static bool v8bis;
static V8bisProtocol v8bisProto { netIn, netOut };
static void updateRxFifoStatus()
{
// The rx fifo timeout set in dspram[0x32c]:2-4 doesn't seem to be used
if (!rxFifo.empty())
{
modem_regs.RBUFFER = rxFifo.front();
modem_regs.reg1e.RDBF = (u8)(rxFifo.size() >= rxFifoTrigger[(dspram[0x32c] >> 6) & 3]);
if (modem_regs.reg04.FIFOEN)
SET_STATUS_BIT(0x0c, modem_regs.reg0c.RXFNE, 1);
SET_STATUS_BIT(0x01, modem_regs.reg01.RXHF, rxFifo.size() >= 8);
}
else
{
modem_regs.reg1e.RDBF = 0;
if (modem_regs.reg04.FIFOEN)
SET_STATUS_BIT(0x0c, modem_regs.reg0c.RXFNE, 0);
SET_STATUS_BIT(0x01, modem_regs.reg01.RXHF, 0);
}
}
static void configureStreams()
{
if (v8bis) {
curInput = &v8bisProto;
curOutput = &v8bisProto;
}
else if (modem_regs.reg08.ASYN == 0) {
curInput = &v42Proto;
curOutput = &v42Proto;
}
else {
curInput = &netIn;
curOutput = &netOut;
}
}
static bool setModemSpeedFromCONF()
{
static constexpr int v90Speeds[] = {
0, 28800, 29333, 30667, 32000, 33333, 34667, 36000, 37333, 38667, 40000, 41333,
42667, 44000, 45333, 46667, 48000, 49333, 50667, 52000, 53333, 54667, 56000
};
static constexpr int v34Speeds[] = {
0, 2400, 4800, 7200, 9600, 12000, 14400, 16800, 19200, 21600, 24000, 26400, 28800, 31200, 33600
};
modem_regs.reg0e.SPEED = 0;
int speed;
int rxSpeed = 0;
switch (modem_regs.CONF)
{
default:
if (modem_regs.CONF >= 0xe0 && modem_regs.CONF <= 0xf6)
{
// V.90
rxSpeed = v90Speeds[modem_regs.CONF - 0xe0];
if (rxSpeed == 0) {
speed = 0;
}
else
{
dspram[0x2e4] = modem_regs.CONF - 0xe0;
speed = 33600;
modem_regs.reg0e.SPEED = 0x10;
dspram[0x2e5] = 0xd;
}
}
else if (modem_regs.CONF >= 0x90 && modem_regs.CONF <= 0x9d)
{
// K56flex
if (modem_regs.CONF == 0x90) {
speed = 0;
}
else
{
rxSpeed = 30000 + (modem_regs.CONF - 0x90) * 2000;
speed = 33600;
modem_regs.reg0e.SPEED = 0x10;
}
}
else if (modem_regs.CONF >= 0xc0 && modem_regs.CONF <= 0xce)
{
// V.34
speed = v34Speeds[modem_regs.CONF - 0xc0];
if (speed != 0)
modem_regs.reg0e.SPEED = modem_regs.CONF - 0xc0 + 2;
dspram[0x2e4] = modem_regs.CONF - 0xc0;
dspram[0x2e5] = dspram[0x2e4];
}
else {
speed = 0;
}
break;
// V.33
case 0x31: speed = 14400; break;
case 0x32: speed = 12000; break;
case 0x34: speed = 9600; break;
case 0x38: speed = 7200; break;
// V.32 [bis]
case 0x76: speed = 14400; break;
case 0x72: speed = 12000; break;
case 0x74: speed = 9600; break;
case 0x75: speed = 9600; break;
case 0x78: speed = 7200; break;
case 0x71: speed = 4800; break;
case 0x70: speed = 0; break;
// V.17
case 0xb1: speed = 14400; break;
case 0xb2: speed = 12000; break;
case 0xb4: speed = 9600; break;
case 0xb8: speed = 7200; break;
// V.29
case 0x14: speed = 9600; break;
case 0x12: speed = 7200; break;
case 0x11: speed = 4800; break;
// V.27 ter, V.22 bis, V.22, V.21
case 0x02: speed = 4800; break;
case 0x01: speed = 2400; break;
case 0x84: speed = 2400; break;
case 0x82: speed = 1200; break;
case 0x52: speed = 1200; break;
case 0x51: speed = 600; break;
case 0xa0: speed = 300; break;
case 0xa8: speed = 300; break;
// Bell, V.23
case 0x23: speed = 4800; break;
case 0x62: speed = 1200; break;
case 0x60: speed = 300; break;
case 0xa4: speed = 1200; rxSpeed = 75; break;
case 0xa1: speed = 75; rxSpeed = 1200; break;
}
if (rxSpeed == 0)
rxSpeed = speed;
if (speed == 0)
{
modem_regs.reg0e.SPEED = 0;
cyclesPerByte = SH4_MAIN_CLOCK * 8 / 300;
txRxRatio = 1.f;
}
else
{
if (modem_regs.reg0e.SPEED == 0)
{
switch (speed)
{
case 14400: modem_regs.reg0e.SPEED = 7; break;
case 12000: modem_regs.reg0e.SPEED = 6; break;
case 9600: modem_regs.reg0e.SPEED = 5; break;
case 7200: modem_regs.reg0e.SPEED = 8; break;
case 4800: modem_regs.reg0e.SPEED = 4; break;
case 2400: modem_regs.reg0e.SPEED = 3; break;
case 1200: modem_regs.reg0e.SPEED = 2; break;
case 600: modem_regs.reg0e.SPEED = 1; break;
default: modem_regs.reg0e.SPEED = 0; break;
}
}
cyclesPerByte = SH4_MAIN_CLOCK * 8 / speed;
txRxRatio = (float)speed / rxSpeed;
}
NOTICE_LOG(MODEM, "MODEM Connected %s %d/%d bps", modem_regs.reg08.ASYN ? "ASYNC" : "SYNC", rxSpeed, speed);
return speed != 0;
}
static int modem_sched_func(int tag, int cycles, int jitter, void *arg)
{
#ifndef NDEBUG
@@ -164,7 +369,7 @@ static int modem_sched_func(int tag, int cycles, int jitter, void *arg)
switch (connect_state)
{
case DIALING:
if (last_dial_time != 0 && sh4_sched_now64() - last_dial_time >= (u64)(SH4_MAIN_CLOCK + jitter))
if (last_dial_time != 0 && sh4_sched_now64() - last_dial_time >= SH4_MAIN_CLOCK)
{
LOG("Switching to RINGING state");
connect_state = RINGING;
@@ -179,10 +384,11 @@ static int modem_sched_func(int tag, int cycles, int jitter, void *arg)
}
break;
case RINGING:
case NEGO_COMPLETE:
last_dial_time = 0;
LOG("\t\t *** RINGING STATE ***");
LOG("\t\t *** %s STATE ***", connect_state == RINGING ? "RINGING" : "NEGO COMPLETE");
modem_regs.reg1f.NEWS = 1;
if (!modem_regs.reg09.DATA)
if (!modem_regs.reg09.DATA && connect_state == RINGING)
{
SET_STATUS_BIT(0x0f, modem_regs.reg0f.RI, 1);
SET_STATUS_BIT(0x0b, modem_regs.reg0b.ATV25, 1);
@@ -190,8 +396,7 @@ static int modem_sched_func(int tag, int cycles, int jitter, void *arg)
break;
case HANDSHAKING:
LOG("\t\t *** HANDSHAKING STATE ***");
if (modem_regs.reg12 == 0xAA)
{
if (modem_regs.CONF == 0xAA) {
// V8 AUTO mode
dspram[0x302] |= 1 << 3; // ANSam detected
}
@@ -205,44 +410,69 @@ static int modem_sched_func(int tag, int cycles, int jitter, void *arg)
break;
case PRE_CONNECTED:
INFO_LOG(MODEM, "MODEM Connected");
if (modem_regs.reg03.RLSDE)
SET_STATUS_BIT(0x0f, modem_regs.reg0f.RLSD, 1);
if (modem_regs.reg12 == 0xAA)
bool validConfig;
if (modem_regs.CONF == 0xAA)
{
// V8 AUTO mode
dspram[0x302] |= 1 << 4; // protocol octet received
dspram[0x302] = (dspram[0x302] & 0x1f) | (dspram[0x304] & 0xe0); // Received Call Function
dspram[0x301] |= 1 << 4; // JM detected
dspram[0x303] |= 0xE0; // Received protocol bits (?)
dspram[0x303] |= 0xE1; // Received protocol bits (?), Received GSTN Octet
dspram[0x2e3] = 5; // Symbol rate 3429
dspram[0x2e4] = 0xe; // V.34 Receiver Speed 33.6
dspram[0x2e5] = 0xe; // V.34 Transmitter Speed 33.6
// set the negotiated speed to the max configured by the game
if ((dspram[0x6a3] & 1) == 1 && (dspram[0x6a2] & 0x20) == 0x20)
{
// V.90
modem_regs.CONF = 0xE0 | (dspram[0x6a2] & 0x1F);
dspram[0x6c1] = 0xff; // V90 Server DPCM Transmit Data Rate Mask
dspram[0x6c2] = 0xff;
dspram[0x6c3] = 0x3f;
}
else {
modem_regs.CONF = dspram[0x309];
}
validConfig = setModemSpeedFromCONF();
dspram[0x239] = 12; // RTD 0 @ 3429 sym rate
if (modem_regs.reg08.ASYN)
{
modem_regs.reg12 = 0xce; // CONF V34 - K56flex
modem_regs.reg0e.SPEED = 0x10; // 33.6k
}
else
{
// Force the driver to ASYN=1 so it sends raw data
modem_regs.reg12 = 0xa1; // CONF V23 75 TX/1200 RX
modem_regs.reg0e.SPEED = 0x02; // 1.2k
}
if (modem_regs.reg1f.NSIE)
{
// CONF
if (dspram[regs_int_mask_addr[0x12]] & (1 << 7))
modem_regs.reg1f.NSIA = 1;
// SPEED
if (dspram[regs_int_mask_addr[0x0e]] & 0x1f)
modem_regs.reg1f.NSIA = 1;
}
}
else {
validConfig = setModemSpeedFromCONF();
}
if (modem_regs.reg1f.NSIE)
{
// SPEED
if (dspram[regs_int_mask_addr[0x0e]] & 0x1f)
modem_regs.reg1f.NSIA = 1;
}
if (modem_regs.reg15.AUTO == 1) {
modem_regs.reg09.DATA = 1;
modem_regs.reg15.AUTO = 0;
}
modem_regs.reg14 = 0x00; // ABCODE: no error
if (!validConfig)
{
modem_regs.ABCODE = 1; // (fake) FED turned off while waiting to get into round trip delay estimate (RTDE)
connect_state = DISCONNECTED;
// TODO proper failure
}
else
{
modem_regs.ABCODE = 0; // no error
SET_STATUS_BIT(0x0f, modem_regs.reg0f.DSR, 1);
if (modem_regs.reg02.v0.RTSDE)
SET_STATUS_BIT(0x0f, modem_regs.reg0f.RTSDT, 1);
// Energy detected. Required for games to detect the connection
SET_STATUS_BIT(0x0f, modem_regs.reg0f.FED, 1);
net::modbba::start();
connect_state = CONNECTED;
}
if (modem_regs.reg1f.NSIE)
{
// ABCODE
@@ -250,21 +480,13 @@ static int modem_sched_func(int tag, int cycles, int jitter, void *arg)
modem_regs.reg1f.NSIA = 1;
}
modem_regs.reg1f.NEWS = 1;
SET_STATUS_BIT(0x0f, modem_regs.reg0f.DSR, 1);
if (modem_regs.reg02.v0.RTSDE)
SET_STATUS_BIT(0x0f, modem_regs.reg0f.RTSDT, 1);
// Energy detected. Required for games to detect the connection
SET_STATUS_BIT(0x0f, modem_regs.reg0f.FED, 1);
// V.34 Remote Modem Data Rate Capability
dspram[0x208] = 0xff; // 2.4 - 19.2 kpbs supported
dspram[0x209] = 0xbf; // 21.6 - 33.6 kpbs supported, asymmetric supported
net::modbba::start();
connect_state = CONNECTED;
callback_cycles = SH4_MAIN_CLOCK / 1000000 * 238; // 238 us
data_sent = false;
v42Proto.reset();
break;
@@ -278,32 +500,23 @@ static int modem_sched_func(int tag, int cycles, int jitter, void *arg)
LOG("modem_regs %02x == %02x", i, modem_regs.ptr[i]);
}
#endif
// This value is critical. Setting it too low will cause some sockets to stall.
// Check Sonic Adventure 2 and Samba de Amigo (PAL) integrated browsers.
// 143 us/bytes corresponds to 56K
// 57600 @ 10b: 174
// 38400 @ 10b: 260
callback_cycles = SH4_MAIN_CLOCK / 1000000 * 143;
modem_regs.reg1e.TDBE = 1;
callback_cycles = cyclesPerByte;
txFifoSize = std::max(txFifoSize - txRxRatio, 0.f);
modem_regs.reg1e.TDBE = txFifoSize == 0.f;
// Let WinCE send data first to avoid choking it
if (!modem_regs.reg1e.RDBF && data_sent)
if (v8bis && v8bisProto.completed()) {
connect_state = NEGO_COMPLETE;
v8bis = false;
configureStreams();
}
if (rxFifo.size() < 16)
{
int c = net::modbba::readModem();
if (c >= 0)
{
//LOG("pppd received %02x", c);
#ifndef NDEBUG
recvd_bytes++;
#endif
modem_regs.reg00 = c & 0xFF;
modem_regs.reg1e.RDBF = 1;
if (modem_regs.reg04.FIFOEN)
SET_STATUS_BIT(0x0c, modem_regs.reg0c.RXFNE, 1);
SET_STATUS_BIT(0x01, modem_regs.reg01.RXHF, 1);
int c = curInput->read();
if (c >= 0) {
rxFifo.push_back(c);
updateRxFifoStatus();
}
}
break;
default:
@@ -350,7 +563,7 @@ static void NormalDefaultRegs()
memset(&modem_regs, 0, sizeof(modem_regs));
memcpy(dspram, por_dspram, sizeof(dspram));
modem_regs.reg05.CEQ = 1;
modem_regs.reg12 = 0x76; // CONF: V.32 bis TCM 14400
modem_regs.CONF = 0x76; // CONF: V.32 bis TCM 14400
modem_regs.reg09.DATA = 1;
modem_regs.reg09.DTMF = 1;
modem_regs.reg15.HWRWK = 1;
@@ -363,6 +576,10 @@ static void NormalDefaultRegs()
modem_regs.reg1e.TDBE = 1;
connect_state = DISCONNECTED;
last_dial_time = 0;
txFifoSize = 0.f;
rxFifo.clear();
v8bis = false;
configureStreams();
}
static void DSPTestEnd()
{
@@ -453,7 +670,7 @@ static void check_start_handshake()
{
if (modem_regs.reg09.DTR
&& (modem_regs.reg09.DATA || modem_regs.reg15.AUTO)
&& connect_state == RINGING)
&& (connect_state == RINGING || connect_state == NEGO_COMPLETE))
{
LOG("DTR asserted. starting handshaking");
connect_state = HANDSHAKING;
@@ -475,11 +692,12 @@ static void ModemNormalWrite(u32 reg, u32 data)
switch(reg)
{
case 0x02:
case 0x02: // TDE SQDIS S511/DCDEN CDEN RTSDE V54TE V54AE V54PE / CODBITS
modem_regs.reg0f.RTSDT = modem_regs.reg02.v0.RTSDE && connect_state == CONNECTED;
LOG("reg02 = %x", modem_regs.ptr[reg]);
break;
case 0x06:
case 0x06: // EXOS CCRTN HDLC PEN STB WDSZ/DECBITS
LOG("PEN = %d", modem_regs.reg06.PEN);
if (modem_regs.reg06.PEN)
WARN_LOG(MODEM, "Parity not supported");
@@ -487,16 +705,38 @@ static void ModemNormalWrite(u32 reg, u32 data)
WARN_LOG(MODEM, "HDLC mode not supported");
break;
case 0x08:
LOG("TPDM = %d ASYN = %d", modem_regs.reg08.TPDM, modem_regs.reg08.ASYN);
case 0x08: // ASYN TPDM V21S V54T V54A V54P RTRN RTS
LOG("TPDM = %d ASYN = %d V54T,A,P = %d,%d,%d RTS = %d", modem_regs.reg08.TPDM, modem_regs.reg08.ASYN,
modem_regs.reg08.V54T, modem_regs.reg08.V54A, modem_regs.reg08.V54P, modem_regs.reg08.RTS);
if ((old ^ data) & 0x80)
// ASYN changed
configureStreams();
break;
case 0x09:
case 0x09: // NV25 CC DTMF ORG LL DATA RRTSE DTR
LOG("reg09 = %x", modem_regs.ptr[reg]);
check_start_handshake(); // DTR and DATA
if (connect_state == CONNECTED)
{
if (modem_regs.reg09.DTR) {
LOG("DTR asserted");
SET_STATUS_BIT(0x0f, modem_regs.reg0f.DSR, 1);
}
else
{
LOG("DTR reset");
SET_STATUS_BIT(0x0f, modem_regs.reg0f.DSR, 0);
SET_STATUS_BIT(0x0f, modem_regs.reg0f.RLSD, 0);
}
}
break;
case 0x10: // TBUFFER
if (module_download)
if (v8bis && modem_regs.reg08.RTS == 0) {
// emit V8 bis tone
v8bisProto.emitTone((u8)data);
}
else if (module_download)
{
download_crc = (download_crc << 1) + ((download_crc & 0x80) >> 7) + (data & 0xFF);
}
@@ -507,26 +747,37 @@ static void ModemNormalWrite(u32 reg, u32 data)
{
INFO_LOG(MODEM, "MODEM Dialing");
connect_state = DIALING;
modem_regs.SECRXB = 0;
}
schedule_callback(100);
}
else if (connect_state == CONNECTED && modem_regs.reg08.RTS)
{
//LOG("ModemNormalWrite : TBUFFER = %X", data);
data_sent = true;
#ifndef NDEBUG
sent_bytes++;
#endif
net::modbba::writeModem(data);
curOutput->write(data);
modem_regs.reg1e.TDBE = 0;
txFifoSize += 1.f;
if (modem_regs.reg04.FIFOEN) {
SET_STATUS_BIT(0x0d, modem_regs.reg0d.TXFNF, (u8)(txFifoSize <= 15.f));
SET_STATUS_BIT(0x01, modem_regs.reg01.TXHF, (u8)(txFifoSize >= 8.f));
}
else {
SET_STATUS_BIT(0x0d, modem_regs.reg0d.TXFNF, 0);
SET_STATUS_BIT(0x01, modem_regs.reg01.TXHF, 1);
}
}
break;
case 0x11:
case 0x11: // BRKS PARSL TXV RXV V23HDX TEOF TXP
LOG("PARSL = %d", modem_regs.reg11.PARSL);
break;
case 0x14: // ABCODE
LOG("ABCODE = %x", modem_regs.ptr[reg]);
if (data == 0x4f || data == 0x5f)
{
reg1b_save = modem_regs.ptr[0x1b];
@@ -538,8 +789,8 @@ static void ModemNormalWrite(u32 reg, u32 data)
else if (data == 0 && module_download)
{
// If module download is in progress, this signals the end of it
modem_regs.reg17 = 0xFF;
modem_regs.reg16 = download_crc;
modem_regs.SECTXB = 0xFF;
modem_regs.SECRXB = download_crc;
// Restore reg 1b
modem_regs.ptr[0x1b] = reg1b_save;
module_download = false;
@@ -547,7 +798,8 @@ static void ModemNormalWrite(u32 reg, u32 data)
}
break;
case 0x15:
case 0x15: // SLEEP STOP RDWK HWRWK AUTO RREN EXL3 EARC
LOG("reg15 = %x", modem_regs.ptr[reg]);
check_start_handshake(); // AUTO
break;
@@ -559,7 +811,8 @@ static void ModemNormalWrite(u32 reg, u32 data)
word_dspram_write = true;
break;
case 0x1a:
case 0x1a: // SFRES RIEN RION DMAE SCOBF SCIBE SECEN
LOG("reg1a = %x", modem_regs.ptr[reg]);
if (connect_state == CONNECTED && modem_regs.reg1a.SCIBE)
WARN_LOG(MODEM, "Unexpected state: connected and SCIBE==1");
break;
@@ -568,7 +821,7 @@ static void ModemNormalWrite(u32 reg, u32 data)
case 0x1C: // MEADDL
break;
case 0x1D: // MEADDH
case 0x1D: // MEACC MEADDHB12 MEMW MEMCR MEADDH
if (modem_regs.reg1c_1d.MEMW && !(old & (1 << 5)))
{
word_dspram_write = false;
@@ -580,32 +833,42 @@ static void ModemNormalWrite(u32 reg, u32 data)
u32 dspram_addr = modem_regs.reg1c_1d.MEMADD_l | (modem_regs.reg1c_1d.MEMADD_h << 8);
if (modem_regs.reg1c_1d.MEMW)
{
LOG("DSP mem Write%s address %08x = %x", word_dspram_write ? " (w)" : "", dspram_addr, modem_regs.reg18_19);
LOG("DSPRam Write<%d> %03x (%s) = %x", word_dspram_write ? 16 : 8,
dspram_addr, getDSPRamLabel(dspram_addr),
modem_regs.MEDA & (word_dspram_write ? 0xffff : 0xff));
if (word_dspram_write)
{
if (dspram_addr & 1)
{
dspram[dspram_addr] = modem_regs.reg18_19 & 0xFF;
dspram[dspram_addr + 1] = (modem_regs.reg18_19 >> 8) & 0xFF;
dspram[dspram_addr] = modem_regs.MEDA & 0xFF;
dspram[dspram_addr + 1] = (modem_regs.MEDA >> 8) & 0xFF;
}
else {
*(u16*)&dspram[dspram_addr] = modem_regs.MEDA;
}
else
*(u16*)&dspram[dspram_addr] = modem_regs.reg18_19;
}
else
dspram[dspram_addr] = modem_regs.reg18_19 & 0xFF;
else {
dspram[dspram_addr] = modem_regs.MEDA & 0xFF;
}
if ((dspram_addr == 0x26B || dspram_addr == 0x26c) && (modem_regs.MEDA & 0xFF) != 0)
dspram[0x26F] = 1; // Saved Filtered EQM
}
else
{
if (dspram_addr & 1)
modem_regs.reg18_19 = dspram[dspram_addr] | (dspram[dspram_addr + 1] << 8);
modem_regs.MEDA = dspram[dspram_addr] | (dspram[dspram_addr + 1] << 8);
else
modem_regs.reg18_19 = *(u16*)&dspram[dspram_addr];
LOG("DSP mem Read address %08x == %x", dspram_addr, modem_regs.reg18_19 );
modem_regs.MEDA = *(u16*)&dspram[dspram_addr];
LOG("DSPRam Read<16> %03x (%s) == %x", dspram_addr, getDSPRamLabel(dspram_addr), modem_regs.MEDA);
}
}
break;
case 0x1F:
case 0x1E: // TDBIA RDBIA TDBIE TDBE RDBIE RDBF
//LOG("TDBIE=%d RDBIE=%d", modem_regs.reg1e.TDBIE, modem_regs.reg1e.RDBIE);
break;
case 0x1F: // NSIA NCIA NSIE NEWS NCIE NEWC
if (old != modem_regs.ptr[reg])
LOG("reg1f = %x", modem_regs.ptr[reg]);
if (!modem_regs.reg1f.NCIE)
modem_regs.reg1f.NCIA = 0;
if (modem_regs.reg1f.NEWC)
@@ -624,7 +887,16 @@ static void ModemNormalWrite(u32 reg, u32 data)
modem_regs.reg1f.NEWC = 0; // accept the settings
if (modem_regs.reg1f.NCIE)
modem_regs.reg1f.NCIA = 1;
LOG("NEWC CONF=%x", modem_regs.reg12);
LOG("NEWC CONF=%x", modem_regs.CONF);
}
if (modem_regs.CONF == 0x81 && !v8bis)
{
// V.8 bis detector
v8bis = (dspram[0x439] & 1) == 1;
if (v8bis) {
v8bisProto.reset();
configureStreams();
}
}
}
// Don't allow NEWS to be set if 0
@@ -637,7 +909,7 @@ static void ModemNormalWrite(u32 reg, u32 data)
break;
default:
//LOG("ModemNormalWrite : undef %03X = %X", reg, data);
LOG("ModemNormalWrite: reg %02x = %x", reg, data);
break;
}
update_interrupt();
@@ -661,28 +933,47 @@ u32 ModemReadMem_A0_006(u32 addr, u32 size)
//if (reg==0xF)
{
SET_STATUS_BIT(0x0f, modem_regs.reg0f.CTS, modem_regs.reg08.RTS && connect_state == CONNECTED);
SET_STATUS_BIT(0x0b, modem_regs.reg0b.TONEA, connect_state == DISCONNECTED);
SET_STATUS_BIT(0x0b, modem_regs.reg0b.TONEB, connect_state == DISCONNECTED);
SET_STATUS_BIT(0x0b, modem_regs.reg0b.TONEC, connect_state == DISCONNECTED);
// FIXME This should be reset if transmit buffer is full
if (modem_regs.reg04.FIFOEN || module_download)
SET_STATUS_BIT(0x0d, modem_regs.reg0d.TXFNF, 1);
SET_STATUS_BIT(0x0b, modem_regs.reg0b.TONEA, connect_state == DISCONNECTED || connect_state == NEGO_COMPLETE);
SET_STATUS_BIT(0x0b, modem_regs.reg0b.TONEB, connect_state == DISCONNECTED || connect_state == NEGO_COMPLETE);
SET_STATUS_BIT(0x0b, modem_regs.reg0b.TONEC, connect_state == DISCONNECTED || connect_state == NEGO_COMPLETE);
if (modem_regs.reg04.FIFOEN || module_download) {
SET_STATUS_BIT(0x0d, modem_regs.reg0d.TXFNF, (u8)(txFifoSize <= 15.f));
SET_STATUS_BIT(0x01, modem_regs.reg01.TXHF, (u8)(txFifoSize >= 8.f));
}
else {
SET_STATUS_BIT(0x0d, modem_regs.reg0d.TXFNF, (u8)(txFifoSize == 0.f));
SET_STATUS_BIT(0x01, modem_regs.reg01.TXHF, (u8)(txFifoSize >= 1.f));
}
}
u8 data = modem_regs.ptr[reg];
if (reg == 0x00) // RBUFFER
{
//LOG("Read RBUFFER = %X", data);
modem_regs.reg1e.RDBF = 0;
SET_STATUS_BIT(0x0c, modem_regs.reg0c.RXFNE, 0);
SET_STATUS_BIT(0x01, modem_regs.reg01.RXHF, 0);
update_interrupt();
if (!rxFifo.empty())
{
rxFifo.pop_front();
updateRxFifoStatus();
update_interrupt();
}
}
else if (reg == 0x16 || reg == 0x17)
else if (reg == 0x16)
{
//LOG("SECTXB / SECTXB being read %02x", reg)
if (v8bis)
{
if (connect_state == CONNECTED)
data = 0;
else
data = v8bisProto.detectTone();
modem_regs.SECRXB = data;
}
LOG("Read SECRXB == %x", data);
}
else if (reg == 0x17) {
LOG("Read SECTXB == %x", data);
}
else {
//LOG("Read reg%02x == %x", reg, data);
}
//else
// LOG("Read Reg %03x = %x", reg, data);
return data;
}
@@ -746,7 +1037,7 @@ void ModemSerialize(Serializer& ser)
ser << state;
ser << connect_state;
ser << last_dial_time;
ser << data_sent;
ser << false; // data_sent TODO get rid of this in the next savestate version
}
void ModemDeserialize(Deserializer& deser)
{
@@ -759,6 +1050,158 @@ void ModemDeserialize(Deserializer& deser)
deser >> state;
deser >> connect_state;
deser >> last_dial_time;
bool data_sent;
deser >> data_sent;
}
rxFifo.clear();
v42Proto.reset();
v8bis = false;
v8bisProto.reset();
configureStreams();
}
static const std::map<u32, const char *> dspRamDesc
{
{ 0x26b, "EQM Baud Interval" },
{ 0x26C, "Num EQM Samples" },
{ 0x26F, "Saved Filtered EQM" },
{ 0x208, "V.34 Remote Mode Data Rate Capability" },
{ 0x209, "V.34 Remote Mode Data Rate Capability 2" },
{ 0x302, "V.8 Status Bits 2" },
{ 0x303, "V.8 Status Bits 3" },
{ 0x2e5, "K56flex/V.34 Transmitter Speed" },
{ 0x2e4, "K56flex/V.34 Receiver Speed" },
{ 0x239, "Round Trip Far Echo Delay" },
{ 0x2e3, "V.34 Symbol Rate Value" },
{ 0x247, "NEWS Masking Register for 01" },
{ 0x246, "NEWS Masking Register for 0A" },
{ 0x245, "NEWS Masking Register for 0B" },
{ 0x244, "NEWS Masking Register for 0C" },
{ 0x243, "NEWS Masking Register for 0D" },
{ 0x242, "NEWS Masking Register for 0E" },
{ 0x241, "NEWS Masking Register for 0F" },
{ 0x089, "NEWS Masking Register for CONF" },
{ 0x38a, "NEWS Masking Register for ABCODE" },
{ 0x370, "NEWS Masking Register for SECRXB" },
{ 0x371, "NEWS Masking Register for SECTXB" },
{ 0x27d, "NEWS Masking Register for 1A" },
{ 0x27c, "NEWS Masking Register for 1B" },
{ 0x32c, "Receive FIFO Trigger Level" },
{ 0x701, "Receive FIFO Extension Enable" },
{ 0x702, "Transmit FIFO Extension Enable" },
{ 0x309, "V.34 Full-Duplex configuration" },
{ 0x312, "Modulation mode V23 full" },
{ 0x313, "Modulation mode V23 half" },
{ 0x382, "V.34 Data Rate Mask" },
{ 0x383, "V.34 Data Rate Mask 2" },
{ 0x21e, "Minimum Period of Valid Ring Signal" },
{ 0x21f, "Maximum Period of Valid Ring Signal" },
{ 0xaa0, "TONEA LPGAIN" },
{ 0xaa1, "TONEA Biquad1 A3" },
{ 0xaa2, "TONEA Biquad1 A2" },
{ 0xaa3, "TONEA Biquad1 A1" },
{ 0xaa4, "TONEA Biquad1 B2" },
{ 0xaa5, "TONEA Biquad1 B1" },
{ 0xaa6, "TONEB LPGAIN" },
{ 0xaa7, "TONEB Biquad1 A3" },
{ 0xaa8, "TONEB Biquad1 A2" },
{ 0xaa9, "TONEB Biquad1 A1" },
{ 0xaaa, "TONEB Biquad1 B2" },
{ 0xaab, "TONEB Biquad1 B1" },
{ 0xaac, "TONEC LPGAIN" },
{ 0xaad, "TONEC Biquad1 A3" },
{ 0xaae, "TONEC Biquad1 A2" },
{ 0xaaf, "TONEC Biquad1 A1" },
{ 0xab0, "TONEC Biquad1 B2" },
{ 0xab1, "TONEC Biquad1 B1" },
{ 0xab2, "PreFilter Biquad1 A3" },
{ 0xab3, "PreFilter Biquad1 A2" },
{ 0xab4, "PreFilter Biquad1 A1" },
{ 0xab5, "PreFilter Biquad1 B2" },
{ 0xab6, "PreFilter Biquad1 B1" },
{ 0xab8, "TONEA THRESHU" },
{ 0xab9, "TONEB THRESHU" },
{ 0xaba, "TONEC THRESHU" },
{ 0xba0, "TONEA LPFBK" },
{ 0xba1, "TONEA Biquad2 A3" },
{ 0xba2, "TONEA Biquad2 A2" },
{ 0xba3, "TONEA Biquad2 A1" },
{ 0xba4, "TONEA Biquad2 B2" },
{ 0xba5, "TONEA Biquad2 B1" },
{ 0xba6, "TONEB LPFBK" },
{ 0xba7, "TONEB Biquad2 A3" },
{ 0xba8, "TONEB Biquad2 A2" },
{ 0xba9, "TONEB Biquad2 A1" },
{ 0xbaa, "TONEB Biquad2 B2" },
{ 0xbab, "TONEB Biquad2 B1" },
{ 0xbac, "TONEC LPFBK" },
{ 0xbad, "TONEC Biquad2 A3" },
{ 0xbae, "TONEC Biquad2 A2" },
{ 0xbaf, "TONEC Biquad2 A1" },
{ 0xbb0, "TONEC Biquad2 B2" },
{ 0xbb1, "TONEC Biquad2 B1" },
{ 0xbb2, "PreFilter Biquad2 A3" },
{ 0xbb3, "PreFilter Biquad2 A2" },
{ 0xbb4, "PreFilter Biquad2 A1" },
{ 0xbb5, "PreFilter Biquad2 B2" },
{ 0xbb6, "PreFilter Biquad2 B1" },
{ 0xbb8, "TONEA THRESHL" },
{ 0xbb9, "TONEB THRESHL" },
{ 0xbba, "TONEC THRESHL" },
{ 0x29c, "DTMF Low Band Power Level" },
{ 0x29b, "DTMF Low Band Power Level 2" },
{ 0x29e, "DTMF High Band Power Level" },
{ 0x29d, "DTMF High Band Power Level 2" },
{ 0x218, "DTMF Tone Duration 2" },
{ 0x2db, "DTMF Tone Duration" },
{ 0x219, "DTMF Interdigit Delay 2" },
{ 0x2dc, "DTMF Interdigit Delay" },
{ 0x304, "V.8 Control Register 1" },
{ 0x305, "V.8 Control Register 2" },
{ 0x306, "V.8 Control Register 3" },
{ 0x307, "V.8 Control Register 4" },
{ 0x308, "V.8 Control Register 5" },
{ 0x13f, "V.34 Asymmetric Data Rates Enable / No Automode to FSK" },
{ 0x100, "V.34 PREDIS and TLDDIS" },
{ 0x105, "V.34 Spectral Parameters Control" },
{ 0x101, "V.34 Baud Rate Mask (BRM)" },
{ 0x2c1, "V.32/V.32 bis R1 Mask" },
{ 0x2c0, "V.32/V.32 bis R1 Mask 2" },
{ 0x2c3, "V.32/V.32 bis R2 Mask" },
{ 0x2c2, "V.32/V.32 bis R2 Mask 2" },
{ 0x2c5, "V.32 bis R4 Mask" },
{ 0x2c4, "V.32 bis R4 Mask 2" },
{ 0x2c7, "V.32 bis R5 Mask" },
{ 0x2c6, "V.32 bis R5 Mask 2" },
{ 0x6a2, "Desired V.90 Receive Speed" },
{ 0x6a3, "V.90 Host Control" },
{ 0x3db, "Transmitter Output Level Gain (G) - All Modes" },
{ 0x3da, "Transmitter Output Level Gain (G) - All Modes 2" },
{ 0xb57, "Transmitter Output Level Gain (G) - FSK Modes" },
{ 0x3a5, "ARA-in-RAM Enable" },
{ 0x10d, "V.21/V.23 CTS Mark Qualify / RLSD Overwrite Control / Extended RTH Control" },
{ 0x6c1, "V.90 Server DPCM Tx Data Rate Mask 1" },
{ 0x6c2, "V.90 Server DPCM Tx Data Rate Mask 2" },
{ 0x6c3, "V.90 Server DPCM Tx Data Rate Mask 3" },
{ 0x09f, "Available modulation types?" },
{ 0x439, "V.8bis Detector / Host AudioSpan" },
{ 0x3e1, "Voice path control bits" },
{ 0x20c, "Eye Quality Monitor" },
{ 0x810, "V.90/K56 Eye Quality Monitor" },
{ 0x202, "RTS-CTS Delay" },
{ 0x203, "RTS-CTS Delay 2" },
{ 0x270, "RLSD Drop Out Timer" },
{ 0x271, "RLSD Drop Out Timer 2" },
};
static const char *getDSPRamLabel(u32 addr)
{
auto it = dspRamDesc.find(addr);
if (it == dspRamDesc.end())
return "?";
else
return it->second;
}
+16 -14
View File
@@ -29,7 +29,7 @@ union modemreg_t
struct
{
//00 Receive Data Buffer (RBUFFER)/Voice Receive Data Buffer (VBUFR)
u8 reg00;
u8 RBUFFER;
//01 VOLUME VPAUSE - - TXHF RXHF RXP
struct
{
@@ -231,7 +231,7 @@ union modemreg_t
u8 BRKS:1;
} reg11;
//12 Configuration (CONF)
u8 reg12;
u8 CONF;
//13 TLVL RTH TXCLK
struct
{
@@ -240,7 +240,7 @@ union modemreg_t
u8 TXCLK:2; // Transmit Clock Select
} reg13;
//14 ABCODE
u8 reg14;
u8 ABCODE;
//15 SLEEP - RDWK HWRWK AUTO RREN EXL3 EARC
struct
{
@@ -254,12 +254,12 @@ union modemreg_t
u8 SLEEP:1;
} reg15;
//16 Secondary Receive Data Buffer/V.34 Receive Status (SECRXB)
u8 reg16;
u8 SECRXB;
//17 Secondary Transmit Data Buffer/V.34 Transmit Status(SECTXB)
u8 reg17;
u8 SECTXB;
//18 Memory Access Data LSB B7-B0 (MEDAL)
//19 Memory Access Data MSB B15-B8 (MEDAM)
u16 reg18_19;
u16 MEDA;
//1A SFRES RIEN RION DMAE - SCOBF SCIBE SECEN
struct
{
@@ -320,7 +320,7 @@ union modemreg_t
};
#pragma pack(pop)
u8 regs_write_mask[] = {
static constexpr u8 regs_write_mask[] = {
//00 Receive Data Buffer (RBUFFER)/Voice Receive Data Buffer (VBUFR)
0xFF,
//01 VOLUME VPAUSE - - (TXHF RXHF RXP)
@@ -390,7 +390,7 @@ u8 regs_write_mask[] = {
0xFF,
};
u32 regs_int_mask_addr[0x21] {
static constexpr u32 regs_int_mask_addr[0x21] {
0,
0x247, // 01
0,
@@ -423,7 +423,7 @@ u32 regs_int_mask_addr[0x21] {
};
// Power-on reset DSP RAM
static const u8 por_dspram[] =
static constexpr u8 por_dspram[] =
{
/* 0x000 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x010 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -434,7 +434,7 @@ static const u8 por_dspram[] =
/* 0x060 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x070 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x080 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x090 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9F avail mod type (wince): 80, 30, 00?
/* 0x090 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, // 9F avail mod type, 80=V90 support?
/* 0x0A0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x0B0 */ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x0C0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -493,7 +493,7 @@ static const u8 por_dspram[] =
/* 0x3E0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x3F0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x400 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x400 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 401:0: K56flex handshaking, 406:0: K56flex Enable, 409: Desired K56flex Receive Speed
/* 0x410 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x420 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x430 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -527,7 +527,7 @@ static const u8 por_dspram[] =
/* 0x5E0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x5F0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x600 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x600 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 604: K56/V34/V90 Rx Max Data Rate Mask, 605: K56/V34 Tx Max Data Rate Mask
/* 0x610 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x620 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x630 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -537,9 +537,9 @@ static const u8 por_dspram[] =
/* 0x670 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x680 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x690 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x6A0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x6A0 */ 0x00, 0x00, 0xc0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 6A2: Desired V.90 Receive Speed, 6A3: V.90 Host Control
/* 0x6B0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x6C0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x6C0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 6C1-3: Server DPCM Transmit Data Rate Mask
/* 0x6D0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x6E0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0x6F0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -697,3 +697,5 @@ static const u8 por_dspram[] =
/* 0xFE0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 0xFF0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
constexpr unsigned rxFifoTrigger[] { 1u, 4u, 8u, 12u };
File diff suppressed because it is too large. Load diff
+225
View File
@@ -0,0 +1,225 @@
/*
Copyright 2025 flyinghead
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "types.h"
#include "internal.h"
#include "v42bis.h"
#include "network/netservice.h"
#include <vector>
#include <deque>
#include <unordered_map>
namespace modem
{
class V14Encoder : public BufferedTransformer
{
public:
void write(u8 v) override;
void flush();
void reset() override;
void setStopBits(int txStopBits) {
this->txStopBits = txStopBits;
}
private:
void transmitBit(u8 v);
u8 txCurByte = 0;
u32 txBitCount = 0;
int txStopBits = 1;
};
class V14Decoder : public BufferedTransformer
{
public:
void write(u8 v) override;
int receivedStopBits();
void reset() override;
private:
enum RxState {
RX_IDLE,
RX_RECEIVING,
RX_STOP_BIT
};
u8 rxCurByte = 0;
RxState rxState = RX_IDLE;
u32 rxBitCount = 0;
int charsSinceMissingStop = 0;
int rxStopBits = 0;
int maxStopBits = 0;
};
class HdlcDecoder : public OutStream
{
public:
void write(u8 v);
bool frameAvailable() const { return !frames.empty(); }
std::vector<u8> getFrame();
void reset();
private:
std::deque<std::vector<u8>> frames;
std::vector<u8> rxFrame;
int rxOnes = 0;
int rxPosition = 0;
u8 rxCurByte = 0;
};
class HdlcEncoder : public BufferedTransformer
{
public:
void sendFrame(const std::vector<u8>& data);
void sendFlag();
void reset() override;
private:
void sendByte(u8 byte);
void write(u8 v) override {} // unused
u32 txBitBuffer = 0;
int txBitCount = 0;
};
class V42Protocol : public InStream, public OutStream
{
public:
V42Protocol(InStream& in, OutStream& out)
: inputStream(in), outputStream(out)
{}
int read() override;
void write(u8 v) override;
void reset();
int available() override {
return 1;
}
private:
InStream& inputStream;
OutStream& outputStream;
enum Phase {
None,
Detection,
Establish,
Connected,
Release,
V14 // Bomberman Online, Power Smash, Pro Yakyu Team de asobou net!
};
Phase phase = None;
u8 lastRx = 0;
int odpCount = 0;
V14Encoder v14Encoder;
V14Decoder v14Decoder;
// for V14 mode
PipeIn v14PipeIn { v14Encoder, inputStream };
PipeOut v14PipeOut { v14Decoder, outputStream };
// for V42 mode
HdlcEncoder hdlcEncoder;
HdlcDecoder hdlcDecoder;
u8 txSeqNum = 0;
u8 rxSeqNum = 0;
u8 txSeqAck = 0;
unsigned txMaxSize = 128;
int txWindow = 15;
std::unordered_map<int, std::vector<u8>> sentIFrames;
// V42 bis
v42b::Compressor compressor;
v42b::Decompressor decompressor;
bool compressionEnabled = false;
class Timer
{
u64 start_ = 0;
u64 duration;
public:
Timer(u64 duration)
: duration(duration * 200000)
{}
void start();
bool expired();
};
Timer detectionTimer { 750 }; // Detection phase timer (T400)
Timer inactivityTimer { 1000 }; // Inactivity timer (T403)
void handleFrame();
void handleSabme(u8 address, u8 control);
void handleDisc(u8 address);
void handleIFrame(const std::vector<u8>& frame);
void handleXid(std::vector<u8> frame);
void handleReject(u8 control2);
void sendFlag() {
hdlcEncoder.sendFlag();
}
void sendFrame(const std::vector<u8>& data) {
hdlcEncoder.sendFrame(data);
}
void sendIFrame();
void ackIFrame(int seqNum);
friend class V42Test;
};
//
// V.8 bis is used by older browsers (Planet Web 1.125, DreamKey 1.0/1.5 and the many games that include them)
// and Windows CE games.
// To enable 56k, a non-standard info field must be returned indicating K56flex/V90 support.
//
class V8bisProtocol : public InStream, public OutStream
{
public:
V8bisProtocol(InStream& in, OutStream& out)
: inputStream(in), outputStream(out)
{}
void emitTone(u8 tone);
u8 detectTone();
int read() override;
int available() override;
void write(u8 v) override;
void reset();
bool completed() {
return available() == 0 && done;
}
private:
void sendCL();
void sendAck(int n = 1);
void sendNak(int n = 1);
InStream& inputStream;
OutStream& outputStream;
HdlcEncoder encoder;
HdlcDecoder decoder;
int toneState = 0;
u64 toneTime = 0;
bool dataMode = false;
bool done = false;
};
} // namespace modem
+428
View File
@@ -0,0 +1,428 @@
/*
Copyright 2025 flyinghead
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#include "v42bis.h"
#include <algorithm>
#include "stdclass.h"
namespace modem::v42b
{
enum
{
// Control code words (compressed mode)
ETM = 0, // Enter transparent mode
FLUSH = 1, // Flush data
STEPUP = 2, // Step up codeword size
// Command codes (transparent mode)
ECM = 0, // Enter compression mode
EID = 1, // Escape character in data
RESET = 2 // Force reinitialization
};
void V42Base::Dictionary::reset()
{
for (u16 i = CW_RESERVED; i < CW_FIRST; i++)
nodes[i] = Node::makeRootNode(i - CW_RESERVED);
std::fill(nodes.begin() + CW_FIRST, nodes.end(), Node{});
}
void V42Base::Dictionary::addChild(u16 childCode, Node& parent, u8 childChar)
{
nodes[childCode] = { childChar, &parent };
parent.addChild(nodes[childCode]);
#if 0 // !defined(NDEBUG) || defined(DEBUGFAST)
std::string newString;
Node *node = &nodes[childCode];
while (true)
{
std::string s;
if (isprint(node->character))
s = (char)node->character;
else
s = strprintf("\\x%02x", node->character);
newString = s + newString;
if (node->isRoot())
break;
node = node->parent;
}
DEBUG_LOG(MODEM, "V.42bis Dict::added %04x <=> '%s'", childCode, newString.c_str());
#endif
}
void V42Base::reset()
{
dict.reset();
nextCodeWord = CW_FIRST;
codeWordSize = CHAR_SIZE + 1;
codeWordThreshold = 2 * ALPHABET_SIZE;
lastAddedCode = -1;
matchLen = 0;
curCode = -1;
transparent = true;
modeTransition = false;
escapeCode = 0;
}
void V42Base::recoverNode()
{
// 7.7 Node recovery
// 6.5 Recovering a dictionary entry to use next
while (true)
{
// 6.5(a) and (b) counter C1 shall be incremented; if the value of C1 exceeds N2-1 then C1 shall be set to N5
if (++nextCodeWord >= maxCodeWords)
nextCodeWord = CW_FIRST;
Node& node = dict.get(nextCodeWord);
// 6.5(c) We need to reuse a leaf or find a free node
if (!node.isLeaf())
continue;
if (node.isFree())
break;
// 6.5(d) if the node is a leaf node, then it shall be detached from its parent
node.parent->removeChild(node);
break;
}
}
int V42Base::acceptChar(u8 octet)
{
if (curCode == -1)
{
curCode = octet + CW_RESERVED;
matchLen = 1;
modeTransition = false;
return -1;
}
matchLen++;
Node *node = dict.getChild(curCode, octet);
if (node != nullptr && !modeTransition)
{
// node does exist
u16 codeword = dict.getCode(node);
// 6.3(b) If the string matches a dictionary entry, and the entry is not that entry
// created by the last invocation of the string matching procedure, then the
// next character shall be read and appended to the string and this step
// repeated.
if (codeword != lastAddedCode) {
curCode = codeword;
return -1;
}
}
// 6.4 Add the string to the dictionary
if (node == nullptr && matchLen <= maxStringLength)
{
checkDictSize();
dict.addChild(nextCodeWord, dict.get(curCode), octet);
lastAddedCode = nextCodeWord;
recoverNode();
}
else {
lastAddedCode = -1;
}
int emitCode;
if (modeTransition)
// this code's string has already been pushed in transparent mode
emitCode = -1;
else
emitCode = curCode;
modeTransition = false;
curCode = octet + CW_RESERVED;
matchLen = 1;
return emitCode;
}
Compressor::Compressor(u32 maxCodeWords, int maxStringLength)
: V42Base(maxCodeWords, maxStringLength)
{
reset();
}
void Compressor::pushCode(u16 code)
{
outBitBuf |= code << outBitPos;
outBitPos += codeWordSize;
while (outBitPos >= 8)
{
push(outBitBuf & 0xff);
outBitBuf >>= 8;
outBitPos -= 8;
}
}
void Compressor::write(u8 b)
{
if (transparent)
push(b);
if (b == escapeCode)
{
if (transparent)
push(EID);
escapeCode += 51;
}
int matchedLen = this->matchLen;
int code = acceptChar(b);
if (code != -1)
{
if (!transparent)
// 7.5 Transfer - output the last state of the string
pushCode(code);
// 7.8 Data compressibility test
// Same algorithm as the dreamcast modem driver, but we could use something different
// Accumulate the difference in bits between compressed and uncompressed size. Positive numbers
// indicate compressible data.
compressTest += 8 * matchedLen - (int)codeWordSize;
compressTestCount -= matchedLen;
if (compressTestCount <= 0)
{
if (transparent && compressTest > 16)
// Schedule a switch to compressed mode
changeMode();
else if (!transparent && compressTest < -16)
// Schedule a switch to transparent mode
changeMode();
}
}
}
void Compressor::changeMode()
{
if (transparent)
{
DEBUG_LOG(MODEM, "Compressor: switching to compressed mode");
// 7.8.1 Transition to compressed mode
push(escapeCode);
push(ECM);
transparent = false;
compressTestCount = 256;
}
else
{
DEBUG_LOG(MODEM, "Compressor:: switching to transparent mode");
// 7.8.2 Transition to transparent mode
flush(true);
transparent = true;
compressTestCount = 64;
}
modeTransition = true;
compressTest = 0;
}
void Compressor::reset()
{
V42Base::reset();
outBitBuf = 0;
outBitPos = 0;
compressTest = 0;
compressTestCount = 64;
}
void Compressor::checkDictSize()
{
if (transparent)
return;
while (nextCodeWord >= codeWordThreshold && codeWordThreshold <= maxCodeWords)
{
DEBUG_LOG(MODEM, "Compressor: STEPUP");
pushCode(STEPUP);
codeWordSize++;
codeWordThreshold *= 2;
}
}
void Compressor::flush(bool enterTransparent)
{
if (!transparent)
{
// Output the last state of the string
if (curCode != -1 && !modeTransition)
{
// 7.8.2(a) ensure that the codeword representing any partially encoded data has been transferred
pushCode(curCode);
modeTransition = true;
}
if (enterTransparent) {
// 7.8.2(c) indicate to the peer data compression function by transferring the ETM control codeword a
// transition to transparent mode
pushCode(ETM);
}
else {
// 7.9(c) if the previous step leaves extra bits pending for transmission (octet alignment not yet achieved), then
// transfer the FLUSH codeword
if ((outBitPos % 8) != 0)
pushCode(FLUSH);
}
// 7.8.2(d), 7.9(c) transmit sufficient 0 bits to recover octet alignment
while (outBitPos > 0)
{
push(outBitBuf & 0xff);
outBitBuf >>= 8;
outBitPos -= 8;
}
outBitPos = 0;
}
}
Decompressor::Decompressor(u32 maxCodeWords, int maxStringLength)
: V42Base(maxCodeWords, maxStringLength)
{
reset();
decodeBuf.reserve(maxStringLength);
}
void Decompressor::write(u8 b)
{
if (transparent)
{
if (escaped)
{
escaped = false;
bool acceptChar = false;
switch (b)
{
case ECM:
DEBUG_LOG(MODEM, "Decompressor: ECM");
transparent = false;
modeTransition = true;
break;
case EID:
DEBUG_LOG(MODEM, "Decompressor: EID");
acceptChar = true; // the escape code needs to be processed normally
b = escapeCode;
escapeCode += 51;
break;
case RESET:
DEBUG_LOG(MODEM, "Decompressor: RESET");
reset();
break;
default:
// 5.8(a) receipt of a reserved command code is an error condition
WARN_LOG(MODEM, "V.42bis: invalid command code %02x", b);
throw Exception("Invalid command code");
}
if (!acceptChar)
return;
}
else if (b == escapeCode) {
escaped = true;
return;
}
push(b);
acceptChar(b);
}
else
{
inputBitBuffer |= (u32)b << inputBitCount;
inputBitCount += 8;
if (inputBitCount < (int)codeWordSize)
return;
u32 newCode = (inputBitBuffer << (32 - codeWordSize)) >> (32 - codeWordSize);
inputBitCount -= codeWordSize;
inputBitBuffer >>= codeWordSize;
if (newCode < CW_RESERVED)
{
// Control codes
switch (newCode)
{
case ETM:
DEBUG_LOG(MODEM, "Decompressor: ETM");
transparent = true;
modeTransition = true;
inputBitCount = 0;
inputBitBuffer = 0;
break;
case FLUSH:
DEBUG_LOG(MODEM, "Decompressor: FLUSH");
flush();
break;
case STEPUP:
DEBUG_LOG(MODEM, "Decompressor: STEPUP");
++codeWordSize;
codeWordThreshold *= 2;
// 5.8(a) receipt of a STEPUP codeword when it would cause the value of C2 to exceed N1 is an error condition
if (codeWordThreshold > maxCodeWords)
throw Exception("Decompressor dictionary size exceeded limit");
break;
}
return;
}
modeTransition = false;
Node *node = &dict.get(newCode);
// 5.8(c) receipt of a codeword representing an empty dictionary entry is an error condition
if (node->isFree()) {
WARN_LOG(MODEM, "Unknown code %x", newCode);
throw Exception("Unknown V.42bis code");
}
// Walk up the tree one character at a time
decodeBuf.clear();
u8 firstChar;
while (true)
{
firstChar = node->character;
decodeBuf.insert(decodeBuf.begin(), firstChar);
if (node->isRoot())
break;
node = node->parent;
}
// Output the decoded string
for (u8 v : decodeBuf)
{
push(v);
if (v == escapeCode)
escapeCode += 51;
}
// 6.4 Add the string to the dictionary
if (matchLen < maxStringLength
// 6.4(a) The string does not exceed N7 in length
&& lastAddedCode != curCode
// 6.4(b) The string is not already in the dictionary
&& dict.getChild(curCode, firstChar) == nullptr)
{
dict.addChild(nextCodeWord, dict.get(curCode), firstChar);
recoverNode();
lastAddedCode = nextCodeWord;
}
else {
lastAddedCode = -1;
}
curCode = newCode;
matchLen = decodeBuf.size();
}
}
void Decompressor::flush()
{
// 7.9(c) if step a) leaves extra bits pending for transmission (octet alignment not yet achieved),
// then transfer sufficient 0 bits to recover octet alignment
inputBitBuffer >>= inputBitCount & 7;
inputBitCount &= ~7;
}
void Decompressor::reset()
{
V42Base::reset();
escaped = false;
inputBitBuffer = 0;
inputBitCount = 0;
}
} // namespace modem::v42b
+198
View File
@@ -0,0 +1,198 @@
/*
Copyright 2025 flyinghead
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "types.h"
#include "internal.h"
#include <deque>
#include <map>
#include <vector>
#include <stdexcept>
namespace modem::v42b
{
// N3 Character size (bits)
static constexpr u32 CHAR_SIZE = 8;
// N4 Number of characters in the alphabet
static constexpr u32 ALPHABET_SIZE = 1 << CHAR_SIZE;
// N6 Number of control codewords
static constexpr u32 CW_RESERVED = 3;
// N5 Index number of first dictionary entry used to store a string
static constexpr u32 CW_FIRST = ALPHABET_SIZE + CW_RESERVED;
// 5.1 Minimum and default value for P1 (Total number of codewords)
static constexpr u32 MIN_TOTAL_CODEWORDS = 512;
// 5.1 Valid range for P2 (Maximum string length)
static constexpr int DEF_STRING_SIZE = 6;
static constexpr int MAX_STRING_SIZE = 250;
class Exception : public std::runtime_error
{
public:
Exception(const char *what)
: std::runtime_error(what)
{}
};
class V42Base : public BufferedTransformer
{
protected:
struct Node
{
u8 character;
Node *parent;
std::map<int, Node *> children;
Node(u8 character = 0, Node *parent = nullptr)
: character(character), parent(parent)
{}
bool isLeaf() const {
return children.empty();
}
bool isFree() const {
return parent == nullptr;
}
bool isRoot() const {
return parent == reinterpret_cast<Node *>(-1);
}
Node *getChild(u8 octet)
{
auto it = children.find(octet);
if (it == children.end())
return nullptr;
else
return it->second;
}
void addChild(Node& child) {
children[child.character] = &child;
}
void removeChild(Node &child) {
children.erase(child.character);
child.parent = nullptr;
}
static Node makeRootNode(u8 character) {
return Node{ character, reinterpret_cast<Node *>(-1) };
}
};
class Dictionary
{
public:
Dictionary(u32 maxCodeWords) {
nodes.resize(maxCodeWords);
reset();
}
void reset();
Node& get(u16 code) {
return nodes[code];
}
u16 getCode(Node *node) const {
return node - &nodes[0];
}
Node *getChild(u16 code, u8 childCharacter) {
return nodes[code].getChild(childCharacter);
}
void addChild(u16 childCode, Node& parent, u8 childCharacter);
private:
std::vector<Node> nodes;
};
V42Base(u32 maxCodeWords, int maxStringLength)
: maxCodeWords(maxCodeWords), maxStringLength(maxStringLength), dict(maxCodeWords)
{
}
void push(u8 b) {
buffer.push_back(b);
}
void reset();
void recoverNode();
int acceptChar(u8 octet);
virtual void checkDictSize() {}
u32 maxCodeWords; // V.42 param N2
int maxStringLength; // V.42 param N7
u32 nextCodeWord = CW_FIRST; // V.42 param C1
u32 codeWordSize = CHAR_SIZE + 1; // V.42 param C2
u32 codeWordThreshold = 2 * ALPHABET_SIZE; // V.42 param C3
Dictionary dict;
int lastAddedCode = -1;
int matchLen = 0;
int curCode = -1;
bool transparent = true;
// This flag indicates that the current codeword has already been transfered
// and that string matching should restart (but dictionary update should proceed as normal)
// Used for mode transitions, and flush in compressed mode
bool modeTransition = false;
u8 escapeCode = 0;
};
class Compressor : public V42Base
{
public:
Compressor(u32 maxCodeWords = MIN_TOTAL_CODEWORDS, int maxStringLength = DEF_STRING_SIZE);
void write(u8 v) override;
void changeMode();
void reset();
void flush() {
flush(false);
}
private:
void checkDictSize() override;
void flush(bool enterTransparent);
int compressTest = 0;
int compressTestCount = 64;
u32 outBitBuf = 0;
int outBitPos = 0;
void pushCode(u16 code);
};
class Decompressor : public V42Base
{
public:
Decompressor(u32 maxCodeWords = MIN_TOTAL_CODEWORDS, int maxStringLength = DEF_STRING_SIZE);
void write(u8 v) override;
void reset();
private:
void flush();
bool escaped = false;
u32 inputBitBuffer = 0;
int inputBitCount = 0;
std::vector<u8> decodeBuf;
};
} // namespace modem::42b
+3 -2
View File
@@ -21,6 +21,7 @@
#include "hw/sh4/modules/modules.h"
#include "hw/maple/maple_cfg.h"
#include "hw/maple/maple_devs.h"
#include "oslib/i18n.h"
#include <deque>
#include <memory>
#include <cerrno>
@@ -287,7 +288,7 @@ protected:
case CARD_EJECT:
NOTICE_LOG(NAOMI, "Card ejected");
if (cardInserted)
os_notify("Card ejected", 2000);
os_notify(i18n::T("Card ejected"), 2000);
cardInserted = false;
status1 = getStatus1();
break;
@@ -581,7 +582,7 @@ private:
case CARD_EJECT:
NOTICE_LOG(NAOMI, "Card ejected");
if (cardInserted)
os_notify("Card ejected", 2000);
os_notify(i18n::T("Card ejected"), 2000);
cardInserted = false;
break;
case CARD_NEW:
+4 -3
View File
@@ -21,6 +21,7 @@
#include "serialize.h"
#include "hw/sh4/sh4_sched.h"
#include "naomi.h"
#include "oslib/i18n.h"
#include <algorithm>
/*
@@ -520,7 +521,7 @@ void GDCartridge::device_start(LoadProgress *progress, std::vector<u8> *digest)
}
}
if (gdrom == nullptr)
throw NaomiCartException("Naomi GDROM: Cannot open " + gdrom_path + ".chd");
throw NaomiCartException(strprintf(i18n::T("Naomi GDROM: Cannot open %s.chd"), gdrom_path.c_str()));
}
// primary volume descriptor
@@ -593,7 +594,7 @@ void GDCartridge::device_start(LoadProgress *progress, std::vector<u8> *digest)
;
dimm_data = (u8 *)malloc(dimm_data_size);
if (dimm_data == nullptr)
throw NaomiCartException("Memory allocation failed");
throw NaomiCartException(i18n::Ts("Memory allocation failed"));
if (dimm_data_size != file_rounded_size)
memset(dimm_data + file_rounded_size, 0, dimm_data_size - file_rounded_size);
@@ -605,7 +606,7 @@ void GDCartridge::device_start(LoadProgress *progress, std::vector<u8> *digest)
}
if (!dimm_data)
throw NaomiCartException("Naomi GDROM: Could not find the file to decrypt.");
throw NaomiCartException(i18n::Ts("Naomi GDROM: Could not find the file to decrypt."));
}
}
+3 -2
View File
@@ -24,6 +24,7 @@
#include "input/haptic.h"
#include "oslib/oslib.h"
#include "network/output.h"
#include "oslib/i18n.h"
namespace midiffb {
@@ -72,7 +73,7 @@ static void midiReceiver(u8 data)
haptic::stopAll(0);
if (calibrating) {
calibrating = false;
os_notify("Calibration done", 2000);
os_notify(i18n::T("Calibration done"), 2000);
}
}
else if (midiTxBuf[2] == 1)
@@ -197,7 +198,7 @@ static void midiReceiver(u8 data)
case 0x7f:
// Reset board
// FIXME also set when entering the service menu (kingrt66)
os_notify("Calibrating the wheel. Keep it centered.", 10000);
os_notify(i18n::T("Calibrating the wheel. Keep it centered."), 10000);
calibrating = true;
haptic::setSpring(0, 0.8f, 1.f);
position = 8192.f;
+4 -4
View File
@@ -47,7 +47,7 @@ Multiboard::Multiboard()
{
sharedMem = nullptr;
boardId = cfgLoadInt("naomi", "BoardId", 0);
boardId = config::loadInt("naomi", "BoardId");
#ifdef _WIN32
const char *FileName = "Local\\naomi_multiboard_mem";
if (isMaster())
@@ -124,9 +124,9 @@ void Multiboard::startSlave()
for (int i = 0; i < boardCount; i++)
sharedMem->boardReady[i] = false;
int x = cfgLoadInt("window", "left", (1920 - 640) / 2);
int y = cfgLoadInt("window", "top", (1080 - 480) / 2);
int width = cfgLoadInt("window", "width", 640);
int x = config::loadInt("window", "left", (1920 - 640) / 2);
int y = config::loadInt("window", "top", (1080 - 480) / 2);
int width = config::loadInt("window", "width", 640);
std::string biosFile = CurrentCartridge->game->bios == nullptr ? "naomi" : CurrentCartridge->game->bios;
std::string biosPath = hostfs::findNaomiBios(biosFile + ".zip");
if (biosPath.empty())
+3 -3
View File
@@ -33,6 +33,7 @@
#include "hw/flashrom/x76f100.h"
#include "midiffb.h"
#include "atomiswave.h"
#include "oslib/i18n.h"
#include <algorithm>
@@ -346,9 +347,8 @@ struct DriveSimPipe : public SerialPort::Pipe
}
if (!config::NetworkOutput)
{
char message[16];
snprintf(message, sizeof(message), "Speed: %3d", speed);
os_notify(message, 1000);
std::string message = strprintf(i18n::T("Speed: %3d"), speed);
os_notify(message.c_str(), 1000);
}
}
buffer.clear();
+27 -24
View File
@@ -46,6 +46,9 @@
#include "systemsp.h"
#include "hopper.h"
#include "midiffb.h"
#include "oslib/i18n.h"
using namespace i18n;
Cartridge *CurrentCartridge;
bool bios_loaded = false;
@@ -142,7 +145,7 @@ static bool loadBios(const char *filename, Archive *child_archive, Archive *pare
// FIXME memory leak
naomi_default_eeprom = (u8 *)malloc(bios->blobs[romid].length);
if (naomi_default_eeprom == nullptr)
throw NaomiCartException("Memory allocation failed");
throw NaomiCartException(Ts("Memory allocation failed"));
u32 read = file->Read(naomi_default_eeprom, bios->blobs[romid].length);
for (unsigned i = 0; i < bios->blobs[romid].length; i += 2)
@@ -186,7 +189,7 @@ void naomi_cart_LoadBios(const char *filename)
if (settings.naomi.slave)
{
if (!loadBios(filename, nullptr, nullptr, config::Region))
throw NaomiCartException(std::string("Error: cannot load BIOS ") + filename);
throw NaomiCartException(strprintf(T("Error: cannot load BIOS %s"), filename));
bios_loaded = true;
return;
}
@@ -218,7 +221,7 @@ void naomi_cart_LoadBios(const char *filename)
{
// If a specific BIOS is needed for this game, fail.
if (game->bios != nullptr || !bios_loaded)
throw NaomiCartException(std::string("Error: cannot load BIOS ") + (game->bios != nullptr ? game->bios : "naomi.zip"));
throw NaomiCartException(strprintf(T("Error: cannot load BIOS %s"), game->bios != nullptr ? game->bios : "naomi.zip"));
// otherwise use the default BIOS
}
@@ -230,7 +233,7 @@ static void loadMameRom(const std::string& path, const std::string& fileName, Lo
{
const Game *game = FindGame(fileName.c_str());
if (game == nullptr)
throw NaomiCartException("Unknown game");
throw NaomiCartException(Ts("Unknown game"));
// Open archive and parent archive if any
std::unique_ptr<Archive> archive(OpenArchive(path));
@@ -256,9 +259,9 @@ static void loadMameRom(const std::string& path, const std::string& fileName, Lo
if (archive == nullptr && parent_archive == nullptr)
{
if (game->parent_name != nullptr)
throw NaomiCartException(std::string("Cannot open ") + fileName + std::string(" or ") + game->parent_name);
throw NaomiCartException(strprintf(T("Cannot open %s or %s"), fileName.c_str(), game->parent_name));
else
throw NaomiCartException(std::string("Cannot open ") + fileName);
throw NaomiCartException(strprintf(T("Cannot open %s"), fileName.c_str()));
}
// Load the BIOS
@@ -337,7 +340,7 @@ static void loadMameRom(const std::string& path, const std::string& fileName, Lo
u8 *dst = (u8 *)CurrentCartridge->GetPtr(game->blobs[romid].offset, len);
u8 *src = (u8 *)CurrentCartridge->GetPtr(game->blobs[romid].src_offset, len);
if (dst == nullptr || src == nullptr)
throw NaomiCartException("Invalid ROM");
throw NaomiCartException(Ts("Invalid ROM"));
memcpy(dst, src, game->blobs[romid].length);
DEBUG_LOG(NAOMI, "Copied: %x bytes from %07x to %07x", game->blobs[romid].length, game->blobs[romid].src_offset, game->blobs[romid].offset);
}
@@ -358,7 +361,7 @@ static void loadMameRom(const std::string& path, const std::string& fileName, Lo
WARN_LOG(NAOMI, "%s: Cannot open %s", fileName.c_str(), game->blobs[romid].filename);
if (game->blobs[romid].blob_type != Eeprom)
// Default eeprom file is optional
throw NaomiCartException(std::string("Cannot find ") + game->blobs[romid].filename);
throw NaomiCartException(strprintf(T("Cannot find %s"), game->blobs[romid].filename));
else
continue;
}
@@ -368,7 +371,7 @@ static void loadMameRom(const std::string& path, const std::string& fileName, Lo
{
u8 *dst = (u8 *)CurrentCartridge->GetPtr(game->blobs[romid].offset, len);
if (dst == nullptr)
throw NaomiCartException(std::string("Invalid ROM: truncated ") + game->blobs[romid].filename);
throw NaomiCartException(strprintf(T("Invalid ROM: truncated %s"), game->blobs[romid].filename));
u32 read = file->Read(dst, game->blobs[romid].length);
if (config::GGPOEnable)
md5.add(dst, game->blobs[romid].length);
@@ -380,12 +383,12 @@ static void loadMameRom(const std::string& path, const std::string& fileName, Lo
{
u8 *buf = (u8 *)malloc(game->blobs[romid].length);
if (buf == nullptr)
throw NaomiCartException("Memory allocation failed");
throw NaomiCartException(Ts("Memory allocation failed"));
u32 read = file->Read(buf, game->blobs[romid].length);
u16 *to = (u16 *)CurrentCartridge->GetPtr(game->blobs[romid].offset, len);
if (to == nullptr)
throw NaomiCartException(std::string("Invalid ROM: truncated ") + game->blobs[romid].filename);
throw NaomiCartException(strprintf(T("Invalid ROM: truncated %s"), game->blobs[romid].filename));
u16 *from = (u16 *)buf;
for (int i = game->blobs[romid].length / 2; --i >= 0; to++)
*to++ = *from++;
@@ -400,7 +403,7 @@ static void loadMameRom(const std::string& path, const std::string& fileName, Lo
{
u8 *buf = (u8 *)malloc(game->blobs[romid].length);
if (buf == nullptr)
throw NaomiCartException("Memory allocation failed");
throw NaomiCartException(Ts("Memory allocation failed"));
u32 read = file->Read(buf, game->blobs[romid].length);
CurrentCartridge->SetKeyData(buf);
@@ -426,7 +429,7 @@ static void loadMameRom(const std::string& path, const std::string& fileName, Lo
{
naomi_default_eeprom = (u8 *)malloc(game->blobs[romid].length);
if (naomi_default_eeprom == nullptr)
throw NaomiCartException("Memory allocation failed");
throw NaomiCartException(Ts("Memory allocation failed"));
u32 read = file->Read(naomi_default_eeprom, game->blobs[romid].length);
if (config::GGPOEnable)
@@ -478,7 +481,7 @@ static void loadDecryptedRom(const std::string& path, const std::string& fileNam
if (!loadBios("naomi", NULL, NULL, -1))
{
if (!bios_loaded)
throw FlycastException("Error: cannot load BIOS from naomi.zip");
throw FlycastException(Ts("Error: cannot load BIOS from naomi.zip"));
}
}
@@ -494,7 +497,7 @@ static void loadDecryptedRom(const std::string& path, const std::string& fileNam
// LST file
FILE *fl = hostfs::storage().openFile(path, "r");
if (!fl)
throw FlycastException("Error: can't open " + path);
throw FlycastException(strprintf(T("Error: can't open %s"), path.c_str()));
folder = hostfs::storage().getParentPath(path);
char t[512];
@@ -502,7 +505,7 @@ static void loadDecryptedRom(const std::string& path, const std::string& fileNam
if (!line)
{
std::fclose(fl);
throw FlycastException("Error: Invalid LST file");
throw FlycastException(Ts("Error: Invalid LST file"));
}
char* eon = strstr(line, "\n");
@@ -518,7 +521,7 @@ static void loadDecryptedRom(const std::string& path, const std::string& fileNam
if (!line)
{
std::fclose(fl);
throw FlycastException("Error: Invalid LST file");
throw FlycastException(Ts("Error: Invalid LST file"));
}
while (line)
@@ -544,7 +547,7 @@ static void loadDecryptedRom(const std::string& path, const std::string& fileNam
// BIN loading
FILE* fp = hostfs::storage().openFile(path, "rb");
if (fp == nullptr)
throw FlycastException("Error: can't open " + path);
throw FlycastException(strprintf(T("Error: can't open %s"), path.c_str()));
std::fseek(fp, 0, SEEK_END);
u32 file_size = (u32)std::ftell(fp);
@@ -557,14 +560,14 @@ static void loadDecryptedRom(const std::string& path, const std::string& fileNam
INFO_LOG(NAOMI, "+%zd romfiles, %.2f MB set address space", files.size(), romSize / 1024.f / 1024.f);
if (romSize == 0)
throw FlycastException("Invalid empty ROM");
throw FlycastException(Ts("Invalid empty ROM"));
MD5Sum md5;
// Allocate space for the rom
u8 *romBase = (u8 *)malloc(romSize);
if (romBase == nullptr)
throw FlycastException("Out of memory");
throw FlycastException(Ts("Out of memory"));
bool load_error = false;
@@ -615,7 +618,7 @@ static void loadDecryptedRom(const std::string& path, const std::string& fileNam
if (load_error)
{
free(romBase);
throw FlycastException("Error: Failed to load BIN/DAT file");
throw FlycastException(Ts("Error: Failed to load BIN/DAT file"));
}
if (config::GGPOEnable)
md5.getDigest(settings.network.md5.game);
@@ -740,8 +743,8 @@ void naomi_cart_LoadRom(const std::string& path, const std::string& fileName, Lo
config::LocalPort.override(config::LocalPort + settings.naomi.drivingSimSlave);
if (settings.naomi.drivingSimSlave == 0)
{
int x = cfgLoadInt("window", "left", (1920 - 640) / 2);
int w = cfgLoadInt("window", "width", 640);
int x = config::loadInt("window", "left", (1920 - 640) / 2);
int w = config::loadInt("window", "width", 640);
std::string region = "config:Dreamcast.Region=" + std::to_string(config::Region);
for (int i = 0; i < 2; i++)
{
@@ -838,7 +841,7 @@ Cartridge::Cartridge(u32 size)
{
RomPtr = (u8 *)malloc(size);
if (RomPtr == nullptr)
throw NaomiCartException("Memory allocation failed");
throw NaomiCartException(Ts("Memory allocation failed"));
RomSize = size;
if (size != 0)
memset(RomPtr, 0xFF, RomSize);
+2 -1
View File
@@ -31,6 +31,7 @@
#include "network/naomi_network.h"
#include "emulator.h"
#include "oslib/oslib.h"
#include "oslib/i18n.h"
#include <chrono>
#include <memory>
@@ -72,7 +73,7 @@ void NaomiM3Comm::closeNetwork()
void NaomiM3Comm::connectNetwork()
{
os_notify("Network started", 5000);
os_notify(i18n::T("Network started"), 5000);
packet_number = 0;
slot_count = naomiNetwork.getSlotCount();
slot_id = naomiNetwork.getSlotId();
+122 -122
View File
@@ -1426,23 +1426,23 @@ const Game Games[] =
M2,
ROT0,
{
{ "epr-22284a.ic22", 0x0000000, 0x400000 },
{ "mpr-22223.ic1", 0x0800000, 0x800000 },
{ "mpr-22224.ic2", 0x1000000, 0x800000 },
{ "mpr-22225.ic3", 0x1800000, 0x800000 },
{ "mpr-22226.ic4", 0x2000000, 0x800000 },
{ "mpr-22227.ic5", 0x2800000, 0x800000 },
{ "mpr-22228.ic6", 0x3000000, 0x800000 },
{ "mpr-22229.ic7", 0x3800000, 0x800000 },
{ "mpr-22230.ic8", 0x4000000, 0x800000 },
{ "mpr-22231.ic9", 0x4800000, 0x800000 },
{ "mpr-22232.ic10", 0x5000000, 0x800000 },
{ "mpr-22233.ic11", 0x5800000, 0x800000 },
{ "mpr-22234.ic12s", 0x6000000, 0x800000 },
{ "mpr-22235.ic13s", 0x6800000, 0x800000 },
{ "mpr-22236.ic14s", 0x7000000, 0x800000 },
{ "mpr-22237.ic15s", 0x7800000, 0x800000 },
{ "mpr-22238.ic16s", 0x8000000, 0x800000 },
{ "epr-22284a.ic22", 0x0000000, 0x400000, 0x1e8e067c },
{ "mpr-22223.ic1", 0x0800000, 0x800000, 0x5159df22 },
{ "mpr-22224.ic2", 0x1000000, 0x800000, 0xef8bc5a0 },
{ "mpr-22225.ic3", 0x1800000, 0x800000, 0x80e0fdbd },
{ "mpr-22226.ic4", 0x2000000, 0x800000, 0xea940bcf },
{ "mpr-22227.ic5", 0x2800000, 0x800000, 0x300ed93b },
{ "mpr-22228.ic6", 0x3000000, 0x800000, 0x3dd062c7 },
{ "mpr-22229.ic7", 0x3800000, 0x800000, 0xda624f37 },
{ "mpr-22230.ic8", 0x4000000, 0x800000, 0x5a806bb6 },
{ "mpr-22231.ic9", 0x4800000, 0x800000, 0xc633d57d },
{ "mpr-22232.ic10", 0x5000000, 0x800000, 0x6f25a6dd },
{ "mpr-22233.ic11", 0x5800000, 0x800000, 0xcf3ac2df },
{ "mpr-22234.ic12s", 0x6000000, 0x800000, 0xd8c41648 },
{ "mpr-22235.ic13s", 0x6800000, 0x800000, 0xf1dedac5 },
{ "mpr-22236.ic14s", 0x7000000, 0x800000, 0x85f54964 },
{ "mpr-22237.ic15s", 0x7800000, 0x800000, 0x718dd6bf },
{ "mpr-22238.ic16s", 0x8000000, 0x800000, 0xfb3e55da },
},
nullptr,
nullptr,
@@ -1460,21 +1460,21 @@ const Game Games[] =
M2,
ROT0,
{
{ "epr-22099b.ic22", 0x0000000, 0x0400000 },
{ "mpr-22085.ic1", 0x0800000, 0x0800000 },
{ "mpr-22086.ic2", 0x1000000, 0x0800000 },
{ "mpr-22087.ic3", 0x1800000, 0x0800000 },
{ "mpr-22088.ic4", 0x2000000, 0x0800000 },
{ "mpr-22089.ic5", 0x2800000, 0x0800000 },
{ "mpr-22090.ic6", 0x3000000, 0x0800000 },
{ "mpr-22091.ic7", 0x3800000, 0x0800000 },
{ "mpr-22092.ic8", 0x4000000, 0x0800000 },
{ "mpr-22093.ic9", 0x4800000, 0x0800000 },
{ "mpr-22094.ic10", 0x5000000, 0x0800000 },
{ "mpr-22095.ic11", 0x5800000, 0x0800000 },
{ "mpr-22096.ic12s", 0x6000000, 0x0800000 },
{ "mpr-22097.ic13s", 0x6800000, 0x0800000 },
{ "mpr-22098.ic14s", 0x7000000, 0x0800000 },
{ "epr-22099b.ic22", 0x0000000, 0x0400000, 0x5e708879 },
{ "mpr-22085.ic1", 0x0800000, 0x0800000, 0xfffe9cc5 },
{ "mpr-22086.ic2", 0x1000000, 0x0800000, 0x610fe214 },
{ "mpr-22087.ic3", 0x1800000, 0x0800000, 0xf0cd2a26 },
{ "mpr-22088.ic4", 0x2000000, 0x0800000, 0x62a7e6db },
{ "mpr-22089.ic5", 0x2800000, 0x0800000, 0xcb135eb6 },
{ "mpr-22090.ic6", 0x3000000, 0x0800000, 0x13e44d57 },
{ "mpr-22091.ic7", 0x3800000, 0x0800000, 0xefa1e2fc },
{ "mpr-22092.ic8", 0x4000000, 0x0800000, 0xde1ea163 },
{ "mpr-22093.ic9", 0x4800000, 0x0800000, 0xecbc523b },
{ "mpr-22094.ic10", 0x5000000, 0x0800000, 0x72af7a70 },
{ "mpr-22095.ic11", 0x5800000, 0x0800000, 0xae74b61a },
{ "mpr-22096.ic12s", 0x6000000, 0x0800000, 0xd8c41648 },
{ "mpr-22097.ic13s", 0x6800000, 0x0800000, 0xf1dedac5 },
{ "mpr-22098.ic14s", 0x7000000, 0x0800000, 0xf9824d2e },
},
nullptr,
nullptr,
@@ -1492,18 +1492,18 @@ const Game Games[] =
M2,
ROT0,
{
{ "epr-22306b.ic22", 0x0000000, 0x0400000 },
{ "mpr-22295.ic1", 0x0800000, 0x1000000 },
{ "mpr-22296.ic2", 0x1800000, 0x1000000 },
{ "mpr-22297.ic3", 0x2800000, 0x1000000 },
{ "mpr-22298.ic4", 0x3800000, 0x1000000 },
{ "mpr-22299.ic5", 0x4800000, 0x1000000 },
{ "mpr-22300.ic6", 0x5800000, 0x1000000 },
{ "mpr-22301.ic7", 0x6800000, 0x1000000 },
{ "mpr-22302.ic8", 0x7800000, 0x1000000 },
{ "mpr-22303.ic9", 0x8800000, 0x1000000 },
{ "mpr-22304.ic10", 0x9800000, 0x1000000 },
{ "mpr-22305.ic11", 0xa800000, 0x1000000 },
{ "epr-22306b.ic22", 0x0000000, 0x0400000, 0xfcac20eb },
{ "mpr-22295.ic1", 0x0800000, 0x1000000, 0x1085001b },
{ "mpr-22296.ic2", 0x1800000, 0x1000000, 0xf18cb28b },
{ "mpr-22297.ic3", 0x2800000, 0x1000000, 0xa78a527b },
{ "mpr-22298.ic4", 0x3800000, 0x1000000, 0xcd76c7ea },
{ "mpr-22299.ic5", 0x4800000, 0x1000000, 0xffb96da5 },
{ "mpr-22300.ic6", 0x5800000, 0x1000000, 0x6fb05214 },
{ "mpr-22301.ic7", 0x6800000, 0x1000000, 0x52eb076d },
{ "mpr-22302.ic8", 0x7800000, 0x1000000, 0x86767b0b },
{ "mpr-22303.ic9", 0x8800000, 0x1000000, 0x73a80bd5 },
{ "mpr-22304.ic10", 0x9800000, 0x1000000, 0x46c1fb1f },
{ "mpr-22305.ic11", 0xa800000, 0x1000000, 0x027d0e7b },
},
nullptr,
nullptr,
@@ -3184,14 +3184,14 @@ const Game Games[] =
M2,
ROT270,
{
{ "epr-23627.ic22", 0x0000000, 0x0400000 },
{ "mpr-23275.ic1", 0x0800000, 0x1000000 },
{ "mpr-23276.ic2", 0x1800000, 0x1000000 },
{ "mpr-23277.ic3", 0x2800000, 0x1000000 },
{ "mpr-23278.ic4", 0x3800000, 0x1000000 },
{ "mpr-23279.ic5", 0x4800000, 0x1000000 },
{ "mpr-23280.ic6", 0x5800000, 0x1000000 },
{ "mpr-23281.ic7", 0x6800000, 0x0800000 },
{ "epr-23627.ic22", 0x0000000, 0x0400000, 0x7caccc5e },
{ "mpr-23275.ic1", 0x0800000, 0x1000000, 0xffc62eab },
{ "mpr-23276.ic2", 0x1800000, 0x1000000, 0x8b7ce666 },
{ "mpr-23277.ic3", 0x2800000, 0x1000000, 0x47a6f9c5 },
{ "mpr-23278.ic4", 0x3800000, 0x1000000, 0xc12b189c },
{ "mpr-23279.ic5", 0x4800000, 0x1000000, 0xb8b39559 },
{ "mpr-23280.ic6", 0x5800000, 0x1000000, 0xb1c8daa2 },
{ "mpr-23281.ic7", 0x6800000, 0x0800000, 0xc0378369 },
//ROM_REGION(0x84, "some_eeprom", 0)
//ROM_LOAD( "sflash.ic46", 0x000000, 0x000084, CRC(4929e940) SHA1(f8c4277ca0ae5e36b2eed033cc731b8fc4fccafc) )
@@ -3208,25 +3208,25 @@ const Game Games[] =
M2,
ROT0,
{
{ "epr-23626.ic22", 0x0000000, 0x200000 },
{ "epr-23626.ic22", 0x0200000, 0x200000 }, //reload
{ "rom1.ic1s", 0x0800000, 0x800000 },
{ "rom2.ic2s", 0x1000000, 0x800000 },
{ "rom3.ic3s", 0x1800000, 0x800000 },
{ "rom4.ic4s", 0x2000000, 0x800000 },
{ "rom5.ic5s", 0x2800000, 0x800000 },
{ "rom6.ic6s", 0x3000000, 0x800000 },
{ "rom7.ic7s", 0x3800000, 0x800000 },
{ "rom8.ic8s", 0x4000000, 0x800000 },
{ "rom9.ic9s", 0x4800000, 0x800000 },
{ "rom10.ic10s", 0x5000000, 0x800000 },
{ "rom11.ic11s", 0x5800000, 0x800000 },
{ "rom12.ic12s", 0x6000000, 0x800000 },
{ "rom13.ic13s", 0x6800000, 0x800000 },
{ "rom14.ic14s", 0x7000000, 0x800000 },
{ "rom15.ic15s", 0x7800000, 0x800000 },
{ "rom16.ic16s", 0x8000000, 0x800000 },
{ "rom17.ic17s", 0x8800000, 0x800000 },
{ "epr-23626.ic22", 0x0000000, 0x200000, 0xd5893a19 },
{ "epr-23626.ic22", 0x0200000, 0x200000, 0xd5893a19 }, //reload
{ "rom1.ic1s", 0x0800000, 0x800000, 0xe45ab26f },
{ "rom2.ic2s", 0x1000000, 0x800000, 0x4d0e4e64 },
{ "rom3.ic3s", 0x1800000, 0x800000, 0xa18c7ce7 },
{ "rom4.ic4s", 0x2000000, 0x800000, 0x82001b50 },
{ "rom5.ic5s", 0x2800000, 0x800000, 0x5e4af8b2 },
{ "rom6.ic6s", 0x3000000, 0x800000, 0x2950c543 },
{ "rom7.ic7s", 0x3800000, 0x800000, 0xa2bb8ebb },
{ "rom8.ic8s", 0x4000000, 0x800000, 0xfde9b537 },
{ "rom9.ic9s", 0x4800000, 0x800000, 0x4db3e79a },
{ "rom10.ic10s", 0x5000000, 0x800000, 0x37167167 },
{ "rom11.ic11s", 0x5800000, 0x800000, 0x927f1edb },
{ "rom12.ic12s", 0x6000000, 0x800000, 0x05de610d },
{ "rom13.ic13s", 0x6800000, 0x800000, 0x17ed44c3 },
{ "rom14.ic14s", 0x7000000, 0x800000, 0x66d7e2a1 },
{ "rom15.ic15s", 0x7800000, 0x800000, 0x0c701416 },
{ "rom16.ic16s", 0x8000000, 0x800000, 0x5d8e6e8d },
{ "rom17.ic17s", 0x8800000, 0x800000, 0xb4c40606 },
// .18s chip is not present but is tested for an FF fill (pull-up resistors on the PCB's data bus presumably accomplish this)
//ROM_REGION(0x84, "some_eeprom", 0)
@@ -3244,11 +3244,11 @@ const Game Games[] =
M2,
ROT0,
{
{ "epr-23625.ic22", 0x0000000, 0x0400000 },
{ "mpr-23231.ic1", 0x0800000, 0x1000000 },
{ "mpr-23232.ic2", 0x1800000, 0x1000000 },
{ "mpr-23233.ic3", 0x2800000, 0x1000000 },
{ "mpr-23234.ic4", 0x3800000, 0x1000000 },
{ "epr-23625.ic22", 0x0000000, 0x0400000, 0x7300bc6c },
{ "mpr-23231.ic1", 0x0800000, 0x1000000, 0xe41ddc53 },
{ "mpr-23232.ic2", 0x1800000, 0x1000000, 0x30f963a0 },
{ "mpr-23233.ic3", 0x2800000, 0x1000000, 0xd6451cab },
{ "mpr-23234.ic4", 0x3800000, 0x1000000, 0x44044c14 },
//ROM_REGION(0x84, "some_eeprom", 0)
//ROM_LOAD( "sflash.ic46", 0x000000, 0x000084, CRC(951684e4) SHA1(0beaf5827064252293223b946c04b8698e7207bb) )
@@ -3265,14 +3265,14 @@ const Game Games[] =
M2,
ROT270,
{
{ "epr-24122a.ic22", 0x0000000, 0x0400000 },
{ "mpr-24123.ic1", 0x0800000, 0x1000000 },
{ "mpr-24124.ic2", 0x1800000, 0x1000000 },
{ "mpr-24125.ic3", 0x2800000, 0x1000000 },
{ "mpr-24126.ic4", 0x3800000, 0x1000000 },
{ "mpr-24127.ic5", 0x4800000, 0x1000000 },
{ "mpr-24128.ic6", 0x5800000, 0x1000000 },
{ "mpr-24129.ic7", 0x6800000, 0x1000000 },
{ "epr-24122a.ic22", 0x0000000, 0x0400000, 0x34ef47d2 },
{ "mpr-24123.ic1", 0x0800000, 0x1000000, 0x657f0c6d },
{ "mpr-24124.ic2", 0x1800000, 0x1000000, 0x034f2d83 },
{ "mpr-24125.ic3", 0x2800000, 0x1000000, 0x103aec1d },
{ "mpr-24126.ic4", 0x3800000, 0x1000000, 0x9a6f2f59 },
{ "mpr-24127.ic5", 0x4800000, 0x1000000, 0xbb4af13e },
{ "mpr-24128.ic6", 0x5800000, 0x1000000, 0xbfdbb853 },
{ "mpr-24129.ic7", 0x6800000, 0x1000000, 0xc3f0f06a },
}
},
// Star Horse Progress (backup data)
@@ -3286,8 +3286,8 @@ const Game Games[] =
M2,
ROT0,
{
{ "rom1.ic1s", 0x00800000, 0x00800000 },
{ "rom2.ic2s", 0x01000000, 0x00800000 },
{ "rom1.ic1s", 0x00800000, 0x00800000, 0xd1305180 },
{ "rom2.ic2s", 0x01000000, 0x00800000, 0xcfb9881d },
//ROM_REGION(0x84, "some_eeprom", 0)
//ROM_LOAD( "sflash.ic37", 0x000000, 0x000084, CRC(fe8f8f5c) SHA1(839461ab736e0228dec7e2512e1692d6ecc4e664) )
@@ -3304,18 +3304,18 @@ const Game Games[] =
M2,
ROT0,
{
{ "epr-24107a.ic22", 0x00000000, 0x00400000 },
{ "mpr-24108.ic1", 0x00800000, 0x01000000 },
{ "mpr-24109.ic2", 0x01800000, 0x01000000 },
{ "mpr-24110.ic3", 0x02800000, 0x01000000 },
{ "mpr-24111.ic4", 0x03800000, 0x01000000 },
{ "mpr-24112.ic5", 0x04800000, 0x01000000 },
{ "mpr-24113.ic6", 0x05800000, 0x01000000 },
{ "mpr-24114.ic7", 0x06800000, 0x01000000 },
{ "mpr-24115.ic8", 0x07800000, 0x01000000 },
{ "mpr-24116.ic9", 0x08800000, 0x01000000 },
{ "mpr-24117.ic10", 0x09800000, 0x01000000 },
{ "mpr-24118.ic11", 0x0a800000, 0x01000000 },
{ "epr-24107a.ic22", 0x00000000, 0x00400000, 0x8df0f545 },
{ "mpr-24108.ic1", 0x00800000, 0x01000000, 0xe8e9c09c },
{ "mpr-24109.ic2", 0x01800000, 0x01000000, 0xa5c103d0 },
{ "mpr-24110.ic3", 0x02800000, 0x01000000, 0x58a5a8c2 },
{ "mpr-24111.ic4", 0x03800000, 0x01000000, 0xdcb17013 },
{ "mpr-24112.ic5", 0x04800000, 0x01000000, 0x711e656b },
{ "mpr-24113.ic6", 0x05800000, 0x01000000, 0xd6c7d611 },
{ "mpr-24114.ic7", 0x06800000, 0x01000000, 0xcb1846b7 },
{ "mpr-24115.ic8", 0x07800000, 0x01000000, 0x73c5168d },
{ "mpr-24116.ic9", 0x08800000, 0x01000000, 0xe074c41a },
{ "mpr-24117.ic10", 0x09800000, 0x01000000, 0xd6d33ab2 },
{ "mpr-24118.ic11", 0x0a800000, 0x01000000, 0x99b64022 },
}
},
// Star Horse Progress (main screens)
@@ -3329,13 +3329,13 @@ const Game Games[] =
M2,
ROT0,
{
{ "epr-24087b.ic22", 0x00000000, 0x00400000 },
{ "mpr-24088.ic1", 0x00800000, 0x01000000 },
{ "mpr-24089.ic2", 0x01800000, 0x01000000 },
{ "mpr-24090.ic3", 0x02800000, 0x01000000 },
{ "mpr-24091.ic4", 0x03800000, 0x01000000 },
{ "mpr-24092.ic5", 0x04800000, 0x01000000 },
{ "mpr-24093.ic6", 0x05800000, 0x01000000 },
{ "epr-24087b.ic22", 0x00000000, 0x00400000, 0x9388f109 },
{ "mpr-24088.ic1", 0x00800000, 0x01000000, 0x386d9a06 },
{ "mpr-24089.ic2", 0x01800000, 0x01000000, 0x404e231b },
{ "mpr-24090.ic3", 0x02800000, 0x01000000, 0x48fb173d },
{ "mpr-24091.ic4", 0x03800000, 0x01000000, 0x7c92174c },
{ "mpr-24092.ic5", 0x04800000, 0x01000000, 0xd32299a6 },
{ "mpr-24093.ic6", 0x05800000, 0x01000000, 0x6491dd68 },
}
},
// Star Horse Progress (sound & backup)
@@ -3349,13 +3349,13 @@ const Game Games[] =
M2,
ROT0,
{
{ "epr-24097a.ic22", 0x00000000, 0x00400000 },
{ "mpr-24098.ic1", 0x00800000, 0x01000000 },
{ "mpr-24099.ic2", 0x01800000, 0x01000000 },
{ "mpr-24100.ic3", 0x02800000, 0x01000000 },
{ "mpr-24101.ic4", 0x03800000, 0x01000000 },
{ "mpr-24102.ic5", 0x04800000, 0x01000000 },
{ "mpr-24103.ic6", 0x05800000, 0x01000000 },
{ "epr-24097a.ic22", 0x00000000, 0x00400000, 0xec61a9e8 },
{ "mpr-24098.ic1", 0x00800000, 0x01000000, 0xc55b45be },
{ "mpr-24099.ic2", 0x01800000, 0x01000000, 0xe3f0f02e },
{ "mpr-24100.ic3", 0x02800000, 0x01000000, 0xa5f3cbe9 },
{ "mpr-24101.ic4", 0x03800000, 0x01000000, 0xdaaf9531 },
{ "mpr-24102.ic5", 0x04800000, 0x01000000, 0x55121ff3 },
{ "mpr-24103.ic6", 0x05800000, 0x01000000, 0xbfbc3569 },
}
},
// Star Horse 2001 (satellite, Rev B)
@@ -3369,14 +3369,14 @@ const Game Games[] =
M2,
ROT270,
{
{ "epr-23739b.ic22", 0x00000000, 0x00400000 },
{ "mpr-23740.ic1", 0x00800000, 0x01000000 },
{ "mpr-23741.ic2", 0x01800000, 0x01000000 },
{ "mpr-23742.ic3", 0x02800000, 0x01000000 },
{ "mpr-23743.ic4", 0x03800000, 0x01000000 },
{ "mpr-23744.ic5", 0x04800000, 0x01000000 },
{ "mpr-23745.ic6", 0x05800000, 0x01000000 },
{ "mpr-23746.ic7", 0x06800000, 0x01000000 },
{ "epr-23739b.ic22", 0x00000000, 0x00400000, 0x2d19b6a2 },
{ "mpr-23740.ic1", 0x00800000, 0x01000000, 0xe84f8611 },
{ "mpr-23741.ic2", 0x01800000, 0x01000000, 0x5fd84c54 },
{ "mpr-23742.ic3", 0x02800000, 0x01000000, 0x240e1779 },
{ "mpr-23743.ic4", 0x03800000, 0x01000000, 0xd9dc0a12 },
{ "mpr-23744.ic5", 0x04800000, 0x01000000, 0x95759982 },
{ "mpr-23745.ic6", 0x05800000, 0x01000000, 0x0eda5807 },
{ "mpr-23746.ic7", 0x06800000, 0x01000000, 0x955bb184 },
}
},
// Star Horse 2002 (main screens)
@@ -4966,8 +4966,8 @@ const Game Games[] =
M4,
ROT270,
{
{ "fpr-24489.ic8", 0x00000000, 0x4000000 },
{ "fpr-24490.ic9", 0x04000000, 0x4000000 },
{ "fpr-24489.ic8", 0x00000000, 0x4000000, 0x156797a4 },
{ "fpr-24490.ic9", 0x04000000, 0x4000000, 0xb6c40348 },
}
},
// Naomi GD Roms
+2 -1
View File
@@ -36,6 +36,7 @@
#else
#include "oslib/resources.h"
#endif
#include "oslib/i18n.h"
#include <stb/stb_image.h>
#include <stb/stb_image_write.h>
@@ -853,7 +854,7 @@ private:
const bool success = bitmapWriter->save(s);
bitmapWriter.reset();
if (success) {
os_notify("Print out saved", 5000, s.c_str());
os_notify(i18n::T("Print out saved"), 5000, s.c_str());
NOTICE_LOG(NAOMI, "%s", s.c_str());
}
}
+2 -1
View File
@@ -35,6 +35,7 @@
#include "naomi_roms.h"
#include "stdclass.h"
#include "hw/mem/addrspace.h"
#include "oslib/i18n.h"
#include <cerrno>
#include <deque>
@@ -2184,7 +2185,7 @@ void SystemSpCart::Init(LoadProgress *progress, std::vector<u8> *digest)
}
}
if (chd == nullptr)
throw NaomiCartException("SystemSP: Cannot open CompactFlash file " + gdrom_path);
throw NaomiCartException(strprintf(i18n::T("SystemSP: Cannot open CompactFlash file %s"), gdrom_path.c_str()));
BootIdLoader loader(*this);
romBootId.reset(loader.load());
+3 -2
View File
@@ -659,9 +659,10 @@ static bool dec_generic(u32 op)
state.info.has_fpu=true;
if (state.cpu.FPR64) {
// fallback to interpreter for double float ops
// except fmov, flds and fsts that don't depend on PR
// except fmov, flds, fsts and fneg that don't depend on PR
if (((op & 0xf) < 6 || (op & 0xf) > 0xc) // fmov
&& (op & 0xef) != 0x0d) // flds, flts
&& (op & 0xef) != 0x0d // flds, fsts
&& (op & 0xff) != 0x4d) // fneg
return false;
}
+7 -3
View File
@@ -5,6 +5,7 @@
#include "ngen.h"
#include "ssa.h"
#include <sstream>
#include <locale>
void AnalyseBlock(RuntimeBlockInfo* blk)
{
@@ -64,7 +65,8 @@ u32* GetRegPtr(Sh4Context& ctx, u32 reg)
std::string name_reg(Sh4RegType reg)
{
std::stringstream ss;
std::ostringstream ss;
ss.imbue(std::locale::classic());
if (reg >= reg_fr_0 && reg <= reg_xf_15)
ss << "f" << (reg - reg_fr_0);
@@ -126,7 +128,8 @@ std::string name_reg(Sh4RegType reg)
static std::string dissasm_param(const shil_param& prm, bool comma)
{
std::stringstream ss;
std::ostringstream ss;
ss.imbue(std::locale::classic());
if (!prm.is_null() && comma)
ss << ", ";
@@ -170,7 +173,8 @@ static std::string dissasm_param(const shil_param& prm, bool comma)
std::string shil_opcode::dissasm() const
{
std::stringstream ss;
std::ostringstream ss;
ss.imbue(std::locale::classic());
ss << shilop_str[op] << " " << dissasm_param(rd,false) << dissasm_param(rd2,true) << " <- " << dissasm_param(rs1,false) << dissasm_param(rs2,true) << dissasm_param(rs3,true);
return ss.str();
}
+9 -15
View File
@@ -400,8 +400,8 @@ sh4op(i1111_nnnn_1010_1101)
//fipr <FV_M>,<FV_N>
sh4op(i1111_nnmm_1110_1101)
{
int n=GetN(op)&0xC;
int m=(GetN(op)&0x3)<<2;
int n = GetN(op) & 0xC;
int m = (GetN(op) & 0x3) << 2;
if (ctx->fpscr.PR == 0)
{
double idp = (double)ctx->fr[n + 0] * ctx->fr[m + 0];
@@ -411,9 +411,8 @@ sh4op(i1111_nnmm_1110_1101)
ctx->fr[n + 3] = fixNaN((float)idp);
}
else
{
die("FIPR Precision=1");
else {
iNimp("FIPR with FPSCR.PR=1");
}
}
@@ -474,11 +473,7 @@ sh4op(i1111_nnnn_0010_1101)
sh4op(i1111_nnnn_0100_1101)
{
u32 n = GetN(op);
if (ctx->fpscr.PR == 0)
ctx->fr_hex(n) ^= 0x80000000;
else
ctx->fr_hex(n & 0xE) ^= 0x80000000;
ctx->fr_hex(n) ^= 0x80000000;
}
@@ -581,7 +576,7 @@ sh4op(i1111_nn01_1111_1101)
XF[3] XF[7] XF[11] XF[15] FR[n+3] FR[n+3]
*/
u32 n=GetN(op)&0xC;
u32 n = GetN(op) & 0xC;
if (ctx->fpscr.PR==0)
{
@@ -610,13 +605,12 @@ sh4op(i1111_nn01_1111_1101)
ctx->fr[n + 2] = fixNaN((float)v3);
ctx->fr[n + 3] = fixNaN((float)v4);
}
else
{
iNimp("FTRV in dp mode");
else {
iNimp("FTRV with FPSCR.PR=1");
}
}
static void iNimp(const char *str)
{
WARN_LOG(INTERPRETER, "Unimplemented sh4 FPU instruction: %s", str);
WARN_LOG(INTERPRETER, "Unimplemented SH4 FPU op: %s", str);
}
+15 -4
View File
@@ -190,11 +190,22 @@ bool UTLB_Sync(u32 entry)
cache_entry(tlb_entry);
if (!mmu_enabled() && (tlb_entry.Address.VPN & (0xFC000000 >> 10)) == (0xE0000000 >> 10))
if (!mmu_enabled())
{
// Used when FullMMU is off
u32 vpn_sq = ((tlb_entry.Address.VPN & 0x7FFFF) >> 10) & 0x3F;//upper bits are always known [0xE0/E1/E2/E3]
sq_remap[vpn_sq] = tlb_entry.Data.PPN << 10;
if ((tlb_entry.Address.VPN & (0xFC000000 >> 10)) == (0xE0000000 >> 10))
{
// Used when FullMMU is off
u32 vpn_sq = ((tlb_entry.Address.VPN & 0x7FFFF) >> 10) & 0x3F;//upper bits are always known [0xE0/E1/E2/E3]
sq_remap[vpn_sq] = tlb_entry.Data.PPN << 10;
}
else if (CCN_MMUCR.AT == 1 && tlb_entry.Address.VPN != 0x30040 && tlb_entry.Address.VPN != 0x30000 && tlb_entry.Data.V == 1)
{
// Enable Full MMU if not an expected store queue mapping
// VPN checks to ignore many arcade and Visual Concepts games presumably bogus mappings
mmuOn = true;
NOTICE_LOG(SH4, "Enabling on-demand Full MMU support");
mmu_set_state();
}
}
return true;
}
+11 -11
View File
@@ -1,8 +1,8 @@
#include "common.h"
#include "stdclass.h"
#include "oslib/storage.h"
#include "deps/chdpsr/cdipsr.h"
#include "oslib/i18n.h"
#include "chdpsr/cdipsr.h"
Disc* cdi_parse(const char* file, std::vector<u8> *digest)
{
@@ -14,21 +14,21 @@ Disc* cdi_parse(const char* file, std::vector<u8> *digest)
if (fsource == nullptr)
{
WARN_LOG(GDROM, "Cannot open file '%s' errno %d", file, errno);
throw FlycastException(std::string("Cannot open CDI file ") + file);
throw FlycastException(strprintf(i18n::T("Cannot open CDI file %s"), file));
}
try {
image_s image = { 0 };
track_s track = { 0 };
if (!CDI_init(fsource, &image, file))
throw FlycastException(std::string("Invalid CDI file ") + file);
throw FlycastException(strprintf(i18n::T("Invalid CDI file %s"), file));
if (!CDI_get_sessions(fsource, &image)) {
WARN_LOG(GDROM, "Invalid CDI file '%s': can't get sessions", file);
throw FlycastException("Invalid CDI image");
throw FlycastException(i18n::Ts("Invalid CDI image"));
}
if (image.sessions > 255) {
WARN_LOG(GDROM, "Invalid CDI file '%s': too many sessions (%d)", file, image.sessions);
throw FlycastException("Invalid CDI image");
throw FlycastException(i18n::Ts("Invalid CDI image"));
}
std::unique_ptr<Disc> rv = std::make_unique<Disc>();
@@ -46,7 +46,7 @@ Disc* cdi_parse(const char* file, std::vector<u8> *digest)
if (!CDI_get_tracks(fsource, &image)) {
WARN_LOG(GDROM, "Invalid CDI file '%s': can't get tracks", file);
throw FlycastException("Invalid CDI image");
throw FlycastException(i18n::Ts("Invalid CDI image"));
}
image.header_position = std::ftell(fsource);
@@ -57,7 +57,7 @@ Disc* cdi_parse(const char* file, std::vector<u8> *digest)
{
if (image.tracks > 100) {
WARN_LOG(GDROM, "Invalid CDI file '%s': too many tracks (%d)", file, image.tracks);
throw FlycastException("Invalid CDI image");
throw FlycastException(i18n::Ts("Invalid CDI image"));
}
// Clear cuesheet
image.remaining_tracks = image.tracks;
@@ -70,12 +70,12 @@ Disc* cdi_parse(const char* file, std::vector<u8> *digest)
track.number = image.tracks - image.remaining_tracks + 1;
if (!CDI_read_track (fsource, &image, &track))
throw FlycastException("Invalid CDI image");
throw FlycastException(i18n::Ts("Invalid CDI image"));
if (track.sector_size != 2048 && track.sector_size != 2336 && track.sector_size != 2352 && track.sector_size != 2448)
{
WARN_LOG(GDROM, "Invalid sector size: %lu", track.sector_size);
throw FlycastException("Invalid CDI sector size");
throw FlycastException(i18n::Ts("Invalid CDI sector size"));
}
image.header_position = std::ftell(fsource);
@@ -123,7 +123,7 @@ Disc* cdi_parse(const char* file, std::vector<u8> *digest)
FILE *trackFile = hostfs::storage().openFile(file, "rb");
if (trackFile == nullptr) {
WARN_LOG(GDROM, "Cannot re-open file '%s' errno %d", file, errno);
throw FlycastException("Cannot re-open CDI file");
throw FlycastException(i18n::Ts("Cannot re-open CDI file"));
}
t.file = new RawTrackFile(trackFile, track.position + track.pregap_length * track.sector_size, t.StartFAD, track.sector_size);
+9 -9
View File
@@ -1,7 +1,7 @@
#include "common.h"
#include "stdclass.h"
#include "oslib/storage.h"
#include "oslib/i18n.h"
#include <libchdr/chd.h>
struct CHDDisc : Disc
@@ -106,7 +106,7 @@ static u32 getSectorSize(const std::string& type)
else if (type == "MODE2_RAW" || type == "MODE2/2352" || type == "CDI/2352")
return 2352; // CDROM XA Mode2 Data
throw FlycastException("chd: track type " + type + " is not supported");
throw FlycastException(strprintf(i18n::T("chd: track type %s is not supported"), type.c_str()));
}
void CHDDisc::tryOpen(const char* file)
@@ -115,13 +115,13 @@ void CHDDisc::tryOpen(const char* file)
if (fp == nullptr)
{
WARN_LOG(COMMON, "Cannot open file '%s' errno %d", file, errno);
throw FlycastException(std::string("Cannot open CHD file ") + file);
throw FlycastException(strprintf(i18n::T("Cannot open CHD file %s"), file));
}
chd_error err = chd_open_file(fp, CHD_OPEN_READ, 0, &chd);
if (err != CHDERR_NONE)
throw FlycastException(std::string("Invalid CHD file ") + file);
throw FlycastException(strprintf(i18n::T("Invalid CHD file %s"), file));
INFO_LOG(GDROM, "chd: parsing file %s", file);
@@ -134,7 +134,7 @@ void CHDDisc::tryOpen(const char* file)
sph = hunkbytes/(2352+96);
if (hunkbytes % (2352 + 96) != 0)
throw FlycastException(std::string("Invalid hunkbytes for CHD file ") + file);
throw FlycastException(strprintf(i18n::T("Invalid hunkbytes for CHD file %s"), file));
u32 tag;
u8 flags;
@@ -180,10 +180,10 @@ void CHDDisc::tryOpen(const char* file)
}
if (tkid != (int)tracks.size() + 1)
throw FlycastException("Unexpected track number");
throw FlycastException(i18n::Ts("Unexpected track number"));
if (strcmp(subtype, "NONE") != 0 || pregap != 0 || postgap != 0)
throw FlycastException("Unsupported subtype or pre/postgap");
throw FlycastException(i18n::Ts("Unsupported subtype or pre/postgap"));
DEBUG_LOG(GDROM, "%s", temp);
Track t;
@@ -209,13 +209,13 @@ void CHDDisc::tryOpen(const char* file)
if (total_frames != 549300)
WARN_LOG(GDROM, "WARNING: chd: Total GD-Rom frames is wrong: %u frames (549300 expected) in %zu tracks", total_frames, tracks.size());
if (tracks.size() < 3)
throw FlycastException("Invalid CHD: less than 3 tracks");
throw FlycastException(i18n::Ts("Invalid CHD: less than 3 tracks"));
FillGDSession();
}
else
{
if (tracks.empty())
throw FlycastException("Invalid CHD: no track found");
throw FlycastException(i18n::Ts("Invalid CHD: no track found"));
Session ses;
ses.FirstTrack = 1;
+3 -2
View File
@@ -4,6 +4,7 @@
#include "stdclass.h"
#include "hw/sh4/sh4_sched.h"
#include "serialize.h"
#include "oslib/i18n.h"
Disc* chd_parse(const char* file, std::vector<u8> *digest);
Disc* gdi_parse(const char* file, std::vector<u8> *digest);
@@ -93,7 +94,7 @@ Disc* OpenDisc(const std::string& path, std::vector<u8> *digest)
return disc;
}
throw FlycastException("Unknown disk format");
throw FlycastException(i18n::Ts("Unknown disk format"));
}
namespace gdr {
@@ -366,7 +367,7 @@ namespace gdr
void insertDisk(const std::string& path)
{
if (!doDiscSwap(path))
throw FlycastException("This media cannot be loaded");
throw FlycastException(i18n::Ts("This media cannot be loaded"));
settings.content.path = path;
// Drive is busy after the lid was closed
sns_asc = 4;
+5 -5
View File
@@ -111,11 +111,11 @@ struct Track
struct Disc
{
std::vector<Session> sessions; //info for sessions
std::vector<Track> tracks; //info for tracks
Track LeadOut; //info for lead out track (can't read from here)
u32 EndFAD; //Last valid disc sector
DiscType type;
std::vector<Session> sessions; // info for sessions
std::vector<Track> tracks; // info for tracks
Track LeadOut; // info for lead out track (can't read from here)
u32 EndFAD = 0; // Last valid disc sector
DiscType type = CdRom;
std::string catalog;
u32 ReadSectors(u32 FAD, u32 count, u8 *dst, u32 fmt, bool stopOnMiss = false, LoadProgress *progress = nullptr);
+41 -21
View File
@@ -20,6 +20,7 @@
#include "common.h"
#include "stdclass.h"
#include "oslib/storage.h"
#include "oslib/i18n.h"
#include <sstream>
static u32 getSectorSize(const std::string& type) {
@@ -53,7 +54,7 @@ Disc* cue_parse(const char* file, std::vector<u8> *digest)
if (fsource == nullptr)
{
WARN_LOG(COMMON, "Cannot open file '%s' errno %d", file, errno);
throw FlycastException(std::string("Cannot open CUE file ") + file);
throw FlycastException(strprintf(i18n::T("Cannot open CUE file %s"), file));
}
hostfs::FileInfo fileInfo = hostfs::storage().getFileInfo(file);
@@ -64,7 +65,7 @@ Disc* cue_parse(const char* file, std::vector<u8> *digest)
if (cue_len >= sizeof(cue_data))
{
std::fclose(fsource);
throw FlycastException("CUE parse error: CUE file too big");
throw FlycastException(i18n::Ts("CUE parse error: CUE file too big"));
}
if (std::fread(cue_data, 1, cue_len, fsource) != cue_len)
@@ -72,12 +73,13 @@ Disc* cue_parse(const char* file, std::vector<u8> *digest)
std::fclose(fsource);
std::istringstream istream(cue_data);
istream.imbue(std::locale::classic());
std::string basepath = hostfs::storage().getParentPath(file);
MD5Sum md5;
Disc* disc = new Disc();
std::unique_ptr<Disc> disc = std::make_unique<Disc>();
u32 current_fad = 150;
std::string track_filename;
u32 track_number = -1;
@@ -88,7 +90,8 @@ Disc* cue_parse(const char* file, std::vector<u8> *digest)
while (std::getline(istream, line))
{
std::stringstream cuesheet(line);
std::istringstream cuesheet(line);
cuesheet.imbue(std::locale::classic());
std::string token;
cuesheet >> token;
@@ -119,8 +122,10 @@ Disc* cue_parse(const char* file, std::vector<u8> *digest)
else
{
// GD-Rom
if (token == "HIGH-DENSITY")
if (token == "HIGH-DENSITY") {
current_fad = 45000 + 150;
disc->type = GdRom;
}
else if (token != "SINGLE-DENSITY")
{
INFO_LOG(GDROM, "CUE parse: unrecognized REM token %s. Expected SINGLE-DENSITY, HIGH-DENSITY or SESSION", token.c_str());
@@ -181,16 +186,11 @@ Disc* cue_parse(const char* file, std::vector<u8> *digest)
std::string path = hostfs::storage().getSubPath(basepath, track_filename);
FILE *track_file = hostfs::storage().openFile(path, "rb");
if (track_file == nullptr)
{
delete disc;
throw FlycastException("CUE file: cannot open track " + path);
}
throw FlycastException(strprintf(i18n::T("CUE file: cannot open track %s"), path.c_str()));
u32 sector_size = getSectorSize(track_type);
if (sector_size == 0)
{
if (sector_size == 0) {
std::fclose(track_file);
delete disc;
throw FlycastException("CUE file: track has unknown sector type: " + track_type);
throw FlycastException(strprintf(i18n::T("CUE file: track has unknown sector type: %s"), track_type.c_str()));
}
fileInfo = hostfs::storage().getFileInfo(path);
if (fileInfo.size % sector_size != 0)
@@ -222,21 +222,41 @@ Disc* cue_parse(const char* file, std::vector<u8> *digest)
}
}
if (disc->tracks.empty())
{
delete disc;
throw FlycastException("CUE parse error: failed to parse or invalid file with 0 tracks");
throw FlycastException(i18n::Ts("CUE parse error: failed to parse or invalid file with 0 tracks"));
if (disc->tracks.size() > 99) {
WARN_LOG(GDROM, "CUE: more than 99 tracks");
throw FlycastException(i18n::Ts("Invalid CUE file"));
}
if (session_number == 0)
{
if (disc->tracks.size() < 3) {
delete disc;
throw FlycastException("CUE parse error: less than 3 tracks");
if (disc->type == GdRom)
{
// GD-Rom
if (disc->tracks.size() < 3)
throw FlycastException(i18n::Ts("CUE parse error: less than 3 tracks"));
if (!disc->tracks[0].isDataTrack()) {
WARN_LOG(GDROM, "CUE: track 1 must be a data track");
throw FlycastException(i18n::Ts("Invalid CUE file"));
}
if (disc->tracks[1].isDataTrack()) {
WARN_LOG(GDROM, "CUE: track 2 must be an audio track");
throw FlycastException(i18n::Ts("Invalid CUE file"));
}
if (!disc->tracks[2].isDataTrack()) {
WARN_LOG(GDROM, "CUE: track 3 must be a data track");
throw FlycastException(i18n::Ts("Invalid CUE file"));
}
if (disc->tracks[2].StartFAD != 45150) {
WARN_LOG(GDROM, "CUE: track 3 must start at FAD 45000+150, not %d", disc->tracks[2].StartFAD);
throw FlycastException(i18n::Ts("Invalid CUE file"));
}
disc->FillGDSession();
}
disc->FillGDSession();
}
else
{
// CD-Rom XA
disc->type = CdRom_XA;
disc->LeadOut.ADR = 1;
disc->LeadOut.CTRL = 4;
@@ -246,5 +266,5 @@ Disc* cue_parse(const char* file, std::vector<u8> *digest)
if (digest != nullptr)
*digest = md5.getDigest();
return disc;
return disc.release();
}
+73 -37
View File
@@ -1,26 +1,27 @@
#include "common.h"
#include "stdclass.h"
#include "oslib/storage.h"
#include "oslib/i18n.h"
#include <sstream>
Disc* load_gdi(const char* file, std::vector<u8> *digest)
static Disc* load_gdi(const char* file, std::vector<u8> *digest)
{
FILE *t = hostfs::storage().openFile(file, "rb");
if (t == nullptr)
{
WARN_LOG(COMMON, "Cannot open file '%s' errno %d", file, errno);
throw FlycastException(std::string("Cannot open GDI file ") + file);
throw FlycastException(strprintf(i18n::T("Cannot open GDI file %s"), file));
}
hostfs::FileInfo fileInfo = hostfs::storage().getFileInfo(file);
size_t gdi_len = fileInfo.size;
char gdi_data[8193] = { 0 };
char gdi_data[16384] {};
if (gdi_len >= sizeof(gdi_data))
{
std::fclose(t);
throw FlycastException("GDI file too big");
throw FlycastException(i18n::Ts("GDI file too big"));
}
if (std::fread(gdi_data, 1, gdi_len, t) != gdi_len)
@@ -28,25 +29,28 @@ Disc* load_gdi(const char* file, std::vector<u8> *digest)
std::fclose(t);
std::istringstream gdi(gdi_data);
gdi.imbue(std::locale::classic());
u32 iso_tc = 0;
gdi >> iso_tc;
if (iso_tc == 0)
throw FlycastException("GDI: empty or invalid GDI file");
u32 trackCount = 0;
gdi >> trackCount;
if (trackCount < 3 || trackCount > 99) {
WARN_LOG(GDROM, "Invalid track count: %d", trackCount);
throw FlycastException(i18n::Ts("Invalid GDI file"));
}
INFO_LOG(GDROM, "GDI : %d tracks", iso_tc);
INFO_LOG(GDROM, "GDI: %d tracks", trackCount);
std::string basepath = hostfs::storage().getParentPath(file);
MD5Sum md5;
Disc* disc = new Disc();
u32 TRACK=0,FADS=0,CTRL=0,SSIZE=0;
s32 OFFSET=0;
for (u32 i=0;i<iso_tc;i++)
std::unique_ptr<Disc> disc = std::make_unique<Disc>();
for (u32 i = 0; i < trackCount; i++)
{
std::string track_filename;
u32 TRACK = 0, FADS = 0, CTRL = 0, SSIZE = 0;
s32 OFFSET = 0;
//TRACK FADS CTRL SSIZE file OFFSET
gdi >> TRACK;
gdi >> FADS;
@@ -54,7 +58,6 @@ Disc* load_gdi(const char* file, std::vector<u8> *digest)
gdi >> SSIZE;
char last;
do {
gdi >> last;
} while (isspace(last));
@@ -75,44 +78,77 @@ Disc* load_gdi(const char* file, std::vector<u8> *digest)
gdi >> track_filename;
track_filename = last + track_filename;
}
gdi >> OFFSET;
if (TRACK < 1 || TRACK > trackCount) {
WARN_LOG(GDROM, "Track number out of bounds: %d", TRACK);
throw FlycastException(i18n::Ts("Invalid GDI file"));
}
if (CTRL != 0 && CTRL != 4) {
WARN_LOG(GDROM, "Unsupported track type: %d", CTRL);
throw FlycastException(i18n::Ts("Invalid GDI file"));
}
switch (TRACK)
{
case 1:
if (CTRL != 4) {
WARN_LOG(GDROM, "Track 1 must be a data track");
throw FlycastException(i18n::Ts("Invalid GDI file"));
}
break;
case 2:
if (CTRL != 0) {
WARN_LOG(GDROM, "Track 2 must be an audio track");
throw FlycastException(i18n::Ts("Invalid GDI file"));
}
break;
case 3:
if (CTRL != 4) {
WARN_LOG(GDROM, "Track 3 must be a data track");
throw FlycastException(i18n::Ts("Invalid GDI file"));
}
if (FADS != 45000) {
WARN_LOG(GDROM, "Track 3 should start at 45000, not %d", FADS);
throw FlycastException(i18n::Ts("Invalid GDI file"));
}
break;
default:
break;
}
if (SSIZE != 2352 && SSIZE != 2048) {
WARN_LOG(GDROM, "Unsupported sector size: %d", SSIZE);
throw FlycastException(i18n::Ts("Invalid GDI file"));
}
DEBUG_LOG(GDROM, "file[%d] \"%s\": FAD:%d, CTRL:%d, SSIZE:%d, OFFSET:%d", TRACK, track_filename.c_str(), FADS, CTRL, SSIZE, OFFSET);
Track t;
t.StartFAD = FADS + 150;
t.CTRL = CTRL;
if (SSIZE != 0)
{
std::string path = hostfs::storage().getSubPath(basepath, track_filename);
FILE *file = hostfs::storage().openFile(path, "rb");
if (file == nullptr)
{
delete disc;
throw FlycastException("GDI file: Cannot open track " + path);
}
if (digest != nullptr)
md5.add(file);
t.file = new RawTrackFile(file, OFFSET, t.StartFAD, SSIZE);
hostfs::FileInfo fileInfo = hostfs::storage().getFileInfo(path);
if ((fileInfo.size - OFFSET) % SSIZE != 0)
WARN_LOG(GDROM, "Warning: Size of track %s is not multiple of sector size %d", track_filename.c_str(), SSIZE);
t.EndFAD = t.StartFAD + (u32)(fileInfo.size - OFFSET) / SSIZE - 1;
}
std::string path = hostfs::storage().getSubPath(basepath, track_filename);
FILE *file = hostfs::storage().openFile(path, "rb");
if (file == nullptr)
throw FlycastException(strprintf(i18n::T("GDI file: Cannot open track %s"), path.c_str()));
if (digest != nullptr)
md5.add(file);
t.file = new RawTrackFile(file, OFFSET, t.StartFAD, SSIZE);
hostfs::FileInfo fileInfo = hostfs::storage().getFileInfo(path);
if ((fileInfo.size - OFFSET) % SSIZE != 0)
WARN_LOG(GDROM, "Warning: Size of track %s is not multiple of sector size %d", track_filename.c_str(), SSIZE);
t.EndFAD = t.StartFAD + (u32)(fileInfo.size - OFFSET) / SSIZE - 1;
disc->tracks.push_back(t);
}
if (disc->tracks.size() < 3) {
delete disc;
throw FlycastException("GDI parse error: less than 3 tracks");
if (disc->tracks.size() != trackCount) {
WARN_LOG(GDROM, "Track count %d but found %d tracks", trackCount, (int)disc->tracks.size());
throw FlycastException(i18n::Ts("Invalid GDI file"));
}
disc->FillGDSession();
if (digest != nullptr)
*digest = md5.getDigest();
return disc;
return disc.release();
}
+2
View File
@@ -28,6 +28,8 @@ public:
virtual bool isDirectory() const = 0;
virtual ~Entry() = default;
const std::string& getName() const { return name; }
u32 getStartFAD() const { return startFad; }
u32 getLength() const { return len; }
protected:
Entry(IsoFs *fs) : fs(fs) {}
+5
View File
@@ -1,3 +1,8 @@
target_sources(${PROJECT_NAME} PRIVATE
dreampotato.cpp
dreampotato.h
maplelink.cpp
maplelink.h)
if(NOT LIBRETRO)
target_sources(${PROJECT_NAME} PRIVATE
gamepad.h
+208
View File
@@ -0,0 +1,208 @@
/*
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#include "types.h"
#include <asio.hpp> // Must be included first to avoid winsock issues on Windows
#include "dreampotato.h"
#include "maplelink.h"
#include "emulator.h"
#include "cfg/option.h"
#include <memory>
#include <array>
#include <iomanip>
asio::error_code sendMsg(const MapleMsg& msg, asio::ip::tcp::iostream& stream)
{
static char buffer[1024 * 3 + 2];
char *p = buffer;
p += sprintf(p, "%02X %02X %02X %02X", msg.command, msg.destAP, msg.originAP, msg.size);
const u32 sz = msg.getDataSize();
for (u32 i = 0; i < sz; i++)
p += sprintf(p, " %02X", msg.data[i]);
strcpy(p, "\r\n");
p += 2;
asio::ip::tcp::socket& sock = static_cast<asio::ip::tcp::socket&>(stream.socket());
asio::error_code ec;
asio::write(sock, asio::buffer(buffer, p - buffer), ec);
return ec;
}
bool receiveMsg(MapleMsg& msg, std::istream& stream)
{
std::string response;
if (!std::getline(stream, response))
return false;
sscanf(response.c_str(), "%hhx %hhx %hhx %hhx", &msg.command, &msg.destAP, &msg.originAP, &msg.size);
if ((msg.getDataSize() - 1) * 3 + 13 >= response.length())
return false;
for (unsigned i = 0; i < msg.getDataSize(); i++)
sscanf(&response[i * 3 + 12], "%hhx", &msg.data[i]);
return !stream.fail();
}
namespace dreampotato
{
class DreamPotato : public BaseMapleLink
{
int bus;
asio::ip::tcp::iostream iostream;
bool connected = false;
static constexpr u16 BASE_PORT = 37393;
public:
DreamPotato(int bus)
: BaseMapleLink(true), bus(bus)
{}
bool send(const MapleMsg& msg) override;
bool sendReceive(const MapleMsg& txMsg, MapleMsg& rxMsg) override;
bool isConnected() override {
return connected;
}
void connect();
void disconnect();
void init();
void term();
};
static std::array<std::shared_ptr<DreamPotato>, 4> Potatoes;
void DreamPotato::init()
{
connect();
registerLink(bus, 0);
}
void DreamPotato::term()
{
disconnect();
unregisterLink(bus, 0);
}
void DreamPotato::connect()
{
if (connected)
return;
iostream = asio::ip::tcp::iostream("::1", std::to_string(BASE_PORT + bus), asio::ip::resolver_base::flags::numeric_host);
if (!iostream)
iostream = asio::ip::tcp::iostream("127.0.0.1", std::to_string(BASE_PORT + bus), asio::ip::resolver_base::flags::numeric_host);
if (!iostream) {
WARN_LOG(INPUT, "DreamPotato[%d] connection failed: %s", bus, iostream.error().message().c_str());
disconnect();
return;
}
connected = true;
iostream.expires_from_now(std::chrono::milliseconds(500));
MapleMsg txMsg;
txMsg.command = MDC_DeviceRequest;
txMsg.destAP = (bus << 6) | 1;
txMsg.originAP = bus << 6;
MapleMsg rxMsg;
// Note: connected *must* be true or infinite recursion loop
if (!sendReceive(txMsg, rxMsg))
// error has been logged and disconnect() called
return;
const u32 fnCode = rxMsg.readData<u32>();
if ((fnCode & MFID_1_Storage) == 0)
{
WARN_LOG(INPUT, "Unrecognized response from DreamPotato");
disconnect();
return;
}
NOTICE_LOG(INPUT, "Connected to DreamPotato[%d]", bus);
}
void DreamPotato::disconnect()
{
if (!connected)
return;
if (storageEnabled())
disableStorage();
connected = false;
iostream.close();
NOTICE_LOG(INPUT, "Disconnected from DreamPotato[%d]", bus);
}
bool DreamPotato::send(const MapleMsg& msg)
{
connect();
if (!connected)
return false;
iostream.expires_from_now(std::chrono::milliseconds(100));
asio::error_code ec = sendMsg(msg, iostream);
if (ec)
{
WARN_LOG(INPUT, "DreamPotato[%d] send failed: %s", bus, ec.message().c_str());
disconnect();
return false;
}
return true;
}
bool DreamPotato::sendReceive(const MapleMsg& txMsg, MapleMsg& rxMsg)
{
if (!send(txMsg))
return false;
if (!receiveMsg(rxMsg, iostream)) {
WARN_LOG(INPUT, "DreamPotato[%d] receive failed", bus);
disconnect();
return false;
}
return true;
}
// Instantiate DreamPotato devices where needed and delete the others
void update()
{
for (unsigned bus = 0; bus < Potatoes.size(); bus++)
{
auto& potato = Potatoes[bus];
if (config::NetworkExpansionDevices[bus][0] == 1
&& config::MapleExpansionDevices[bus][0] == MDT_SegaVMU
&& maple_getPortCount(config::MapleMainDevices[bus]) >= 1)
{
if (potato == nullptr) {
potato = std::make_shared<DreamPotato>(bus);
potato->init();
}
}
else if (potato != nullptr) {
potato->term();
potato = nullptr;
}
}
}
void term()
{
for (unsigned bus = 0; bus < Potatoes.size(); bus++)
{
auto& potato = Potatoes[bus];
if (potato != nullptr) {
potato->term();
potato = nullptr;
}
}
}
} // namespace dreampotato
+23
View File
@@ -0,0 +1,23 @@
/*
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
namespace dreampotato
{
void update();
void term();
}
+4 -4
View File
@@ -675,9 +675,9 @@ void GamepadDevice::detectInput(bool combo, input_detected_cb input_changed)
#ifdef TEST_AUTOMATION
static FILE *get_record_input(bool write)
{
if (write && !cfgLoadBool("record", "record_input", false))
if (write && !config::loadBool("record", "record_input", false))
return NULL;
if (!write && !cfgLoadBool("record", "replay_input", false))
if (!write && !config::loadBool("record", "replay_input", false))
return NULL;
std::string game_dir = settings.content.path;
size_t slash = game_dir.find_last_of("/");
@@ -689,7 +689,7 @@ static FILE *get_record_input(bool write)
void GamepadDevice::Register(const std::shared_ptr<GamepadDevice>& gamepad)
{
int maple_port = cfgLoadInt("input",
int maple_port = config::loadInt("input",
MAPLE_PORT_CFG_PREFIX + gamepad->unique_id(), 12345);
if (maple_port != 12345)
gamepad->set_maple_port(maple_port);
@@ -725,7 +725,7 @@ void GamepadDevice::SaveMaplePorts()
{
std::shared_ptr<GamepadDevice> gamepad = GamepadDevice::GetGamepad(i);
if (gamepad != NULL && !gamepad->unique_id().empty())
cfgSaveInt("input", MAPLE_PORT_CFG_PREFIX + gamepad->unique_id(), gamepad->maple_port());
config::saveInt("input", MAPLE_PORT_CFG_PREFIX + gamepad->unique_id(), gamepad->maple_port());
}
}
+2 -1
View File
@@ -160,6 +160,8 @@ protected:
return _input_detected != nullptr;
}
virtual void registered() {}
std::string _name;
std::string _unique_id;
std::shared_ptr<InputMapping> input_mapper;
@@ -168,7 +170,6 @@ protected:
int rumblePower = 100;
private:
virtual void registered() {}
bool handleButtonInput(int port, DreamcastKey key, bool pressed);
bool handleButtonInputDef(const InputMapping::InputDef& inputDef, bool pressed);
std::string make_mapping_filename(bool instance, int system, bool perGame = false);
+2 -1
View File
@@ -22,6 +22,7 @@
#include "gamepad_device.h"
#include "ui/gui.h"
#include "hw/maple/maple_devs.h"
#include "oslib/i18n.h"
#include <memory>
extern u8 kb_key[4][6]; // normal keys pressed
@@ -73,7 +74,7 @@ class KeyboardDevice : public GamepadDevice
protected:
KeyboardDevice(int maple_port, const char* apiName, bool remappable = true)
: GamepadDevice(maple_port, apiName, remappable) {
_name = "Keyboard";
_name = i18n::Ts("Keyboard");
}
std::shared_ptr<InputMapping> getDefaultMapping() override {
+127
View File
@@ -0,0 +1,127 @@
/*
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#include "maplelink.h"
#include "cfg/option.h"
#include "hw/maple/maple_if.h"
std::array<std::array<MapleLink::Ptr, 2>, 4> MapleLink::Links;
std::mutex MapleLink::Mutex;
namespace
{
struct GameState
{
GameState()
{
EventManager::listen(Event::Start, [](Event, void*) {
started = true;
});
EventManager::listen(Event::Terminate, [](Event, void*) {
started = false;
});
}
static bool started;
static GameState instance;
};
bool GameState::started;
GameState GameState::instance;
}
bool MapleLink::isGameStarted() const {
return GameState::started;
}
void MapleLink::registerLink(int bus, int port)
{
if (bus >= 0 && bus < (int)Links.size()
&& port >= 0 && port < (int)Links[0].size())
{
std::lock_guard<std::mutex> _(Mutex);
Links[bus][port] = shared_from_this();
}
}
void MapleLink::unregisterLink(int bus, int port)
{
if (bus >= 0 && bus < (int)Links.size()
&& port >= 0 && port < (int)Links[0].size())
{
std::lock_guard<std::mutex> _(Mutex);
Ptr& existing = Links[bus][port];
if (existing == shared_from_this())
existing = nullptr;
}
}
bool MapleLink::StorageEnabled()
{
std::lock_guard<std::mutex> _(Mutex);
for (const auto& ports : Links)
{
for (auto link : ports)
if (link != nullptr && link->storageEnabled())
return true;
}
return false;
}
BaseMapleLink::BaseMapleLink(bool storageSupported)
: storageSupported(storageSupported)
{
EventManager::listen(Event::LoadState, eventLoadState, this);
EventManager::listen(Event::Start, eventStart, this);
if (isGameStarted())
vmuStorage = storageSupported && config::UsePhysicalVmuMemory;
}
BaseMapleLink::~BaseMapleLink()
{
EventManager::unlisten(Event::LoadState, eventLoadState, this);
EventManager::unlisten(Event::Start, eventStart, this);
}
void BaseMapleLink::eventStart(Event event, void *p)
{
BaseMapleLink *self = (BaseMapleLink *)p;
self->vmuStorage = self->storageSupported && config::UsePhysicalVmuMemory && self->isConnected();
}
void BaseMapleLink::eventLoadState(Event event, void *p)
{
BaseMapleLink *self = (BaseMapleLink *)p;
if (self->vmuStorage) {
WARN_LOG(INPUT, "State loaded but VMU has storage enabled");
self->disableStorage();
}
}
void BaseMapleLink::disableStorage()
{
if (!vmuStorage)
return;
vmuStorage = false;
if (isGameStarted())
emu.run(maple_ReconnectDevices);
}
bool BaseMapleLink::storageEnabled()
{
if (!isConnected())
return false;
if (!isGameStarted())
return storageSupported && config::UsePhysicalVmuMemory;
else
return vmuStorage;
}
+76
View File
@@ -0,0 +1,76 @@
/*
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "types.h"
#include "hw/maple/maple_devs.h"
#include "emulator.h"
#include <array>
#include <memory>
#include <mutex>
// parent interface of DreamLink and DreamPotato for use by hw/maple
class MapleLink : public std::enable_shared_from_this<MapleLink>
{
public:
using Ptr = std::shared_ptr<MapleLink>;
virtual ~MapleLink() = default;
//! Sends a message to the controller, not expecting a response
virtual bool send(const MapleMsg& msg) = 0;
//! Sends a message to the controller and waits for a response
virtual bool sendReceive(const MapleMsg& txMsg, MapleMsg& rxMsg) = 0;
//! True if VMU reads and writes should be sent to the device
virtual bool storageEnabled() = 0;
//! True if the link is operational
virtual bool isConnected() = 0;
//! Returns the maple link at the given location if any
static Ptr GetMapleLink(int bus, int port) {
std::lock_guard<std::mutex> _(Mutex);
return Links[bus][port];
}
//! True if any maple link currently has storage enabled
static bool StorageEnabled();
protected:
//! True if a game has been started
bool isGameStarted() const;
void registerLink(int bus, int port);
void unregisterLink(int bus, int port);
private:
static std::mutex Mutex;
static std::array<std::array<Ptr, 2>, 4> Links;
};
class BaseMapleLink : public MapleLink
{
public:
~BaseMapleLink();
bool storageEnabled() override;
protected:
BaseMapleLink(bool storageSupported);
void disableStorage(); // Disable VMU storage for this link
static void eventStart(Event event, void *p);
static void eventLoadState(Event event, void *p);
const bool storageSupported;
bool vmuStorage = false;
};
+16 -16
View File
@@ -422,7 +422,7 @@ void InputMapping::deleteTrigger(u32 code)
}
}
using namespace emucfg;
using namespace config;
static DreamcastKey getKeyId(const std::string& name)
{
@@ -439,20 +439,20 @@ static DreamcastKey getKeyId(const std::string& name)
void InputMapping::load(FILE* fp)
{
ConfigFile mf;
mf.parse(fp);
IniFile mf;
mf.load(fp);
this->name = mf.get("emulator", "mapping_name", "<Unknown>");
int dz = mf.get_int("emulator", "dead_zone", 10);
int dz = mf.getInt("emulator", "dead_zone", 10);
dz = std::min(dz, 100);
dz = std::max(dz, 0);
this->dead_zone = (float)dz / 100.f;
int sat = std::clamp(mf.get_int("emulator", "saturation", 100), 50, 200);
int sat = std::clamp(mf.getInt("emulator", "saturation", 100), 50, 200);
this->saturation = (float)sat / 100.f;
this->rumblePower = mf.get_int("emulator", "rumble_power", this->rumblePower);
this->rumblePower = mf.getInt("emulator", "rumble_power", this->rumblePower);
version = mf.get_int("emulator", "version", 1);
version = mf.getInt("emulator", "version", 1);
if (version < 3)
{
loadv1(mf);
@@ -582,7 +582,7 @@ void InputMapping::load(FILE* fp)
dirty = false;
}
void InputMapping::loadv1(ConfigFile& mf)
void InputMapping::loadv1(IniFile& mf)
{
for (int port = 0; port < 4; port++)
{
@@ -593,7 +593,7 @@ void InputMapping::loadv1(ConfigFile& mf)
option = button_list[i].option;
else
option = button_list[i].option + std::to_string(port);
int button_code = mf.get_int(button_list[i].section, option, -1);
int button_code = mf.getInt(button_list[i].section, option, -1);
if (button_code >= 0)
{
DreamcastKey id = button_list[i].id;
@@ -613,14 +613,14 @@ void InputMapping::loadv1(ConfigFile& mf)
option = axis_list[i].option;
else
option = axis_list[i].option + std::to_string(port);
int axis_code = mf.get_int(axis_list[i].section, option, -1);
int axis_code = mf.getInt(axis_list[i].section, option, -1);
if (axis_code >= 0)
{
if (port == 0)
option = axis_list[i].option_inverted;
else
option = axis_list[i].option_inverted + std::to_string(port);
bool inverted = mf.get_bool(axis_list[i].section_inverted, option, false);
bool inverted = mf.getBool(axis_list[i].section_inverted, option, false);
this->set_axis(port, axis_list[i].id, axis_code, !inverted);
@@ -933,13 +933,13 @@ bool InputMapping::save(const std::string& name)
WARN_LOG(INPUT, "Cannot save controller mappings into %s", path.c_str());
return false;
}
ConfigFile mf;
IniFile mf;
mf.set("emulator", "mapping_name", this->name);
mf.set_int("emulator", "dead_zone", (int)std::round(this->dead_zone * 100.f));
mf.set_int("emulator", "saturation", (int)std::round(this->saturation * 100.f));
mf.set_int("emulator", "rumble_power", this->rumblePower);
mf.set_int("emulator", "version", CURRENT_FILE_VERSION);
mf.set("emulator", "dead_zone", (int)std::round(this->dead_zone * 100.f));
mf.set("emulator", "saturation", (int)std::round(this->saturation * 100.f));
mf.set("emulator", "rumble_power", this->rumblePower);
mf.set("emulator", "version", CURRENT_FILE_VERSION);
int bindIndex = 0;
for (int port = 0; port < 4; port++)
+3 -3
View File
@@ -27,8 +27,8 @@
#include <vector>
#include <limits>
namespace emucfg {
struct ConfigFile;
namespace config {
class IniFile;
}
class InputMapping
@@ -265,7 +265,7 @@ protected:
bool dirty = false;
private:
void loadv1(emucfg::ConfigFile& mf);
void loadv1(config::IniFile& mf);
static std::vector<std::string> strSplit(const std::string str, char c, size_t maxsplit = 0);
std::map<std::pair<u32, bool>, DreamcastKey> axes[NUM_PORTS];
+2 -2
View File
@@ -75,7 +75,7 @@ void Mouse::setButton(Button button, bool pressed)
void SystemMouse::setAbsPos(int x, int y, int width, int height) {
gui_set_mouse_position(x, y);
gui_set_mouse_position(x, y, touchMouse);
Mouse::setAbsPos(x, y, width, height);
}
@@ -83,7 +83,7 @@ void SystemMouse::setButton(Button button, bool pressed) {
int uiBtn = (int)button - 1;
if (uiBtn < 2)
uiBtn ^= 1;
gui_set_mouse_button(uiBtn, pressed);
gui_set_mouse_button(uiBtn, pressed, touchMouse);
Mouse::setButton(button, pressed);
}
+14 -9
View File
@@ -18,6 +18,7 @@
*/
#pragma once
#include "gamepad_device.h"
#include "oslib/i18n.h"
#include <mutex>
// Mouse position and buttons
@@ -53,7 +54,7 @@ class Mouse : public GamepadDevice
{
protected:
Mouse(const char *apiName, int maplePort = 0) : GamepadDevice(maplePort, apiName) {
this->_name = "Mouse";
this->_name = i18n::Ts("Mouse");
}
std::shared_ptr<InputMapping> getDefaultMapping() override {
@@ -74,15 +75,15 @@ public:
switch((Button)code)
{
case LEFT_BUTTON:
return "Left Button";
return i18n::T("Left Button");
case RIGHT_BUTTON:
return "Right Button";
return i18n::T("Right Button");
case MIDDLE_BUTTON:
return "Middle Button";
return i18n::T("Middle Button");
case BUTTON_4:
return "Button 4";
return i18n::T("Button 4");
case BUTTON_5:
return "Button 5";
return i18n::T("Button 5");
default:
return nullptr;
}
@@ -96,8 +97,12 @@ public:
class SystemMouse : public Mouse
{
bool touchMouse;
protected:
SystemMouse(const char *apiName, int maplePort = 0) : Mouse(apiName, maplePort) {}
SystemMouse(const char *apiName, int maplePort = 0, bool touchMouse = false)
: Mouse(apiName, maplePort), touchMouse(touchMouse)
{}
public:
void setAbsPos(int x, int y, int width, int height);
@@ -108,9 +113,9 @@ public:
class TouchMouse : public SystemMouse
{
public:
TouchMouse() : SystemMouse("Flycast", -1)
TouchMouse() : SystemMouse("Flycast", -1, true)
{
_name = "Touch Mouse";
_name = i18n::Ts("Touch Mouse");
_unique_id = "touch_mouse";
loadMapping();
}
+2 -1
View File
@@ -19,6 +19,7 @@
#pragma once
#include "gamepad_device.h"
#include "ui/vgamepad.h"
#include "oslib/i18n.h"
class VirtualGamepad : public GamepadDevice
{
@@ -26,7 +27,7 @@ public:
VirtualGamepad(const char *api_name, int maple_port = 0)
: GamepadDevice(maple_port, api_name, false)
{
_name = "Virtual Gamepad";
_name = i18n::Ts("Virtual Gamepad");
_unique_id = "virtual_gamepad_uid";
input_mapper = std::make_shared<IdentityInputMapping>();
// hasAnalogStick = true; // TODO has an analog stick but input mapping isn't persisted
+11 -2
View File
@@ -3,18 +3,20 @@
#endif
#include "types.h"
#if defined(__unix__)
#if defined(__unix__) || defined(__HAIKU__)
#include "log/LogManager.h"
#include "emulator.h"
#include "ui/mainui.h"
#include "oslib/directory.h"
#include "oslib/oslib.h"
#include "oslib/i18n.h"
#include "stdclass.h"
#include <csignal>
#include <string>
#include <unistd.h>
#include <vector>
#include <exception>
#if defined(SUPPORT_X11)
#include "x11.h"
@@ -238,6 +240,7 @@ int main(int argc, char* argv[])
#endif
LogManager::Init();
i18n::init();
// Set directories
set_user_config_dir(find_user_config_dir());
@@ -266,7 +269,13 @@ int main(int argc, char* argv[])
auto async = std::async(std::launch::async, uploadCrashes, "/tmp");
#endif
mainui_loop();
try {
mainui_loop();
} catch (const std::exception& e) {
ERROR_LOG(BOOT, "mainui_loop error: %s", e.what());
} catch (...) {
ERROR_LOG(BOOT, "mainui_loop unknown exception");
}
flycast_term();
os_UninstallFaultHandler();
+10 -10
View File
@@ -262,7 +262,7 @@ void input_x11_init()
void x11_window_create()
{
if (cfgLoadInt("pvr", "nox11", 0) == 0)
if (config::loadInt("pvr", "nox11") == 0)
{
XInitThreads();
@@ -300,15 +300,15 @@ void x11_window_create()
sWA.event_mask |= PointerMotionMask | FocusChangeMask;
unsigned long ui32Mask = CWBackPixel | CWBorderPixel | CWEventMask | CWColormap;
x11_width = cfgLoadInt("window", "width", 0);
x11_width = config::loadInt("window", "width");
if (x11_width == 0)
x11_width = cfgLoadInt("x11", "width", DEFAULT_WINDOW_WIDTH);
x11_height = cfgLoadInt("window", "height", 0);
x11_fullscreen = cfgLoadBool("window", "fullscreen", DEFAULT_FULLSCREEN);
x11_width = config::loadInt("x11", "width", DEFAULT_WINDOW_WIDTH);
x11_height = config::loadInt("window", "height");
x11_fullscreen = config::loadBool("window", "fullscreen", DEFAULT_FULLSCREEN);
if (x11_height == 0)
{
x11_height = cfgLoadInt("x11", "height", DEFAULT_WINDOW_HEIGHT);
x11_fullscreen = cfgLoadBool("x11", "fullscreen", DEFAULT_FULLSCREEN);
x11_height = config::loadInt("x11", "height", DEFAULT_WINDOW_HEIGHT);
x11_fullscreen = config::loadBool("x11", "fullscreen", DEFAULT_FULLSCREEN);
}
if (x11_width < 0 || x11_height < 0)
@@ -380,10 +380,10 @@ void x11_window_destroy()
{
if (!x11_fullscreen)
{
cfgSaveInt("window", "width", x11_width);
cfgSaveInt("window", "height", x11_height);
config::saveInt("window", "width", x11_width);
config::saveInt("window", "height", x11_height);
}
cfgSaveBool("window", "fullscreen", x11_fullscreen);
config::saveBool("window", "fullscreen", x11_fullscreen);
XDestroyWindow(x11_disp, x11_win);
x11_win = (Window)0;
}
+2 -2
View File
@@ -1,6 +1,6 @@
#include "types.h"
#if defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__)
#if defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__) || defined(__HAIKU__)
#if defined(__APPLE__)
#define _XOPEN_SOURCE 1
#define __USE_GNU 1
@@ -111,7 +111,7 @@ void os_UninstallFaultHandler()
#endif
}
#if !defined(__unix__) && !defined(LIBRETRO) && !defined(__SWITCH__)
#if !defined(__unix__) && !defined(LIBRETRO) && !defined(__SWITCH__) && !defined(__HAIKU__)
[[noreturn]] void os_DebugBreak()
{
__builtin_trap();
+6 -1
View File
@@ -8,7 +8,7 @@
#define __USE_GNU 1
#endif
#if !defined(__OpenBSD__)
#if !defined(__OpenBSD__) && !defined(__HAIKU__)
#include <ucontext.h>
#endif
@@ -112,6 +112,11 @@ static void context_segfault(host_context_t* hostctx, void* segfault_ctx)
bicopy<ToSegfault>(hostctx->rsp, MCTX(.__gregs[_REG_RSP]));
bicopy<ToSegfault>(hostctx->r9, MCTX(.__gregs[_REG_R9]));
bicopy<ToSegfault>(hostctx->rdi, MCTX(.__gregs[_REG_RDI]));
#elif defined(__HAIKU__)
bicopy<ToSegfault>(hostctx->pc, MCTX(.rip));
bicopy<ToSegfault>(hostctx->rsp, MCTX(.rsp));
bicopy<ToSegfault>(hostctx->r9, MCTX(.r9));
bicopy<ToSegfault>(hostctx->rdi, MCTX(.rdi));
#elif defined(__unix__)
bicopy<ToSegfault>(hostctx->pc, MCTX(.gregs[REG_RIP]));
bicopy<ToSegfault>(hostctx->rsp, MCTX(.gregs[REG_RSP]));
+5 -5
View File
@@ -120,7 +120,7 @@ LogManager::LogManager()
m_log[LogTypes::SH4] = {"SH4", "SH4 Modules"};
// Set up log listeners
int verbosity = cfgLoadInt("log", "Verbosity", LogTypes::LDEBUG);
int verbosity = config::loadInt("log", "Verbosity", LogTypes::LDEBUG);
// Ensure the verbosity level is valid
if (verbosity < 1)
@@ -130,12 +130,12 @@ LogManager::LogManager()
SetLogLevel(static_cast<LogTypes::LOG_LEVELS>(verbosity));
RegisterListener(LogListener::CONSOLE_LISTENER, new ConsoleListener());
EnableListener(LogListener::CONSOLE_LISTENER, cfgLoadBool("log", "LogToConsole", true));
EnableListener(LogListener::CONSOLE_LISTENER, config::loadBool("log", "LogToConsole", true));
RegisterListener(LogListener::IN_MEMORY_LISTENER, new InMemoryListener());
EnableListener(LogListener::IN_MEMORY_LISTENER, true);
for (LogContainer& container : m_log)
container.m_enable = cfgLoadBool("log", container.m_short_name, true);
container.m_enable = config::loadBool("log", container.m_short_name, true);
m_path_cutoff_point = DeterminePathCutOffPoint();
@@ -144,7 +144,7 @@ LogManager::LogManager()
void LogManager::UpdateConfig()
{
bool logToFile = cfgLoadBool("log", "LogToFile", false);
bool logToFile = config::loadBool("log", "LogToFile", false);
if (logToFile != IsListenerEnabled(LogListener::FILE_LISTENER))
{
if (!logToFile) {
@@ -170,7 +170,7 @@ void LogManager::UpdateConfig()
}
EnableListener(LogListener::FILE_LISTENER, logToFile);
}
std::string newLogServer = cfgLoadStr("log", "LogServer", "");
std::string newLogServer = config::loadStr("log", "LogServer");
if (logServer != newLogServer)
{
logServer = newLogServer;
Loaded 100 of 259 files, more files were not shown because too many files have changed in this diff. Show more