mirror of
https://github.com/flyinghead/flycast.git
synced 2026-11-08 01:48:31 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5eadfb388e | ||
|
|
83f6d818b5 | ||
|
|
bbae3de9a4 | ||
|
|
c2f66018d8 | ||
|
|
82ca2db61a | ||
|
|
b4d83c0d12 | ||
|
|
1aba1b467e | ||
|
|
5be5eeb905 | ||
|
|
2f842238a5 | ||
|
|
3de74672ce | ||
|
|
b76b1c7275 | ||
|
|
7ce50e5229 | ||
|
|
99fc194dd0 | ||
|
|
bd8f7c8a75 | ||
|
|
fd72eebbe3 | ||
|
|
fea8699884 | ||
|
|
d00b5d4c17 | ||
|
|
1652143f5f | ||
|
|
75bca2a166 | ||
|
|
13df86c520 | ||
|
|
8a228e4e16 | ||
|
|
0a3c68f7cc | ||
|
|
4905dc21c8 | ||
|
|
98dfb4c32a | ||
|
|
df2bdfc9bf | ||
|
|
e86c1c992f | ||
|
|
5b6cc33a75 | ||
|
|
e37518524f | ||
|
|
994f72f954 | ||
|
|
5978b3a058 | ||
|
|
bb12d5c72d | ||
|
|
b127c96cf3 | ||
|
|
0d3beb1c6d | ||
|
|
9bd3179745 | ||
|
|
5833fb3a47 | ||
|
|
49ec396e24 | ||
|
|
5607b8eebd | ||
|
|
35888406d3 | ||
|
|
6b3bbd6f85 | ||
|
|
ef689fd35d | ||
|
|
6a78f27bef | ||
|
|
97de155fd8 | ||
|
|
f183f30262 | ||
|
|
065c91f9ad | ||
|
|
161d057d4c | ||
|
|
a7ee2e9a66 | ||
|
|
e08ec86962 | ||
|
|
7653b1c811 | ||
|
|
1475812a94 | ||
|
|
c8a724ad63 | ||
|
|
2cfce747c5 | ||
|
|
f8bdf6c35e | ||
|
|
f4c2d001bd | ||
|
|
f3ddec5f1e | ||
|
|
13c7ddc6c9 | ||
|
|
c24d7fb0fe | ||
|
|
3ebc79a5ce | ||
|
|
6989a0aa2c | ||
|
|
bd24812d0c | ||
|
|
15889616dd | ||
|
|
e60efed8b5 | ||
|
|
11fcadf37e | ||
|
|
88b8a0b51e | ||
|
|
8e9a0f9c1f | ||
|
|
dd50fcf81c | ||
|
|
7b8656b944 | ||
|
|
00ae6c42a2 | ||
|
|
8c4c463c38 | ||
|
|
329b6a1e34 | ||
|
|
9c28800384 | ||
|
|
602982fb56 | ||
|
|
cb830e0bd3 | ||
|
|
ec3d85a96c | ||
|
|
d3bc72b0d0 | ||
|
|
4170e783b9 | ||
|
|
083ccb984a | ||
|
|
0220207545 | ||
|
|
aee4cd34fc | ||
|
|
a99cce27bb | ||
|
|
b2941a8593 | ||
|
|
3ad072f9aa | ||
|
|
14374084d5 | ||
|
|
5c7a241897 | ||
|
|
7fcc5fa2f7 | ||
|
|
cf2fe027f7 | ||
|
|
b13685f0bb | ||
|
|
cf7b6bdcb0 | ||
|
|
894c61ed64 | ||
|
|
82cadfde22 | ||
|
|
1586cbaea9 | ||
|
|
2113e4e249 |
No files matched your search
@@ -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' }}
|
||||
@@ -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
|
||||
@@ -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' }}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }}
|
||||
@@ -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: |
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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})
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Submodule core/deps/DreamPicoPort-API updated: 0f6a902bc8...e06d9f6c59.
@@ -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
|
||||
|
||||
@@ -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
@@ -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
File diff suppressed because it is too large.
Load diff
+181
-124
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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))
|
||||
|
||||
+508
-327
File diff suppressed because it is too large.
Load diff
Regular → Executable
File mode changed.
Regular → Executable
+75
-36
@@ -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
File mode changed.
Submodule core/deps/libusb-cmake deleted from 000e06eb24.
Submodule
+1
Submodule core/deps/tinygettext added at 41572a67f9.
+41
-41
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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."));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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++)
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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]));
|
||||
|
||||
@@ -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
Reference in new issue
Block a user