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

Add EXCLUDE_FROM_ALL to add_subdirectory to prevent this.
2026-01-14 12:17:21 +01:00
Flyinghead 5be5eeb905 naomi: more rom crc32
[ci skip]
2026-01-14 12:17:02 +01:00
Flyinghead 2f842238a5 ini: don't save transient values and empty sections 2026-01-14 12:16:23 +01:00
flyinghead 3de74672ce Crowdin Translations: Japanese, Swedish 2026-01-13 11:41:42 +01:00
Flyinghead b76b1c7275 Merge remote-tracking branch 'origin/master' into dev
[ci skip]
2026-01-13 11:39:33 +01:00
Flyinghead 392a429e8b boxart: replace invalid utf-8 chars instead of failing when saving
Open (and truncate) the file only after json serialization.
2026-01-07 14:28:32 +01:00
Flyinghead 0491f2e6a5 reios: check boot file destination ptr before loading
Abort load if ptr is null (size > 0xff0000)
Fixes MINIDUMP-7W9 MINIDUMP-84B MINIDUMP-87V
2026-01-07 09:50:35 +01:00
Flyinghead 33d9a58cf9 emu: don't crash if unexpected state in Emulator::start()
Simply log and ignore. Happens on android under some undetermined
conditions.
Fixes MINIDUMP-2Q MINIDUMP-3SG
2026-01-07 08:52:36 +01:00
Flyinghead 401332d520 sdl: don't run haptic sine effect if rumblePower is 0 2026-01-06 16:23:47 +01:00
Flyinghead 8511d1ad3c dreamconn: only build on win32. remove unused code 2026-01-06 16:21:35 +01:00
Flyinghead 7ce50e5229 mmu: stricter test before enabling on-demand MMU
Visual Concepts games and many naomi and systemsp games create some
bogus mappings at boot. Ignore those

Issue #2184
2026-01-05 11:48:03 +01:00
Flyinghead 99fc194dd0 ini: transient values always hide regular ones regardless of order 2026-01-05 11:42:41 +01:00
Craig d794b53512 webOS: disable OPENMP as not available until webOS 3.9.2 (#2188)
[ci skip]
2026-01-04 21:10:03 +01:00
vkedwardli bd8f7c8a75 Update imgui to v1.92.5 (#2180)
Update ImGui from v1.92.1 to v1.92.5
2026-01-04 10:56:36 +01:00
Craig 594d82b223 add webOS to CMakeLists (#2167)
[ci skip]
2026-01-04 10:55:23 +01:00
Flyinghead fd72eebbe3 mmu: enable full MMU when non-SQ mapping is created
Fixes super mario 64 audio
Issue #2184
[ci skip]
2026-01-04 10:53:12 +01:00
Flyinghead fea8699884 modem: enforce correct tx speed for dissymmetric connections 2026-01-04 10:48:01 +01:00
Flyinghead d00b5d4c17 modem: 56K support, V8 bis protocol 2026-01-03 17:52:43 +01:00
Flyinghead 74cbee908f arm64 rec: proper fix for MINIDUMP-7W0
ModeImmediateHelper can generate code, resulting in the Mov op being
generated twice.
Fixes MINIDUMP-7W0
2026-01-02 11:51:23 +01:00
flyinghead 1652143f5f New translations flycast.pot (Hungarian)
New translations for {languageName}
2025-12-31 18:25:10 +01:00
Flyinghead 4886dcb1c2 enable rtt2vram for Jikkyo Powerful Pro Yakyu
Issue #2182
2025-12-29 20:20:15 +01:00
flyinghead 75bca2a166 Crowdin Translations: Hungarian and Japanese 2025-12-28 17:51:30 +01:00
Flyinghead 13df86c520 modem: V42bis compression support
[ci skip]
2025-12-28 17:51:01 +01:00
Rikki Gibson 16fc172ffc DreamConn: fix compat issue involving slot 2 VMU support (#2172)
* Do not create a shared_ptr from destructor
* Fall back when MDC_DeviceRequest message does not have an expected response
* Assume the device type for physical DreamConn controllers
* DreamConn controllers do not support physical VMU memory
* Do not maple_ReconnectDevices for DreamConn controller
2025-12-27 19:40:13 +01:00
Flyinghead dfb2ae8987 android: touch mouse may be null so check before delivering events
Fixes MINIDUMP-6S6
[ci skip]
2025-12-27 19:36:51 +01:00
Flyinghead 4529a31372 don't save state when game filename is empty
Happens on android apparently due to issues with automatic savestate and
app pausing.
2025-12-27 19:35:30 +01:00
Flyinghead 5a29ac489f sdl: fix DreamPicoPort crash
io_context_thread can be null in the destructor.
Fixes MINIDUMP-83F
2025-12-27 19:31:32 +01:00
github-actions[bot] c4c3c2ae93 Fetch translations & Recreate libretro_core_options_intl.h 2025-12-26 22:21:56 +00:00
flyinghead 8a228e4e16 Crowdin Translations: Swedish 2025-12-24 17:25:38 +01:00
Flyinghead 0a3c68f7cc i18n: delete bogus zh_CH.po. Load correct one
[ci skip]
2025-12-24 17:25:01 +01:00
flyinghead 4905dc21c8 Crowdin Translations: Japanese 2025-12-23 15:30:29 +01:00
Flyinghead 98dfb4c32a modem: implement V14 sync-async mode and V42 protocol
Use modem sync mode when ASYN is reset.
Implement V14 sync-async mode for V42 detection. Also used by Power
Smash and Pro Yakyu Team de Asobou Net!
Switch to V42 protocol for most games.
Set correct modem speed and timing as set by the game.
Get rid of modem speed/automode cheats.
[ci skip]
2025-12-23 15:29:42 +01:00
Flyinghead df2bdfc9bf ui: support for Asian fonts on linux
Fix width of "Filter" field in the game list.
crowdin.yml: fix typo for zh_CN resource files.
2025-12-23 12:00:53 +01:00
flyinghead e86c1c992f Crowdin Translations: Japanese
courtesy of @kihato
2025-12-22 21:23:57 +01:00
Flyinghead 5b6cc33a75 i18n: add Japanese translation
[ci skip]
2025-12-22 21:22:27 +01:00
flyinghead e37518524f Crowdin Translations: Hungarian and Portuguese, Brazilian 2025-12-21 15:04:05 +01:00
Flyinghead 994f72f954 ci: upload symbols to sentry on push only
[ci skip]
2025-12-21 15:03:09 +01:00
Flyinghead 5978b3a058 ui: merge Asian glyphs into large font too
Issue #2169
2025-12-21 15:00:28 +01:00
flyinghead bb12d5c72d Crowdin Translations (#2162) Hungarian
courtesy of oldman63
2025-12-18 15:56:18 +01:00
Flyinghead b127c96cf3 i18n: Add hu.po to resources
[ci skip]
2025-12-18 15:54:19 +01:00
scribam 0d3beb1c6d fix misleading indentation warning 2025-12-17 14:00:11 +01:00
Flyinghead 9bd3179745 i18n: set all stringstreams to classic locale
Fixes dumped texture names being formatted with the thousands separator
of the current locale.
[ci skip]
2025-12-17 13:59:25 +01:00
Flyinghead 5833fb3a47 ui: use imgui touch support and get rid of delay touch hack 2025-12-16 17:20:45 +01:00
Flyinghead 49ec396e24 i18n: delete empty message files. add zh_HK.po 2025-12-16 17:20:45 +01:00
Stefan Schlosser 5607b8eebd libretro: freebsd build fix 2025-12-16 17:18:52 +01:00
scribam 35888406d3 ci: libretro android build fixes (#2156)
Followup to #2151
[ci skip]
2025-12-16 17:18:32 +01:00
scribam 6b3bbd6f85 ci: update actions (#2155)
* ci: update actions
* ci: use freebsd 15 and openbsd 7.8
[ci skip]
2025-12-16 17:18:07 +01:00
James Smith ef689fd35d Minor bug fixes for DreamPicoPort 2025-12-15 18:05:32 +01:00
scribam 6a78f27bef ci: libretro android build fixes (#2151)
[ci skip]
2025-12-15 18:05:11 +01:00
scribam 97de155fd8 ci: enable libretro on bsd systems (#2150)
[ci skip]
2025-12-15 18:01:47 +01:00
scribam f183f30262 dragonflybsd: build fixes (#2149)
[ci skip]
2025-12-15 18:01:08 +01:00
flyinghead 065c91f9ad Crowdin Translations (#2148)
[ci skip]
2025-12-15 18:00:22 +01:00
Edward Li f65907a256 Remove custom_texture.terminate() during TexCache.Clear() 2025-12-15 12:27:57 +01:00
scribam 161d057d4c haiku: build fixes 2025-12-14 14:47:24 +01:00
scribam a7ee2e9a66 libretro: netbsd/openbsd build fixes 2025-12-14 14:46:52 +01:00
flyinghead e08ec86962 Crowdin Translations (#2143)
[ci skip]
2025-12-14 14:46:29 +01:00
Flyinghead 7653b1c811 sh4: don't crash when FIPR is executed with PR=1
[ci skip]
2025-12-14 14:45:53 +01:00
Flyinghead 1475812a94 i18n: localize input devices names, buttons and axes 2025-12-14 14:35:09 +01:00
flyinghead c8a724ad63 Crowdin Translations (#2142)
French, Swedish
2025-12-13 17:25:16 +01:00
Flyinghead 2cfce747c5 ui: better egl init test. display message box on graphics init error
Fail if eglGetDisplay() is null.
Throw exception on initRenderApi/switchRenderApi failure.
Catch in SDL and Android and show error message.
Localize Android java code.

Fixes MINIDUMP-7SD MINIDUMP-2XH MINIDUMP-5KM MINIDUMP-2FF MINIDUMP-4ZP
MINIDUMP-6T1 MINIDUMP-5D3 MINIDUMP-7ME
[ci skip]
2025-12-13 17:23:55 +01:00
github-actions[bot] 37a9077304 Fetch translations & Recreate libretro_core_options_intl.h 2025-12-12 22:21:51 +00:00
Flyinghead f8bdf6c35e crowdin.yml: fix pt_BR typo
[ci skip]
2025-12-12 21:51:46 +01:00
Flyinghead f4c2d001bd crowdin.yml: add android resource
[ci skip]
2025-12-12 20:04:07 +01:00
Flyinghead f3ddec5f1e android: add localizable strings resource
[ci skip]
2025-12-12 19:53:04 +01:00
scribam 13c7ddc6c9 deps: remove core/deps/libusb-cmake 2025-12-12 19:39:50 +01:00
nexus382 c24d7fb0fe Send game id to vmu pro/vm2 (#2126)
[ci skip]
2025-12-12 19:39:28 +01:00
Flyinghead 3ebc79a5ce Merge remote-tracking branch 'origin/master' into dev [ci skip] 2025-12-12 19:32:09 +01:00
Flyinghead 27a913fc02 arm recs: ensure block linking size doesn't depend on mmu mode
Due to d0ec876936 the mmu enabled path may
be taken when the emu is pausing, so make sure block linking uses the
same number of ops in both branches.
Fixes MINIDUMP-82C MINIDUMP-7T2 MINIDUMP-7W0
2025-12-12 16:49:50 +01:00
Flyinghead 97442c0411 terminate game id at first null character
Fixes MINIDUMP-7YW MINIDUMP-800
2025-12-12 16:13:44 +01:00
Flyinghead 5b12f9ca68 boxart: catch-all handler for scraper thread
Fixes MINIDUMP-7YW
2025-12-12 16:07:22 +01:00
Flyinghead 6a298289a9 ice: guard against null icePipe
Fixes MINIDUMP-7ZF
2025-12-12 16:03:44 +01:00
Flyinghead 59ef8579d2 ui: disable imgui debug tools
Fixes MINIDUMP-7RF
2025-12-12 16:01:52 +01:00
Flyinghead 6989a0aa2c test: fneg on odd register in PR=1 mode
Issue #2139
2025-12-11 16:26:37 +01:00
Flyinghead bd24812d0c jvs: return value from RawDma is incorrect for MIE firmware upload
It didn't seem to bother anyone however
2025-12-11 16:25:03 +01:00
Flyinghead 15889616dd add zh_CH and zh_TW messages. delete outdated docs
Issue #1830
2025-12-11 15:15:43 +01:00
Flyinghead e60efed8b5 pvr: limit framebuffer height to 240 when interlacing is disabled
Fixes Confidential Mission PAL Echelon intro
Issue #2062
2025-12-11 15:11:24 +01:00
Flyinghead 11fcadf37e sh4: fneg doesn't depend on FPSCR.PR at all
When PR=1, fneg can also be used on odd registers.
Issue #2139
2025-12-11 15:03:16 +01:00
Flyinghead 88b8a0b51e crowdin.yml: add zh_HK mapping [ci skip] 2025-12-10 17:15:24 +01:00
flyinghead 8e9a0f9c1f Crowdin Translations (#2136) pt_BR
New translations flycast.pot (Portuguese, Brazilian)
2025-12-10 11:42:58 +01:00
Flyinghead dd50fcf81c i18n: include pt_BR.po [ci skip] 2025-12-10 11:41:30 +01:00
Flyinghead 7b8656b944 imgread: more checks when loading GDI and CUE files (fail fast)
Add missing CMakeLists.txt change for new cue and gdi tests
2025-12-10 11:40:41 +01:00
Flyinghead 00ae6c42a2 fix some include paths 2025-12-09 19:41:48 +01:00
Flyinghead 8c4c463c38 add cue and gdi parsing tests. add non-C locale tests
delete old en resource file
libretro doesn't need cfg/ini.cpp/h anymore
2025-12-09 17:31:31 +01:00
Flyinghead 14f759eeb7 ice: fix crash when loading a state without being connected 2025-12-09 14:36:53 +01:00
flyinghead 329b6a1e34 Crowdin Translations (#2134)
New translations: sv, pt, pt_BR
2025-12-09 09:23:08 +01:00
Flyinghead 9c28800384 i18n: std::from/to_chars missing on BSD and macOS
Use stringstream instead

[ci skip]
2025-12-09 09:21:54 +01:00
Flyinghead 602982fb56 add pt_BR crowdin mapping [ci skip] 2025-12-08 23:38:12 +01:00
flyinghead cb830e0bd3 Update Crowdin configuration file 2025-12-08 23:35:08 +01:00
Flyinghead ec3d85a96c remove [ci skip] in crowdin commit messages 2025-12-08 23:26:58 +01:00
Flyinghead d3bc72b0d0 ci: ignore push on l10n_* branches 2025-12-08 22:28:30 +01:00
Flyinghead 4170e783b9 fix GDI and CUE parsing error in non-English locale
IniFile: use locale-independant float conversions functions
Use std::ostringstream and std::istringstream as appropriate.
Use C locale for GDI and CUE parsing.
2025-12-08 22:26:33 +01:00
flyinghead 083ccb984a Crowdin Translations (#2133)
* New translations flycast.pot (French)
New translations for {languageName} [ci skip]
* New translations flycast.pot (Swedish)
New translations for {languageName} [ci skip]
2025-12-08 21:19:31 +01:00
Flyinghead 0220207545 strprintf utility. localize exception messages. add sv.po (Swedish) 2025-12-08 16:22:26 +01:00
flyinghead aee4cd34fc Crowdin Translations (#2132)
* New translations flycast.pot (French)
New translations for {languageName} [ci skip]
* New translations flycast.pot (Swedish)
New translations for {languageName} [ci skip]
* New translations flycast.pot (English, United Kingdom)
New translations for {languageName} [ci skip]
2025-12-08 16:15:17 +01:00
flyinghead a99cce27bb Crowdin Translations (#2131)
* New translations flycast.pot (French)
New translations for {languageName} [ci skip]
2025-12-08 14:11:01 +01:00
Flyinghead b2941a8593 ui: more l10n. add plural and context support. no l10n for debug stuff 2025-12-08 11:50:51 +01:00
flyinghead 3ad072f9aa New translations flycast.pot (French)
New translations for {languageName} [ci skip]
2025-12-08 09:37:39 +01:00
flyinghead 14374084d5 New translations flycast.pot (French)
New translations for {languageName} [ci skip]
2025-12-07 23:18:42 +01:00
Flyinghead 5c7a241897 uwp build fix 2025-12-07 21:39:13 +01:00
Flyinghead 7fcc5fa2f7 ui: update .pot and .po 2025-12-07 20:37:35 +01:00
Flyinghead cf2fe027f7 ci: bsd,switch,uwp build fixes. Better workflow names 2025-12-07 20:33:57 +01:00
Flyinghead b13685f0bb ui: switch to tinygettext for i18n 2025-12-07 19:57:03 +01:00
Flyinghead cf7b6bdcb0 ui: more localization 2025-12-06 17:11:48 +01:00
github-actions[bot] 230d971d4d Fetch translations & Recreate libretro_core_options_intl.h 2025-12-05 22:22:07 +00:00
Flyinghead 894c61ed64 libretro build fix 2025-12-04 21:41:36 +01:00
Flyinghead 82cadfde22 win: use GetLocaleInfoEx and localtime. android: build fix 2025-12-04 21:29:20 +01:00
Flyinghead 1586cbaea9 android: use system getifaddrs if available (api 24+) 2025-12-04 18:21:17 +01:00
Flyinghead 2113e4e249 localization support WIP. rewrite ini file reader/writer and more.
Support for localized dates, collation and messages.
Rewrite ini file reader/writer and command line parser.
Standardize all config stuff in the config namespace.
2025-12-04 18:17:13 +01:00
Flyinghead 5a150f4121 Merge remote-tracking branch 'origin/dev' 2025-12-03 19:23:43 +01:00
vkedwardli f178e5d76e Custom texture preloading (#2116)
* Creating a `CustomTextureSource` interface for subclassing
* `DumpReplacedTextures` option
* `PreloadCustomTextures` option
2025-12-02 17:02:19 +01:00
James 9222fa8442 Fix: DreamPicoPort locks up and causes flycast to hang (#2124)
* Use DreamPicoPort-API 1.0.2 which automatically clears USB stalls
* Getting condition in order to detect controller seems to be problematic
* Use the libusb implementation of DreamPicoPort-API on Windows because WinRT is buggy
2025-12-01 16:50:02 +01:00
Flyinghead 77359cefb2 ui: add char filter for dns names and IP addresses text input 2025-12-01 16:49:13 +01:00
Flyinghead a6dc38a35b switch: use sync keyboard applet for keyboard input 2025-11-30 18:23:15 +01:00
Flyinghead eab952fcbe ui: UWP keyboard fix. Fix DPad nav & scrolling with child windows
Detect UWP on-screen keyboard hiding and validate imgui text edit (code
by @danprice142).
Don't use list boxes for paths in Settings > General since child windows
cause issues with DPad/cursor keys navigation and thumbstick scrolling.
2025-11-29 18:06:21 +01:00
Flyinghead 1666eb0875 android: ignore touchpad axes (dualshock 4)
The touchpad also reports motion events on the same axis numbers, but
the range source is different so ignore them.
Override known axes in mapping when the gamepad is connected.
Issue #2109
2025-11-22 11:34:36 +01:00
Flyinghead 5bce763f66 Merge remote-tracking branch 'origin/dev' 2025-11-21 12:49:47 +01:00
Flyinghead 0f4fca415b ui: set VMUPath and SavePath options a single path instead of a list
Fix naming of arcade flash save files when using a custom path.
Get rid of legacy net:nvmem option.
Issue #2111
2025-11-20 16:43:27 +01:00
Flyinghead f2fe918379 android: Virtual Gamepad and Touch Mouse duplicated after app pause
These devices were recreated every time the app is unpaused.
2025-11-20 09:43:53 +01:00
Flyinghead a9be4d111c Use nowide::remove instead of unlink or std::remove 2025-11-20 09:37:04 +01:00
danprice142 ce07a7fbf5 Add user-defined paths for boxart, mappings & cheats (#2108)
* Add user-configurable paths for boxart, mappings, and cheats

Introduces new configuration options for Boxart path, Mappings path, and Cheat path, allowing users to specify custom folders for box art, controller mappings, and cheat files.
2025-11-19 15:30:30 +01:00
Flyinghead e44c03fa0e ui: reset button state when closing mapping popup and on map/unmap
Fixes button state staying on until app restart.
2025-11-19 15:29:15 +01:00
Flyinghead 1dac3695fd imgui: ImguiStyleVar must be deleted before the end of the window 2025-11-18 18:58:44 +01:00
Flyinghead 997f6179dd android: normalize axis value using min and range. Use event.actionIndex
Normalize analog axis values using InputDevice.MotionRange.min and
InputDevice.MotionRange.range.
Specify Event.actionIndex when getting axis values like SDL does.
Issue #2109
2025-11-18 18:57:02 +01:00
Flyinghead bed524f0d0 imgui: ImguiStyleVar must be deleted before the end of the window 2025-11-17 19:18:55 +01:00
Flyinghead 410b37b041 naomi: add dc microphone to king of route 66. Add cd-mic switch binding
King of route 66 uses a maple microphone for the CB-MIC feature.
Also define the mic switch button binding (button 6).
Don't add VMUs to naomi2 games since no game uses them afaik
2025-11-17 19:13:45 +01:00
Flyinghead ead4b51d6f fix savestate regression due to custom path
Issue #2111
2025-11-16 11:45:23 +01:00
Flyinghead 043bb5ee99 Merge remote-tracking branch 'origin/master' into dev 2025-11-16 11:41:40 +01:00
Flyinghead 4747e61908 vgamepad: enable touch mouse for mouse/rotary encoders arcade games
Issue #2110
2025-11-15 15:49:26 +01:00
github-actions[bot] 548cf6fe2e Fetch translations & Recreate libretro_core_options_intl.h 2025-11-14 22:21:15 +00:00
Edward Li 339d288dfb macOS: update Syphon library 2025-11-14 17:13:35 +01:00
Flyinghead 8f7e4bf2d3 aica: fix ALFO and PLFO random waveform
Fixes skating sound in Jet Set Radio
Issue #2107
2025-11-14 17:12:58 +01:00
Flyinghead 922ca3e54b cue: read pregap length from cue file and adjust track start
Use the INDEX 01 mm:ss:ff tag to determine the pregap size and add it to
the track start FAD.
Remove previous +02:00 hack for audio tracks.
Fixes Sonic Team jingle cut off in ChuChu Rocket redump cue.
Issue #2103
2025-11-14 11:12:44 +01:00
Flyinghead 4b09de2fc1 vulkan: recreate swap chain when the vulkan context becomes invalid
Fixes freeze when switching to fullscreen on gentoo.
Issue #2082
2025-11-14 10:57:14 +01:00
Flyinghead 98db7a6cd5 android: vgamepad LT and RT not set to minimum when released
LT and RT were left to 50% depressed when released.
Regression due to 7b0c106fc2
Issue #2104
2025-11-13 10:06:16 +01:00
Flyinghead 3405b5b2a6 aica: use member functions for ChannelEx instead of static ones
More enums
No functional changes
2025-11-12 20:50:31 +01:00
Flyinghead 4320685ff8 uwp: simplify activation and exit URIs handling
Use http::urlDecode to decode URIs.
Move exit URI handling to dc_exit() so that all exit paths are covered.
2025-11-12 13:40:07 +01:00
Flyinghead cd3fe7393c http: add urlDecode and tests for urlEncode and urlDecode 2025-11-11 20:43:13 +01:00
Flyinghead 2da0d392fd move oslib/directory.h function defs into a .cpp file 2025-11-11 19:24:12 +01:00
Flyinghead 8d1bf3382c android: add customisable bios and texture pack folders 2025-11-11 19:15:59 +01:00
Bobby Smith b1489d665e [Libretro] Update AV info when switching a PAL game to 60Hz 2025-11-10 14:16:02 +01:00
Flyinghead fc56bd283e fix double-nested game folder when dumping textures
Change TextureDumpPath to a single path instead of a list
Issue #305
2025-11-10 14:11:14 +01:00
Flyinghead 807e4a20ed aica: yet another LPF improvement
Use more bits for coefficients and formula closer to original hardware.
2025-11-10 13:52:21 +01:00
Flyinghead 943311752e cheats: increase max code length and input text height 2025-11-10 11:50:40 +01:00
Flyinghead e7ad12b031 rend: convert palette textures for area 1 on cpu
Only one palette is supported in the shader, so convert area 1 palette
textures (2-volume mode) on the cpu and handle them normally in the
shader.
Issue #1528
2025-11-09 15:44:48 +01:00
danprice142 b2bc6251c7 Add configurable paths for BIOS, VMU, saves, textures & dumps (#2097)
* Add configurable paths for BIOS, VMU, saves, textures & dumps

Introduces new options for user-defined folders for BIOS, VMU, savestates, game saves, texture packs, and texture dumps. Updates path resolution logic in oslib.cpp to prioritize user-specified paths before falling back to defaults.

* Add UWP support for directory and file access

Introduces UWP-specific implementations for directory handling and file access in core/oslib, including custom opendir, readdir, and closedir functions, this is so we can see subfolders properly on UWP

* Custom Paths section hidden on Android and iOS
2025-11-08 15:06:47 +01:00
Flyinghead 6f38dc3210 msvc build fix 2025-10-28 12:23:22 +01:00
danprice142 4bb32466bd Add UWP protocol activation support for ROM loading from UWP Frontends (#2095)
Adds UWP protocol activation support for ROM loading from UWP Frontends/Browsers, allows launching ROMs via SDL's `SDL_WinRTGetProtocolActivationURI()` and redirection using the 'launchOnExit' URI parameter from protocol activation in SDL_WinRTApp, Closing a Rom/Game from the quick menu or closing the app will make use of `launchOnExit` and redirect to the specified app

Implements launching a URI via Windows.System.Launcher after the game is stopped from the Exit button in the quick menu, enabling return to sender/redirection on UWP

Update SDL submodule to include SDL_WinRTGetProtocolActivationURI()
2025-10-28 11:37:16 +01:00
Rikki Gibson e26d356265 DreamConn: allow using a VMU in slot 2 (#2094) 2025-10-28 11:34:31 +01:00
Flyinghead 265c5c6c3d aica: fix EG rate and LSA > LEA behaviour from real hw tests
EG rate formula specified in the doc is correct but FRS+OCT is ignored
if negative.
If LSA > LEA, playback continues after LEA until reaching LSA. Then CA
is reset and playback stops (including looping sounds). Get rid of
Legacy of Kain hack.
2025-10-27 17:26:42 +01:00
Flyinghead 2b1eb55daa maple: flush VMU files when unloading a game
destroy maple devices when unloading a game to flush and close VMU
files.
2025-10-24 15:31:22 +02:00
Flyinghead bd136b1856 ice: vonot support
ice::term() ice on emu termination
2025-10-24 15:29:12 +02:00
Flyinghead bd8adf1da1 delete outdated dreampi dcnet binary 2025-10-22 15:35:51 +02:00
Flyinghead 11625176c2 ggpo: catch serialization exception when saving state 2025-10-22 15:33:10 +02:00
Flyinghead c64441a02f pvr: avoid pure blue video border color. change to black
Hides blue vertical bars in dinosaur king.
Issue #1246
2025-10-22 15:29:35 +02:00
Flyinghead 8e9eee246d Merge remote-tracking branch 'origin/master' into dev 2025-10-21 15:26:52 +02:00
Flyinghead d0ec876936 arm32/64 dynarec: fix crash when disabling mmu
With block linking, CpuRunning isn't tested immediately so add a special
case in DynaRBI::Relink()
Issue #893
Fixes MINIDUMP-56 MINIDUMP-6V MINIDUMP-6X MINIDUMP-CA
Fixes MINIDUMP-H8 MINIDUMP-K6 MINIDUMP-QN MINIDUMP-YZ
Fixes MINIDUMP-YM MINIDUMP-148
2025-10-21 15:24:31 +02:00
Flyinghead b19cbcbf29 android: clean up openmp stuff 2025-10-21 15:18:48 +02:00
John Lansing bf2bd7efed Move duplicate Exception class to SerializeBase 2025-10-21 12:48:39 +02:00
John Lansing d15d5be44a libretro: detect and communicate buffer overflow on save state serialization 2025-10-21 12:48:39 +02:00
Flyinghead 70c8d1d22b vk: handle acquireNextImageKHR error when resizing window
Ignoring the error leads to a deadlock since the imageAcquired semaphore
can't be signaled.
Fixes freeze when resizing window.
Issue #1620
2025-10-21 12:47:46 +02:00
JamesandRikki Gibson 3917c0181b DreamPicoPort API Update (#2079)
Co-authored-by: Rikki Gibson <[email protected]>
2025-10-20 10:48:23 +02:00
Flyinghead 6f58d204b6 arm, arm64 dynarecs: check FPU enabled after PC and block checks
Fixes hang/crash in Sega Rally 2 when exiting the Records menu.
Issue #1877
2025-10-20 10:46:30 +02:00
Flyinghead d450c84a14 rend: disable texture upscaling on mobile. hide option on desktop 2025-10-20 10:31:35 +02:00
Flyinghead 3b7036d14a android: fix init crash on 32-bit platforms 2025-10-19 15:45:40 +02:00
Flyinghead 5d628f8167 naomi: default eeprom for clubkprz. default nvmem for smarinef
Use clubk2k3 eeprom for clubk2kp and clubk2kpa.
Issue #2086
2025-10-18 18:09:29 +02:00
github-actions[bot] d1ed878446 Fetch translations & Recreate libretro_core_options_intl.h 2025-10-17 22:20:56 +00:00
Flyinghead 28ea3dc69c arcade: add CRC for all working roms. Fix mushik2k bios
All roms can now be loaded by CRC value.
Issue #1952
Fix mushik2k to use the multi-region hacked bios set to Korean region
2025-10-16 18:11:16 +02:00
Flyinghead 031a645ffc move HideCdromDrives check into ui/game_scanner 2025-10-16 18:07:09 +02:00
Flyinghead 0a526d7c94 gdb: fix race condition when stepping. Improve vCont handling
Work around race condition when stepping (CpuRunning still true in
Sh4Executor::Step() on emu thread since Step() hasn't be called yet in
main thread).
Don't advertise vCont commands that aren't supported.
Range stepping end is exclusive.
2025-10-16 17:56:39 +02:00
Flyinghead 3d594054eb libretro build fix 2025-10-15 20:30:23 +02:00
Flyinghead 3b651463dc New [config]HideCdromDrives setting to ignore physical CD/DVD drives 2025-10-15 20:00:30 +02:00
Flyinghead 4e6b5b270b vk: remove image barrier at end of oit pipeline
It seems to cause issues on snapdragon 8 elite (SM8750-AB). These GPUs
never cease to amaze me....
Partial revert of b4b4b3ab1e
Issue #2083
2025-10-15 19:58:41 +02:00
Flyinghead fd8f2e24b8 android: switch to sdk 35, min 21, ndk 29
Drop support for Android 4.* (kitkat, jelly bean)
Minimum SDK 21 (Android 5.0, lollipop)
Target SDK 35 (Android 15)
Build with NDK 29 for 16KB-page by default. Issue #1864
2025-10-14 16:47:50 +02:00
Flyinghead 82b4c55f09 fix gdb server networking
Issue #2081
2025-10-14 14:43:47 +02:00
Flyinghead 8176646c5a android: don't set section name for dynarec code cache
Use #define to declare platform-specific dynarec code buffers
Don't set section name on android to avoid error "W+E load segments are
not allowed"
2025-10-13 19:19:48 +02:00
Flyinghead 3f0dd626c1 android: update oboe to 1.10.0 2025-10-13 19:11:12 +02:00
Flyinghead 8edd8fc447 Merge remote-tracking branch 'origin/dev' 2025-10-13 16:58:06 +02:00
Flyinghead 0c7dca9cb7 sdl: move mapping parser into its own class. Add tests 2025-10-13 16:20:34 +02:00
Flyinghead 086e8bc20c android: exclude libz.so from apk 2025-10-13 16:06:47 +02:00
Flyinghead 11880bca30 atomiswave: fix lightgun games button labels
Issue #2080
2025-10-13 10:25:30 +02:00
scribam 37187467e7 cmake: lower minimum requirement 2025-10-12 11:55:35 +02:00
Flyinghead 7b0c106fc2 input: axes handling improvements
Range for all axes, including triggers, is now [-32768,32767]
Detect trigger direction when mapping and store in mapping file.
sdl: parse controller bindings string to correctly map axes.
Issue #2014
2025-10-12 11:54:16 +02:00
github-actions[bot] fd654614ae Fetch translations & Recreate libretro_core_options_intl.h 2025-10-10 22:20:45 +00:00
Flyinghead 4ee24ca612 vk: test automation fix 2025-10-10 09:01:31 +02:00
scribam 4b3704953f ci: use freebsd 14.3 2025-10-09 19:24:16 +02:00
Flyinghead 6ea9a34e8a macOS CI: update vulkan sdk version 2025-10-08 17:09:05 +02:00
Flyinghead c018d46d00 atomiswave: reset maple devices register during reset
Reset aw_maple_devs to 0 during reset.
Fixes analog controls not working after playing a mouse/trackball or
lightgun game.
Issue #2072
2025-10-06 19:39:31 +02:00
Flyinghead 400da2bdfe vk: add yet another image barrier for libretro (nvidia 30xx issue)
Also add image barrier in endFrame for libretro
Issue #1631
2025-10-06 15:42:52 +02:00
Flyinghead 3b5536dd43 new system sp games: btlracer, monomedal 2025-10-06 15:39:20 +02:00
Flyinghead 34e2f426ac build fix 2025-10-05 15:53:24 +02:00
Flyinghead a246964a99 fix some warnings 2025-10-05 15:35:52 +02:00
Flyinghead b4b4b3ab1e vk: add image barrier to work around nvidia 30xx issue
Add image barrier in VulkanContext::PresentFrame() for nvidia gpus.
Tentative fix for square glitches on nvidia 30xx series.
Issue #1631
oit: Add dependency from external to subpass 2 since vulkan's implicit
one is too relaxed.
2025-10-05 15:34:18 +02:00
github-actions[bot] af5f67c15d Fetch translations & Recreate libretro_core_options_intl.h 2025-10-03 22:21:01 +00:00
Rikki GibsonandJames 909ea496ac DreamPotato connectivity (#1989)
Co-authored-by: James <[email protected]>
2025-10-03 14:34:25 +02:00
Flyinghead 3347dc1a46 custom textures: reset available flag when initializing
Fixes crash when starting a new game without custom texture after one
that has some.
Fixes MINIDUMP-6C9
2025-10-03 14:28:31 +02:00
Flyinghead dd5e148e2b input: don't handle button input when port is set to none
Fixes physical keyboard usage on android, where the keyboard is also
handled as a gamepad and must be disabled.
2025-10-03 14:12:23 +02:00
Flyinghead 1f818ca750 naomi: fix zombrvn and zombrvne default eeproms
zombrvn: Disable free play, do analog calibration
zombrvne: Select 2-player cab
Issue #1980
2025-10-03 11:10:44 +02:00
Flyinghead ab6b95ff4d naomi: spawn needs the region number in its eeprom
The current region must be set in the eeprom, otherwise the eeprom is
reset to default and network settings are ignored.
Issue #696
2025-09-27 17:28:20 +02:00
github-actions[bot] 760c62a3da Fetch translations & Recreate libretro_core_options_intl.h 2025-09-26 22:20:52 +00:00
Flyinghead b2d8ad633d boxart: use flycast-content github repo for arcade games screenshots 2025-09-26 15:30:01 +02:00
Flyinghead f9b47b6f91 boxart: use correct file extension for images 2025-09-23 15:55:36 +02:00
Flyinghead 372ef91211 opengl: per-pixel support for GLES
Issue #2034
2025-09-23 15:53:14 +02:00
scribam 3375f6c476 deps: update sdl to version 2.32.10 2025-09-22 09:57:07 +02:00
scribam c5396a041e cmake: add windows setupapi lib 2025-09-22 09:56:56 +02:00
github-actions[bot] 851dd22664 Fetch translations & Recreate libretro_core_options_intl.h 2025-09-19 22:21:19 +00:00
github-actions[bot] ac32a80352 Fetch translations & Recreate libretro_core_options_intl.h 2025-09-12 22:20:41 +00:00
Flyinghead eff11d6a07 Merge remote-tracking branch 'origin/master' into dev 2025-09-12 18:15:23 +02:00
scribam b6c135b0c6 macOS: update libomp to version 21.1.1 2025-09-12 13:44:50 +02:00
scribam 0fe7073496 macOS: remove workaround with local/pinned/cmake from openmp formula 2025-09-12 13:44:50 +02:00
Edward Li d5c5839a92 macOS libomp: Use local/pinned/cmake in homebrew formula, update libomp to 21.1 2025-09-09 14:38:25 +02:00
Clownacy 7ac45a7e46 Remove -DCMAKE_BUILD_TYPE=Release from .gitlab-ci.yml.
There is no longer a need to set this explicitly, as libretro's CI templates have been corrected to not leave it undefined.

See this commit: https://git.libretro.com/libretro-infrastructure/ci-templates/-/commit/0241726e188a7c077dcff6cd7c1739039a79a7fb
2025-09-08 10:39:53 +02:00
github-actions[bot] 0243f81c26 Fetch translations & Recreate libretro_core_options_intl.h 2025-08-29 22:20:49 +00:00
Flyinghead 2dfa647445 naomi: cab linking support for sgtetris 2025-08-29 09:12:55 +02:00
Flyinghead 9c5408a6d3 macOS build fix
Work around brew change: https://github.com/Homebrew/brew/pull/20414
2025-08-20 14:55:37 +02:00
Eric Warmenhoven d3dcb13ef9 Update .gitlab-ci.yml 2025-08-18 14:40:14 +02:00
Flyinghead 2cb2ac23e0 force real bios for wsb2k2
Issue #1757
2025-08-18 14:39:39 +02:00
Flyinghead e38b0ac01f naomi: delete some unnecessary eeproms
Issue #2023
Fix some warnings
2025-08-14 14:30:20 +02:00
Flyinghead 09b27ddd5c maple: use PantherDC id for full controller. ui: buttons and axes state
Use actual Panther DC identifier for full controller. Fixes KISS: Psycho
Circus.
Issue #2026
Show current state of mapped gamepad buttons and axes in controller
mapping.
2025-08-13 18:26:26 +02:00
Flyinghead 33833cfd1e vonot: don't disable VM when acting as master
Issue #2037
2025-08-12 17:30:22 +02:00
Edward Li 3c5c1e0b66 Indentation should use <space> in imgui_impl_vulkan.cpp 2025-08-12 15:40:14 +02:00
Edward Li 3f446a5835 Use target_compile_definitions to disable imgui shell functions for Switch 2025-08-12 15:40:14 +02:00
Edward Li df3c676a04 chmod 644 2025-08-12 15:40:14 +02:00
Edward Li 9ff3ab618c Fix OpenGL3 and Vulkan backend 2025-08-12 15:40:14 +02:00
Edward Li ba33b8c3f7 IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS for Switch 2025-08-12 15:40:14 +02:00
Edward Li eab13e85bc minor fixes 2025-08-12 15:40:14 +02:00
Edward Li a1f1c9825a Fix EllipsisCharCount
Fonts: Rasterizing ellipsis character from dot as one glyph + avoid preloading if it not needed. https://github.com/ocornut/imgui/commit/4ff1631b316fe849da9a649cf30a79aad752d8bb
2025-08-12 15:40:14 +02:00
Edward Li 43a9c1384a Replace ImFont->FontSize with ImFont->LegacySize 2025-08-12 15:40:14 +02:00
Edward Li b4ab21a768 Reapply ImGuiWindowFlags_DragScrolling 2025-08-12 15:40:14 +02:00
Edward Li 30365d13ef replace with upstream v1.92.1 files 2025-08-12 15:40:14 +02:00
scribam f7eae0ff9e deps: update glslang (#1920) 2025-08-11 17:23:33 +02:00
sonninnos e26f170558 Libretro updates (#2033)
Add -Wno-misleading-indentation
Libretro av_info fps timing correction
Stop forcing audio muting on libretro fast-forward
Allow less strict chd extensions
2025-08-11 09:38:59 +02:00
github-actions[bot] 40e400ab08 Fetch translations & Recreate libretro_core_options_intl.h 2025-08-01 22:21:35 +00:00
github-actions[bot] 5743502cdd Fetch translations & Recreate libretro_core_options_intl.h 2025-07-25 22:21:43 +00:00
Flyinghead 8f33b63153 linux build: fix AppImageTool location 2025-07-22 15:37:54 +02:00
Flyinghead 6fca05f00e sdl: set default axis direction for all axes
Fixes regression with known gamepads.
Only use initial state for unknown controllers.
Issue #2014
2025-07-22 15:24:05 +02:00
Flyinghead 8c07a2e1d6 sdl: detect half-axes using initial state. Fix axis range and direction
Assume that full axes are roughly centered at start up and determine
which axis are half ones, and the axis direction.
Fixes driving wheel pedals range to the expected [0, 32767].
Issue #2014
2025-07-18 11:38:15 +02:00
desyn ffbe266413 bump minimal rcheevos 2025-07-15 15:52:07 +02:00
Flyinghead 5d3b7fae6b libretro: boot dreamcast bios when loading core without content
Get rid of "Boot to BIOS" core setting.
Issue #2007
2025-07-15 15:49:32 +02:00
Flyinghead 738b95d2be miniupnpc: include cstddef before miniupnpc headers
Issue #2010
2025-07-15 15:30:15 +02:00
github-actions[bot] 48c58dbd18 Fetch translations & Recreate libretro_core_options_intl.h 2025-07-11 22:21:51 +00:00
James 54b06a5c34 Recover DreamLink hardware after load state (#2003) 2025-07-10 20:32:00 +02:00
Flyinghead d3a46a1e33 windows: fix crash caused by regression
introduced by 6ea61b9
Issue #2001
2025-07-08 12:36:52 +02:00
Rikki Gibson 4fe2f4760f DreamConn: do not lock mutex multiple times 2025-07-07 11:28:27 +02:00
Flyinghead c0809d771d dcnet: support for Pro Yakyuu Team de Asobou Net!
Delete fakeTCP power smash cheat (not needed)
Add pro yakyuu team cheats
dcnet: Use same port as power smash for pro yakyuu team
2025-07-07 11:26:08 +02:00
Flyinghead 54ed92e2b0 fix ggpo regression
introduced by 4baf529ff7
2025-07-06 22:18:58 +02:00
Flyinghead 6ea61b9084 android: support 4KB and 16KB page sizes on 64-bit platforms 2025-07-06 22:16:06 +02:00
Flyinghead f3e0f09ca9 upgrade oboe and libadrenotools to latest 2025-07-06 18:51:51 +02:00
scribam 301b1a0f00 input: set InputMapping::INVALID_CODE as constexpr 2025-07-06 18:09:23 +02:00
scribam b787427446 android: update gradle and agp 2025-07-06 18:08:54 +02:00
Flyinghead 5df5c70994 dcnet: Power Smash support 2025-07-03 17:18:46 +02:00
Shockblast 75defb590b fix: added missing addrspace inclusion in settings.cpp for GDB_SERVER 2025-07-01 19:29:16 +02:00
scribam fbeaf8ba16 ci: fix switch libretro artifact location 2025-06-30 21:34:41 +02:00
github-actions[bot] 3f79b6baed Fetch translations & Recreate libretro_core_options_intl.h 2025-06-27 22:21:54 +00:00
Flyinghead 34f095a91e mingw build fix 2025-06-24 17:19:12 +02:00
Flyinghead 09247b4503 picotcp: use random local UDP port if already in use
Bind to random local UDP port if requested one is in use.
Log to NETWORK instead of MODEM.
Fix get_last_error name conflict with asio.
2025-06-24 16:54:57 +02:00
scribam 109e084e18 deps: update glslang (#1920) 2025-06-24 16:54:29 +02:00
Isaac Marovitz af043c55cd CMake Refactors (#1979)
Signed-off-by: Isaac Marovitz <[email protected]>
2025-06-24 16:33:12 +02:00
Flyinghead 04999d8969 sh4: fix wince api trace calls 2025-06-21 10:25:52 +02:00
Flyinghead 26b8684e78 serial: don't depend on SerialPTY option on windows
On windows, open serial console window when config::SerialConsole is
enabled. Ignore config::SerialPTY.
2025-06-21 10:23:22 +02:00
Flyinghead eefcb0e05d picotcp: improve ppp dump time resolution 2025-06-21 10:17:09 +02:00
github-actions[bot] 449d256995 Fetch translations & Recreate libretro_core_options_intl.h 2025-06-20 22:21:29 +00:00
Flyinghead 0897f0ed8a ui: split settings into their own files 2025-06-19 23:13:47 +02:00
Flyinghead 9bdb90e605 vk: add missing pp_Texture vertex param (naomi 2) 2025-06-19 17:48:20 +02:00
Flyinghead 5b75975237 naomi: fix zombie revenge analog stick for P2
Issue #1980
2025-06-19 17:29:25 +02:00
Flyinghead c73336a86f boxart: don't save absolute paths for images
Issue #1935
2025-06-17 17:13:27 +02:00
Flyinghead 27318443a7 more atomiswave code into its own files 2025-06-16 22:21:10 +02:00
Flyinghead df93c069f2 sdl: add sdl_gamepad.cpp 2025-06-16 18:50:37 +02:00
Flyinghead 7358f29556 naomi: refactor midiffb into its own file 2025-06-16 18:13:36 +02:00
Flyinghead 8801b2ae16 haptic: add sine effect. implement spring for midi FFB
Add sine effect to haptic api and stop using maple rumble for naomi FFB.
Emulate spring in midi FFB. Thanks to njz3 for his help on decoding the
protocol.
Don't use exponential scale for rumble when using haptic sine effect.

serialize Sh4OCache::writeBack/ThroughBufferCycles
2025-06-16 17:50:36 +02:00
github-actions[bot] b10db5a078 Fetch translations & Recreate libretro_core_options_intl.h 2025-06-13 22:21:19 +00:00
xander-will 71341a27e4 Integer Scaling + Screen-Level Linear Interpolation Options (#1976) 2025-06-13 11:59:15 +02:00
Flyinghead 7e7f645f37 sdl: use SDL_HAPTIC_STEERING_AXIS instead of SDL_HAPTIC_CARTESIAN
tentative fix for Issue #1962
2025-06-13 11:58:19 +02:00
oltolm 146e90dae6 Fix MSYS2 build (#1975)
* fix GCC build
* revert c53a1c42c6
it was fixed in https://sourceforge.net/p/mingw-w64/mingw-w64/ci/251fc7e1c3bd3509e291c1862ee745fce8550360
2025-06-13 09:55:41 +02:00
Flyinghead bb5bd71437 Merge remote-tracking branch 'origin/master' into dev 2025-06-07 16:08:03 +02:00
Flyinghead e8217ca0c5 fix input mapping regressions
Emulator keyboard bindings no longer working when UI is open (Exit).
All-ports mapping not being loaded correctly.
All-ports mapping not working on ports B,C and D.
Restore automatic binding of opposite key/axis.
caused by 64b9e51af0
2025-06-07 16:05:34 +02:00
github-actions[bot] 8f033723a1 Fetch translations & Recreate libretro_core_options_intl.h 2025-06-06 22:21:35 +00:00
Flyinghead b097aa3331 dropping iOS support
well done Gabe!
2025-06-06 09:08:14 +02:00
nexus382andJames Smith 64b9e51af0 button mapping multi binds (#1938)
Add button combination mappping support
- Implement tracking of pressed buttons in GamepadDevice
- Add ability to detect button combinations- Fix mapping system to preserve individual button mappings
- Ensure buttons can work both individually and as part of combinations
- Ensured trigger buttons can be the first button in a combination
- Maintained detection state when binding multiple buttons
- Added special handling to recognize both original and normalized trigger codes

---------

Co-authored-by: James Smith <[email protected]>
2025-06-03 16:28:51 +02:00
nexus382andJames Smith 9b91430513 Flycast UI Themes (#1929)
Added a Theme selection system.
---------
Co-authored-by: James Smith <[email protected]>
2025-06-01 19:03:42 +02:00
Flyinghead d9ec0cb3ab map dinoking P2 rock button to P1 button 4. Show on vgamepad
Remap player 2 Rock button to player 1 button 4 and display on vgamepad
so it's accessible on mobile devices.
Issue #1969
2025-06-01 10:57:59 +02:00
Flyinghead 39d68ec7fa sdl: don't reset haptic gain to 0 on exit
Issue #1963
2025-06-01 10:52:29 +02:00
github-actions[bot] a1a782df38 Fetch translations & Recreate libretro_core_options_intl.h 2025-05-30 22:21:39 +00:00
scribam acc106e988 cmake: disable link option --execute-only on openbsd 2025-05-30 18:45:55 +02:00
Flyinghead 11de0eda13 dx9: half-pixel shift left, not right
Fixes shifting of doom64 screen melt effect
Issue #1939
2025-05-30 18:45:18 +02:00
scribam a7148ad7ae cmake: use c++ extensions for alloca on netbsd/arm64 2025-05-26 19:27:55 +02:00
Flyinghead 27f19a5b54 rend: fix dithering kernel
Use the actual PVR2 dithering kernel (standard 4x4 Bayer matrix) used on
real hardware.
Fixes the screen melt effect of Doom64 (per-pixel only).
Issue #1939
2025-05-26 19:27:02 +02:00
scribam b973453317 ci: build uwp for ARM64 and Win32 2025-05-24 19:24:24 +02:00
github-actions[bot] ffaada9e9d Fetch translations & Recreate libretro_core_options_intl.h 2025-05-23 22:21:23 +00:00
Flyinghead 1953eb0eea outtrigger (US) widescreen cheat. Propeller arena modem string patch 2025-05-22 20:23:23 +02:00
scribam c3275d3c6f ci: update bsd workflows 2025-05-21 23:22:47 +02:00
Flyinghead 5e769d5cd2 Merge remote-tracking branch 'origin/master' into dev 2025-05-21 23:21:49 +02:00
Flyinghead f26e10e44a modem string patch for Mobile Suit Gundam: Federation vs.Zeon
Set minimum tx and rx speed to 0.
2025-05-21 08:28:29 +02:00
Flyinghead 3d60fc8c60 Revert "core/deps/picotcp/modules/pico_tcp.c: More checks for correct header sizes"
This reverts commit ffc32d2d86.
PR #1937
2025-05-21 08:22:35 +02:00
Flyinghead 7adeb53aee arm64 rec: fix typo in FTRC implementation
Issue #1905
2025-05-20 08:52:09 +02:00
James eacadf24a6 DreamPicoPort Mapping Updates (for maraca support) (#1940)
Add rightx and righty for DreamPicoPort (for maraca support)
Add secondary d-pad to dreampicoport mapping
Allow DreamLink device to dictate button/axis name; set custom button names in DreamPicoPort to known codes
2025-05-19 10:37:03 +02:00
Flyinghead 8e14c7f394 Merge remote-tracking branch 'origin/master' into dev 2025-05-18 20:28:41 +02:00
Flyinghead a842edb09e naomi: add mushi2k3 rom 2025-05-18 12:40:49 +02:00
github-actions[bot] e2247e4872 Fetch translations & Recreate libretro_core_options_intl.h 2025-05-16 22:21:04 +00:00
Flyinghead eb662da389 flash: add bba settings if missing. cheats for outtrigger and bomberman
Add bba config information to flash if missing. Fixes outtrigger.
cheat: Enable automode in modem connection string for Bomberman Online
and Outtrigger (US)
cheat: disable dricas auth for outtrigger (JP)
2025-05-15 16:46:54 +02:00
Flyinghead 2b79f93d6b ui: dcnet is no longer experimental 2025-05-14 19:50:14 +02:00
npt-1707 ffc32d2d86 core/deps/picotcp/modules/pico_tcp.c: More checks for correct header sizes 2025-05-10 20:56:59 +02:00
Flyinghead 892cdb7cea x86 dynarec: fix crash
Regression due to e394840344
Fixes MINIDUMP-6RC
2025-05-10 20:56:21 +02:00
Flyinghead 90ed30f70c appimage: fix some missing shared libraries
Issue #1930
2025-05-08 21:43:40 +02:00
Flyinghead 5f4eefab58 naomi: add pjustic (rev b) and pjustica. fix gunsur2 and gunsur2j
Issue #716
2025-05-06 20:04:11 +02:00
scribam 34f8ab292f deps: update sdl to version 2.32.6 2025-05-04 20:57:18 +02:00
Flyinghead 7538424efb cheat to allow The Next Tetris online features
Bypass check of modulation type and rx/tx speeds.
2025-05-04 19:31:09 +02:00
Flyinghead 3976fe1be2 readme: smaller flycast logo 2025-05-04 19:26:21 +02:00
Flyinghead c57b2e1aa7 oit: incorrect color used when src=OTHER_COLOR if dest is 2nd accum
Using src=OTHER_COLOR with destination secondary accumulator should use
the secondary accumulator color, not the final one.
Fixes dark gun in Doom 64 with Ultra graphics (bump mapping).
Issue #1771
2025-05-03 11:05:59 +02:00
Flyinghead 9cc3d1030d vk: (mali) add subpass dependency from subpass 2 to external
Add subpass dependency from the last subpass to external/top of pipe.
Fix glitches in upper left corner when using OIT on Mali GPUs.
Issue #1014
Issue #1234
Issue #1356
Issue #1497
Issue #1852
2025-05-03 11:05:49 +02:00
Flyinghead 48e964235c vk: (mali) add memory barrier in GetLastFrame
Add memory barrier between drawing and transfer.
Fix savestate thumbnail image corruption.
Issue #1922
2025-05-03 11:05:40 +02:00
Flyinghead e148fabeae libretro: don't allow saving states in a background thread
Issue #1903
2025-05-03 11:03:11 +02:00
Flyinghead d3b8a67cba naomi: add missing zombrvne rom 2025-05-03 11:01:05 +02:00
Flyinghead 03f2ef6df0 libretro: fix crash when terminating vulkan context early
Issue #1906
2025-05-03 10:57:34 +02:00
Flyinghead e1ab9af89d oit: incorrect color used when src=OTHER_COLOR if dest is 2nd accum
Using src=OTHER_COLOR with destination secondary accumulator should use
the secondary accumulator color, not the final one.
Fixes dark gun in Doom 64 with Ultra graphics (bump mapping).
Issue #1771
2025-05-02 17:46:29 +02:00
Flyinghead 3330225de4 vk: (mali) add subpass dependency from subpass 2 to external
Add subpass dependency from the last subpass to external/top of pipe.
Fix glitches in upper left corner when using OIT on Mali GPUs.
Issue #1014
Issue #1234
Issue #1356
Issue #1497
Issue #1852
2025-05-02 15:37:29 +02:00
Flyinghead 58ce9a4159 vk: (mali) add memory barrier in GetLastFrame
Add memory barrier between drawing and transfer.
Fix savestate thumbnail image corruption.
Issue #1922
2025-05-02 15:30:45 +02:00
Flyinghead 1b5c8678a4 dreampi: select best access point when connecting 2025-04-30 23:02:41 +02:00
Flyinghead c20ec680a8 Merge remote-tracking branch 'origin/master' into dev 2025-04-30 22:58:14 +02:00
Flyinghead 57cc7b4a60 Fix crash on apple silicon due to cmake 4 change
The test to set TARGET_MAC doesn't work anymore with cmake 4.
Fixes #1918
2025-04-30 16:40:25 +02:00
Flyinghead cdef7f7444 dricas auth cheat for Aero Dancing FSD 2025-04-28 21:10:10 +02:00
nexus382 f62582a4bd Screen position bug fix with multiple monitors (#1916) 2025-04-28 18:58:33 +02:00
github-actions[bot] 30e1a858da Fetch translations & Recreate libretro_core_options_intl.h 2025-04-25 22:21:57 +00:00
kapodamy b04f0eb530 Allow tweak GDB related options and see Virtual memory addresses 2025-04-24 21:32:40 +02:00
Flyinghead ef3d3bd246 iOS: fix crash
due to f3a33642f8
2025-04-23 16:39:16 +02:00
github-actions[bot] 2ca596ae20 Fetch translations & Recreate libretro_core_options_intl.h 2025-04-18 22:21:32 +00:00
James Smith 1273c06d4b Load mapping for sdl_gamepad through loadMapping() and getDefaultMapping() instead of custom implementation 2025-04-16 21:05:40 +02:00
Flyinghead 25a882341d linux AppImage build needs libfuse 2025-04-15 14:30:55 +02:00
Flyinghead 75064f5683 ci: build AppImage with ubuntu 22 2025-04-15 14:30:46 +02:00
Flyinghead 93aaf50b4f Revert "libretro: don't restart the emu in retro_serialize_size"
This reverts commit aff64d43d4.
Issue #1885, #1903
2025-04-15 14:26:57 +02:00
Flyinghead 332be9c8aa linux AppImage build needs libfuse 2025-04-12 09:52:54 +02:00
Flyinghead 77776be20b upgrade xBRZ to 1.8 2025-04-12 09:34:39 +02:00
Flyinghead f7676faa68 ci: build AppImage with ubuntu 22 2025-04-12 09:33:26 +02:00
github-actions[bot] 93f463296d Fetch translations & Recreate libretro_core_options_intl.h 2025-04-11 22:21:32 +00:00
Flyinghead aff64d43d4 libretro: don't restart the emu in retro_serialize_size
Stop the emu in retro_serialize_size and don't restart it until after
the state is saved in retro_serialize. This avoids issues when the
actual state size is greater than what retro_serialize_size returned.
Issue #1885
2025-04-11 12:02:53 +02:00
Xphalnos 240be07bbb Misc: Remove Duplicated Semicolons 2025-04-10 11:30:33 +02:00
Flyinghead 786ccf4139 vulkan: fix crash when fast-forwarding with libretro core
Issue #1901
2025-04-10 11:29:52 +02:00
Flyinghead 10a5ed6cec network: ICE improvements
F355: flush tx buffer every 34 bytes
Flush tx/rx buffers when loading a state
Use moving average for stats
2025-04-08 21:40:26 +02:00
Flyinghead b9ba3f1e63 libretro and switch build fixes 2025-04-07 19:43:12 +02:00
JamesandNexus382 34fda43d9c DreamPicoPort fixes/enhancements (#1892)
* added getFunctionDefinitions
* Set DreamPort device default deadzone to 0% while preserving user customization
* Fixed default dead zone 0% for DreamPicoPort; set UsePhysicalVmuMemory default to true
* Use DisabledScope for UsePhysicalVmuMemory; moved DreamLinkGamepad::getDefaultMapping() to protected scope to match parent
* Set unique ID for DreamPicoPort devices so they enumerate the same way by flycast on every load
* Implement DreamLinkGamepad::resetMappingToDefault() and set C, D, and Z buttons for DreamPicoPort
---------
Co-authored-by: Nexus382 <[email protected]>
2025-04-07 19:23:14 +02:00
Flyinghead b602cc9c57 network: add ice/websocket match code system for battle cable 2025-04-07 19:22:02 +02:00
Flyinghead 63534c5d56 add websocket++ and libjuice as submodules 2025-04-07 18:06:44 +02:00
Flyinghead 0267de4997 upgrade xbyak to 7.23.1. Convert to git submodule 2025-04-07 17:58:21 +02:00
Xphalnos 56c00b0eb5 [GUI] Adding Rounded Borders 2025-04-07 09:06:12 +02:00
Flyinghead 3114394019 Merge remote-tracking branch 'origin/dev' 2025-04-05 22:30:31 +02:00
github-actions[bot] e5f13b6858 Fetch translations & Recreate libretro_core_options_intl.h 2025-04-04 22:21:17 +00:00
Flyinghead 3193d2cf5d quick'n'dirty cmake 4.0 support 2025-04-02 14:20:33 +02:00
scribam 0f18e2e167 deps: update sdl to version 2.32.4 2025-04-01 17:17:22 +02:00
Flyinghead 5908b1a84a picoppp: send UDP data synchronously
Use send_to instead of async_send_to for UDP sockets. Set socket to
non-blocking and discard data if socket full. Tentative fix for
corrupted Speed Devils Online UDP packets.
Fix uncaught exception if dns server name can't be resolved.
Use lambda instead of std::bind.
2025-04-01 17:16:39 +02:00
Flyinghead 4d6168dd0b dcnet: find best access point 2025-04-01 17:07:54 +02:00
scribam 2d1c602a69 ci: remove ninja installation for macos runner 2025-03-30 16:00:45 +02:00
Flyinghead 130675aae3 gles3: use GL_R8 internal format for fog texture
regression due to 3469b7f
Issue #1886
2025-03-28 11:12:30 +01:00
Xphalnos 75d5cc4d63 Use DXGI 1.6 and D3D11_4 2025-03-27 17:42:48 +01:00
Isaac Marovitz 5a110fd666 Replace sprintf with snprintf (#1884)
Signed-off-by: Isaac Marovitz <[email protected]>
2025-03-25 18:47:07 +01:00
Flyinghead 13436a4dda support for (some) encrypted cheats 2025-03-23 18:07:56 +01:00
James 2631706fe2 Enable physical VMU memory access (#1881) 2025-03-22 15:59:42 +01:00
Xphalnos ea6d5f8732 Update xxHash to 0.8.3 + Use faster algorithm 2025-03-22 15:58:22 +01:00
Flyinghead 305cae496f dcnet: don't try to detect ppp frames on output. send data immediately 2025-03-22 15:24:23 +01:00
Flyinghead 416085980f dcnet: empty modem buffer on init. Allow hostname override in emu.cfg 2025-03-19 19:46:16 +01:00
RTA 8b35fb80b4 Reset static device state on core unload to ensure input descriptors refresh 2025-03-17 15:36:18 +01:00
Flyinghead 5c1249c55f ci: another switch build fix attempt
gitsome's gh isn't compatible with the github one. Specify rclone
version to avoid gh use.
2025-03-17 15:00:40 +01:00
Flyinghead fac44e4a2f ci: switch build fix. Filter *.json files on android 2025-03-17 14:41:43 +01:00
Flyinghead 4ea317d51f ci: use rclone for android and switch builds 2025-03-17 13:50:12 +01:00
Flyinghead 310d5e612e ci: use rclone instead of aws cli 2025-03-17 10:26:23 +01:00
Flyinghead 62778b1a68 ci: testing scaleway 2025-03-16 23:33:06 +01:00
Flyinghead 604415b0d2 ci: trying s3 cp instead of sync 2025-03-16 22:05:15 +01:00
Flyinghead 49ea1135ee ci: ditch configure-aws-credentials 2025-03-16 20:56:07 +01:00
Flyinghead 46e2d52b75 store build artifacts on OVH 2025-03-16 20:31:17 +01:00
github-actions[bot] 44f77408e4 Fetch translations & Recreate libretro_core_options_intl.h 2025-03-14 22:21:10 +00:00
scribam 2860aa96f4 deps: update sdl to version 2.32.2 2025-03-12 18:30:06 +01:00
scribam 8b0a5fa480 deps: update oboe to version 1.9.3 2025-03-12 18:30:06 +01:00
scribam b150ba2b62 android: update project 2025-03-12 18:30:06 +01:00
github-actions[bot] 48bce25820 Fetch translations & Recreate libretro_core_options_intl.h 2025-03-07 22:19:55 +00:00
Eric Warmenhoven 562f78cd71 libretro: build osx-x64 build on new builder 2025-03-05 21:25:39 +01:00
github-actions[bot] 9e0573aa98 Fetch translations & Recreate libretro_core_options_intl.h 2025-02-28 22:21:22 +00:00
Flyinghead d23b3b6bc7 rcheevos: include flycast version in user-agent header
Issue #1798
2025-02-27 11:26:52 +01:00
Flyinghead 1d9f8ab438 custom textures: fix static deinit order issue 2025-02-27 11:20:31 +01:00
Flyinghead df3c075fba Force TV composite for Caesar's Palace 2000 and PAL for the EU edition
Issue #1851
2025-02-27 11:00:48 +01:00
Mike Kosek 9cdd0aab9f Rename DreamPort to DreamPicoPort (#1850)
* Renamed DreamPort to DreamPicoPort
* Removed fallback to deprecated config keys
2025-02-22 08:11:05 +01:00
github-actions[bot] e4d9220f3f Fetch translations & Recreate libretro_core_options_intl.h 2025-02-21 22:21:05 +00:00
Flyinghead 0bf9c64b1b Merge remote-tracking branch 'origin/dev' 2025-02-18 09:27:07 +01:00
JamesandMike Kosek 4e74619d9e Harden serial comm interface (#1847)
* Harden serial interface
* Fixed build errors
* Minor tweaks
* Automatically attached A->A, B->B, etc
* Interfaces go in decending order
* Do a modulo in case I decide to move interface numbers
* Do specifically mod 4
* Call SDL_JoystickGetDeviceInstanceID which seems to help with a Windows bug
* Made MapleDevices smart pointers so they can freely be swapped out without leaking
* Fixed issues some issues reported by kosekmi
* Added missing bracket
* Fixed another compile issue
* Fixed a missing else
* If device had no serial number, fall down to checking name
* Use new DreamPort firmware dynamics
* Added missing parens
* Fixed screen blanking issue
* Added const
* Reset VMU Screen on game/emulator exit
* Added gameTermination() hook to hardware gamepad
* Send SW port on game termination
* Added checks so port is only sent if data is valid
* Fixed bug: wait for write to complete in sendCmd
* Fixed bug leading to multiple pointers to VMU and Rumble pack
---------

Co-authored-by: Mike Kosek <[email protected]>
2025-02-16 17:36:57 +01:00
Flyinghead 0414a90500 dcnet: don't use timer for modem. better error reporting. race condition
Use io_context::post() instead of a timer to read from modem.
Notify user if connection to DCNet fails.
Fix race condition/crash with BBA.
Use lambdas instead of std::bind.
2025-02-16 17:33:56 +01:00
github-actions[bot] 5866e97d18 Fetch translations & Recreate libretro_core_options_intl.h 2025-02-14 22:21:01 +00:00
scribam 387e2427b4 deps: update sdl to version 2.32.0 2025-02-10 18:13:12 +01:00
Flyinghead 5187258f4b dcnet: BBA support
Stop network service when bba is reset. Start it on first frame out.
dcnet: close connection on dhcp release
picoppp: close all sockets and acceptors when stopping.
2025-02-10 18:12:24 +01:00
e1da1c3a55 CMake: Change core install location to lib/libretro (#1842)
* CMake: Change core install location to lib/libretro

* fix CI build artifacts location

---------

Co-authored-by: Andrew Wong <[email protected]>
Co-authored-by: Flyinghead <[email protected]>
2025-02-10 13:25:34 +01:00
Flyinghead 84356e3b26 dreampi.py: shake off the modem on first run (from scrivani) 2025-02-08 16:20:47 +01:00
Flyinghead c7499df3a8 cheats for Aero Dancing i & i2 in BBA mode 2025-02-08 16:17:15 +01:00
github-actions[bot] 03f7a0f605 Fetch translations & Recreate libretro_core_options_intl.h 2025-02-07 22:20:30 +00:00
Flyinghead f3c838d19d dricas auth cheat for Aero Dancing i - Jikai Saku Made Matemasen 2025-02-05 18:29:56 +01:00
Flyinghead d6f3ca986d fix from jscrivani to work around the initial failed connection 2025-02-05 18:28:12 +01:00
Flyinghead b9df7d3469 network digital outputs: check config option when game starts
Avoid the need to restart when enabling or disabling this option
Send game name to newly connected sockets
ignore SIGPIPE (telnet)
Log network errors
2025-02-04 17:46:02 +01:00
Flyinghead bf1d4a56ae gdrom: fix savestate compatibility issue
consequence of c860807fef
2025-02-04 17:27:47 +01:00
Flyinghead a682e20bbd modem: modem must hang up as soon as reg20 is written
some games don't do a full reset and the modem would stay connected.
Remove debug file dumps.
2025-02-04 17:20:15 +01:00
Flyinghead 5b9dd96064 option to set the ISP username in flash rom 2025-02-04 17:17:38 +01:00
Flyinghead a99a72589b better cheats for dricas auth 2025-02-04 17:14:01 +01:00
James 77ed9c20de Connect to DreamConn only once registered (#1828) 2025-02-03 18:19:22 +01:00
Flyinghead 7cb0fe53d7 add cheats to bypass dricas authentication
Aero Dancing i
Daytona USA (JP)
Sega Tetris
Golf Shiyou Yo 2
Hundred Swords
2025-02-03 18:18:28 +01:00
Flyinghead 3db0fce9b6 bypass dricas auth in aero dancing i 2025-02-02 17:12:54 +01:00
Flyinghead 6a15e7a3aa rend: LoD0 of VQ YUV textures is invalid. Use LoD1 instead.
Fixes background magenta textures in WSB2K2. Issue #1038
2025-02-02 17:11:22 +01:00
github-actions[bot] 431d71de5e Fetch translations & Recreate libretro_core_options_intl.h 2025-01-31 22:20:31 +00:00
Flyinghead 914fa29d31 gdrom: increase dma delay for small DMA transfers
Fixes WSB 2K2 freezes.
Issue #1038
2025-01-31 18:09:33 +01:00
Flyinghead c143cae09b disable Daytona USA (US) widescreen cheat
breaks online features
2025-01-31 15:32:18 +01:00
James a35e8214df Serial comm update (#1820)
* Broke apart DreamConn and DreamcastControllerUsb implementations; added serial timeouts; process serial in thread; removed crc from DreamcastControllerUsb sdl definition

* Added missing include statements

* Changed INFO_LOG to NOTICE_LOG on a couple of lines

* Clear the read queue once string is pulled off of it
2025-01-28 17:27:26 +01:00
Flyinghead d84108de32 maple: yet another maple timing change
Use 2 Mb/s for console and 740 Kb/s for devices.
Fixes Silent Scope vmu load issue at boot. Issue #1796
D+Vine has issues detecting A and B presses with one controller
connected but it does work eventually. No issue with more than 1
controller. Issue #1279
Power Drift only boots with one controller connected. Crashes with 2 or
more.
2025-01-28 17:26:22 +01:00
Flyinghead 1513c6cab8 msvc build fix 2025-01-27 17:47:22 +01:00
Mike Kosek 34104724a6 Added config file override for DreamcastControllerUsb Serial Device 2025-01-27 17:30:46 +01:00
Flyinghead b70c2791b2 dynarec: proper ftrc implem. (arm32/64) Use double for fipr/ftrv
Use double for canonical FIPR and FTRV on all platforms.
Fix interpreter implementation of FTRC.
Fix canonical implementation of cvt_f2i (FTRC).
arm32: use Vfma instead of Vmla for FMAC. Vfma does not a fused
muliply-add, Vmla doesn't.
arm32: Use canonical implementations of FIPR and FTRV.
arm32,arm64: Correct implemetation of cvt_f2i (FTRC)

Fixes desync with NBA 2k1/2k2 online games.
2025-01-27 17:30:06 +01:00
scribam ccdd53ee0e deps: update sdl to version 2.30.11 2025-01-25 17:02:56 +01:00
scribam 57a3e4ca53 ci: update netbsd version 2025-01-25 17:02:56 +01:00
scribam b92ac1544e android: update project 2025-01-25 17:02:56 +01:00
Mike Kosek 26d386f833 Fixed handling of asio::io_context 2025-01-25 17:02:18 +01:00
Mike Kosek ce0060126e Now adding mappings for Dreamcast Controller USB during input_sdl_init() 2025-01-25 17:02:18 +01:00
Mike Kosek 4389a3ffa6 Reverted Dreamcast Controller USB mapping workaround 2025-01-25 17:02:18 +01:00
Flyinghead b3fa453d75 dreamconn: asio::serial_port isn't defined on all platforms 2025-01-25 17:02:18 +01:00
Mike Kosek 32c274f407 Updated serial device search on Windows to select only matching VID/PID 2025-01-25 17:02:18 +01:00
Mike Kosek cb2d3e3776 Maple Message to Dreamcast Controller USB now prefixed for compatibility with newest version 2025-01-25 17:02:18 +01:00
Mike Kosek 28f5dd983e Now properly setting and checking left/right Trigger 2025-01-25 17:02:18 +01:00
Mike Kosek 160828a4c7 Overrode default mapping for DreamcastControllerUsb on MacOS/Windows 2025-01-25 17:02:18 +01:00
Mike Kosek aee932808e Removed unused function getDreamcastControllerType() 2025-01-25 17:02:18 +01:00
Mike Kosek 77636391fc Enabled the usage of VMUs and Rumble Packs with Original DC Controllers 2025-01-25 17:02:18 +01:00
Flyinghead 944af44c7f naomi: some digital outputs still missing for f355
Lamps use 14 bits of output in f355.
2025-01-25 17:00:48 +01:00
github-actions[bot] 5c27648699 Fetch translations & Recreate libretro_core_options_intl.h 2025-01-24 22:21:08 +00:00
Flyinghead 7ef35eaf68 dreampi: fix paths 2025-01-24 18:26:44 +01:00
Flyinghead bb733323ce dcnet utility for dreampi 2025-01-24 18:13:56 +01:00
Flyinghead 1b6bf25e7a naomi: broadcast digital outputs even when using FFB drive
The first byte is for lamps, the next two are for FFB.
Fixes missing lamp notifications when broadcasting digital outputs
(F355).
2025-01-23 18:40:24 +01:00
Flyinghead aa65b81f89 switch build fix 2025-01-23 18:34:58 +01:00
Wunkolo da9f030a0e vk: Refactor swapchain format selection
Use a series of stable-partitions to sort the list of available formats
to find the best candidate surface-format/color-space that is a non-sRGB
format being presented in an sRGB color-space. Vulkan mandates that all
surface formats that have SRGB forms must also support a UNORM form.

This is basically just RGBA8/BGRA8 on all platforms still, but in a way
that is still capable of falling back to secondary formats in a stable
way in the case that the primary choice is not available.  Mobile
devices especially have a LOT of secondary HDR surface formats and other
weird formats that can be used to present such as RGBA16 or RGBA565.
With stable partitions, if we can't get our best option then there is
always a "next best thing" to fall back on rather than relying on the
driver-order.
2025-01-22 18:28:55 +01:00
Wunkolo a0e1eb0005 vulkan: Allow native RGB8 screenshot framebuffers when available
Some notable devices support native RGB8 color-attachments:
https://vulkan.gpuinfo.org/listdevicescoverage.php?optimaltilingformat=R8G8B8_UNORM&featureflagbit=COLOR_ATTACHMENT

This removes the need to do a manual RGBA->RGB format conversion on the CPU in favor of a direct memcpy from the downloaded texture, when available.
2025-01-22 18:27:20 +01:00
Flyinghead 5cc15429cd Force Native Depth Interpolation on UWP/Xbox. UWP build fix 2025-01-22 18:26:29 +01:00
Flyinghead 18656f5986 picoppp: terminate MiniUPnP in a background thread 2025-01-21 18:21:47 +01:00
Flyinghead 536be34853 dcnet: update emu network state when connected 2025-01-21 18:19:39 +01:00
Flyinghead 5b513cac5b gl: use textureProj for palette texture sampling when possible/available
Fixes UV precision issue when doing the Z-divide manually.
Issue #1804
2025-01-21 18:18:05 +01:00
Flyinghead 5efdf58e85 custom texture: skip updating a texture if its hash hasn't changed
Some textures are frequently updated because of their location in vram,
close to a hotspot. This causes the custom texture to never be loaded.
This change will skip the unneeded texture update, allowing the custom
tex to replace it when available.
Issue #1812
2025-01-21 18:12:10 +01:00
github-actions[bot] a93f3697d6 Fetch translations & Recreate libretro_core_options_intl.h 2025-01-17 22:20:48 +00:00
Flyinghead b98e833c57 uwp: define _WIN32_WINNT to avoid compile issues with asio 2025-01-17 17:46:49 +01:00
github-actions[bot] 2e89848041 Fetch translations & Recreate libretro_core_options_intl.h 2025-01-10 22:20:35 +00:00
Flyinghead bb4ba3ed89 dcnet: libretro support 2025-01-09 19:01:50 +01:00
Flyinghead a5a338d408 experimental DCNet cloud service 2025-01-09 18:48:29 +01:00
Edward Li 3114344414 [macOS] Fix booting game stuck at "Starting..." when Video Routing is enabled with OpenGL 2025-01-09 13:59:54 +01:00
oltolm 20ba79668f fix compiler warnings 2025-01-09 13:59:21 +01:00
oltolm 7a4f9d8667 CMake: fix build on Windows 2025-01-06 19:46:52 +01:00
Wunkolo 22bff1b017 cmake: Use set_target_properties to set C++ standard
This stops dependencies from inheriting a global C++20 standard setting
and instead designates the flycast target alone to be a C++20 project.
2025-01-05 22:06:37 +01:00
Wunkolo 9344856c7e ggpo: Fix malformed template constructor
This form is not needed for a template-class's constructor.
Fix for C++20 builds
2025-01-05 22:06:37 +01:00
Wunkolo b2c1d134e1 deps: Update luabridge to 2.8
From 2.6 to 2.8. Includes some C++20 fixes.
2025-01-05 22:06:37 +01:00
Wunkolo 160d3e7ef3 cmake: C++20 enablement
Enables the C++ 20 standard
2025-01-05 22:06:37 +01:00
Flyinghead 3cf4808033 fix perf regression on macOS and iOS. Add Silent Scope (JP) identifier.
Regression introduced in f3a3364
CopyToVRAM wasn't enabled for Silent Scope (JP). Issue #1796
2025-01-05 22:06:37 +01:00
Flyinghead 1bd894f941 rend: reset scissoring before drawing final modvol quad
See d618abc320
Issue #1797
2025-01-05 22:06:37 +01:00
Edward Li 8dc55721d0 Generate Xcode project which can support both iOS and iOS Simulator 2025-01-05 22:06:37 +01:00
scribam c94deaee9d build: drop unsupported architectures 2025-01-05 22:06:37 +01:00
scribam 95583583ca ci: force ubuntu-22.04 on libretro-x86_64-pc-linux-gnu target 2025-01-05 22:06:37 +01:00
Flyinghead 42752acef5 Fix crash when reading 2448-byte sectors
Fixes MINIDUMP-5J0, MINIDUMP-5M7, MINIDUMP-5M8, MINIDUMP-5MA,
MINIDUMP-5MF
2025-01-05 22:06:37 +01:00
github-actions[bot] 45c700ab60 Fetch translations & Recreate libretro_core_options_intl.h 2025-01-05 22:06:37 +01:00
Flyinghead a3902fc278 sdl: disable rumble when rumble power is set to 0
Issue #1766
Issue #1783
2025-01-05 22:06:37 +01:00
Flyinghead 0606f3e63e ios: make the keyboard remappable 2025-01-05 22:06:37 +01:00
Flyinghead 0558c957e8 libretro: set an SDL hint to keep flycast SIGSEGV handler
Thanks to mrfixit2001 for helping troubleshoot the issue.
2025-01-05 22:06:37 +01:00
github-actions[bot] 2ca9187137 Fetch translations & Recreate libretro_core_options_intl.h 2025-01-05 22:06:37 +01:00
Flyinghead 5add29cbed fix perf regression on macOS and iOS. Add Silent Scope (JP) identifier.
Regression introduced in f3a3364
CopyToVRAM wasn't enabled for Silent Scope (JP). Issue #1796
2025-01-05 10:21:54 +01:00
Flyinghead 5a1c3f80f5 msvc32 build fix 2025-01-02 13:55:39 +01:00
Flyinghead a0e8e9a9df libretro: windows build fix 2025-01-02 12:11:45 +01:00
Flyinghead 45f4378184 network: use asio for modem/bba. DirectPlay4 support
DirectPlay4 support: StarLancer, PBA Bowling
miniupnp: remove unused stuff
2025-01-02 11:27:11 +01:00
Flyinghead c7d030c2e6 uwp: enable vmem. fix dynarec crash. windows: dll loading utility
uwp: Fix crash in x64 dynarec when rewriting due to wrong unprotect
range.
uwp: load MapViewOfFileEx dynamically to map regions at a given address.
dll load and function lookup utility.
2025-01-02 11:13:25 +01:00
Flyinghead 2bee6b1ce4 picotcp: fix msvc build. reduce logging 2025-01-02 11:01:58 +01:00
Flyinghead 522505273e miniupnpc: minissdpc.c: fix memory allocation error
backport
https://github.com/miniupnp/miniupnp/commit/9698973600e639ddf0ceb0ee565d7297598486fe
2025-01-02 10:57:38 +01:00
Flyinghead 0481f5464d misc non-functional changes 2025-01-02 10:54:19 +01:00
Flyinghead 7478476f9c multithreading utilities (periodic, worker thread) 2025-01-02 10:50:46 +01:00
Flyinghead 76c3695f6e log: support udp log server 2025-01-02 10:35:27 +01:00
Flyinghead e18ada7225 rend: reset scissoring before drawing final modvol quad
See d618abc320
Issue #1797
2024-12-31 18:22:07 +01:00
Edward Li f3a33642f8 Generate Xcode project which can support both iOS and iOS Simulator 2024-12-30 12:23:27 +01:00
scribam 2f03c1b147 build: drop unsupported architectures 2024-12-30 12:22:59 +01:00
scribam b7e2f5e489 ci: force ubuntu-22.04 on libretro-x86_64-pc-linux-gnu target 2024-12-30 12:22:28 +01:00
Flyinghead d01cbde41c Fix crash when reading 2448-byte sectors
Fixes MINIDUMP-5J0, MINIDUMP-5M7, MINIDUMP-5M8, MINIDUMP-5MA,
MINIDUMP-5MF
2024-12-30 12:20:29 +01:00
Flyinghead 11068012fe picotcp: support threads with msvc. Fix msvc pragma push issue
Enable threading support with msvc, similar to posix.
Fix missing pragma pop after picotcp include files
2024-12-30 12:15:36 +01:00
Flyinghead a06791780d Merge remote-tracking branch 'origin/dev' 2024-12-29 10:59:49 +01:00
Flyinghead ee1a7167f6 dreamconn: open pause menu when LT+RT+Start is pressed
Issue #1305
2024-12-28 15:39:11 +01:00
Flyinghead 10aaec1619 dreamconn: send data with socket to avoid fragmentation. Build fix
Issue #1305
2024-12-28 12:21:26 +01:00
github-actions[bot] 32d8d9a8c3 Fetch translations & Recreate libretro_core_options_intl.h 2024-12-27 22:20:31 +00:00
Flyinghead 553f77c675 dreamconn: send buzzer messages. Rename controller. Use asio iostream
Issue #1305
2024-12-27 18:47:12 +01:00
Flyinghead 9402981b43 net: add asio. use it for the gdb agent 2024-12-27 18:39:13 +01:00
Flyinghead 9038ef6d8a sdl: disable rumble when rumble power is set to 0
Issue #1766
Issue #1783
2024-12-26 16:31:23 +01:00
Flyinghead 1d290560e0 picotcp: accept PSH flag in SYN+ACK packets
Windows CE adds the PSH flag to SYN+ACK packets, which isn't normally
valid, so just ignore the flag. See dcgamespy filter in dreampi.
2024-12-26 13:04:07 +01:00
Flyinghead 59e844db15 picotcp: Integer Overflow in pico icmp4
upstream
https://github.com/virtualsquare/picotcp/commit/cc923d2a1221d2066dc9270fb86c5c43635ce06e
2024-12-26 12:38:26 +01:00
Flyinghead 6853238c8b picotcp: Discard incoming TCP segments with zero-len options
upstream
https://github.com/virtualsquare/picotcp/commit/7aa288510a033691c3f09e2c43e10f07e24434b7
2024-12-26 12:12:48 +01:00
Flyinghead f0aa1abf89 picotcp: Fixed bug in tcpopt_len_check (rolling back index).
upstream
https://github.com/virtualsquare/picotcp/commit/f569ed8fb66630fa4a4dc59259253767da67b23a
2024-12-26 12:03:09 +01:00
Flyinghead cfd3677746 picotcp: add socket event before socket is deleted
Socket callback is called with PICO_SOCK_EV_DEL just before the socket
is deleted. After returning from the callback, the socket pointer is no
longer valid.
2024-12-26 11:45:58 +01:00
Flyinghead 50a10598e4 picotcp: Resolved memory leak
upstream
https://github.com/tass-belgium/picotcp/commit/83527c69d3f69ba2b049b827c4f80c0633dce390
2024-12-26 11:33:26 +01:00
Flyinghead 9bfd48cf40 ios: make the keyboard remappable 2024-12-21 12:26:16 +01:00
Flyinghead b72750c7d5 libretro: set an SDL hint to keep flycast SIGSEGV handler
Thanks to mrfixit2001 for helping troubleshoot the issue.
2024-12-21 12:25:30 +01:00
Flyinghead b9fdd5070b dreamconn: detect dreamconn+ with VID/PID. Send maple data as text.
Create DreamConn gamepad when detected.
Send/receive maple data in ascii dump form.
Simplify maple device creation.

Issue #1305
2024-12-20 20:02:58 +01:00
Flyinghead 6c3d77b012 more windows build fix 2024-12-16 18:14:21 +01:00
Flyinghead 7fee0d2d29 cheat to work around fur fighters hanging
Root issue seems to be TCNT0 returning the same value twice, which
breaks the game calculation of the game speed/frame rate.
Issue #145
2024-12-16 17:54:02 +01:00
Flyinghead dfd4dbebc4 fix windows build 2024-12-16 17:34:38 +01:00
Flyinghead 34a5e7f47d input: dreamconn+ support prototype
Issue #1305
2024-12-16 17:17:45 +01:00
Flyinghead 3f8d645c42 rend: last frame was sometimes presented after the game is stopped
When the clearLastFrame flag is set, renderers should return false from
Present() and RenderLastFrame() until a new frame is rendered.
2024-12-15 15:55:28 +01:00
Flyinghead ecd0305a60 fix build without dynarec 2024-12-14 19:43:32 +01:00
Flyinghead 667e3906d8 sh4: work around previous SCIF issues when loading a state
Force SCIF_SCBRR2 to 8 bits when loading a state.
See e03e11b8a9 and
6115a918b2.
2024-12-14 19:13:05 +01:00
Flyinghead e5b1d56a7c rend: split texture conversion routines into their own file 2024-12-14 19:07:18 +01:00
Flyinghead d789b57412 rend: get rid of globals for fog/palette updates & tex cache flush
Add state vars to Renderer
Use events to detect loading a state and terminating the game.
Don't present last rendered frame after game terminated.
2024-12-14 17:00:51 +01:00
Flyinghead 825778445a ui: show boxart cover when starting a game 2024-12-14 16:54:18 +01:00
Flyinghead 20517cf0da vgamepad: enable touch mouse when lightgun used
Use unique touch mouse class for iOS and Android.
Enable touch mouse for arcade lightgun games or when maple device A is a
lg. Disable it otherwise.
android: only display controllers that actually send events, including
system mouse and keyboard. Use separate Touch Mouse and System Mouse.
iOS: fix missing button input for touch mouse. Don't bubble up touch
events that hit vgampad buttons or controls.
2024-12-14 16:18:35 +01:00
github-actions[bot] 989d3b1370 Fetch translations & Recreate libretro_core_options_intl.h 2024-12-13 22:21:39 +00:00
Flyinghead 421245aeba lightgun xhair: centralize logic in crosshairNeeded() 2024-12-13 17:21:32 +01:00
Flyinghead 686f08e7dd vgamepad: hide btn0 and map reload to btn1 for lightgun games 2024-12-13 16:49:57 +01:00
Flyinghead 4af1d096de audio: reset buffer on loadstate/term. Fix null audio driver on windows
Eliminate audio clicks when loading a state or starting the next game.
Fix overflow in null audio driver on windows. Detect fast forward and
reset time counter.
2024-12-13 15:58:32 +01:00
Flyinghead 27a0497dd5 win32: fix opening CD drive on command line. libretro build fix 2024-12-12 15:52:07 +01:00
Flyinghead 9ea0563a7a gdrom: stop cdda playback when reaching session lead out
Issue #1654
2024-12-12 14:27:23 +01:00
Flyinghead a46677e64e ui: add cancel button when inserting a disk
Issue #1566
2024-12-12 10:17:45 +01:00
Flyinghead a7f2008763 rend: support planar VQ textures
Fixes gens4all emu in teenage mutant ninja turtle collection.
Issue #868
2024-12-12 10:14:30 +01:00
Flyinghead 53e0f12c22 win32: force libcdio static link
Issue #1654
2024-12-12 09:57:18 +01:00
Flyinghead 34961daaec gdrom: add support for hardware CD-ROM devices
Use libcdio to read CD/DVD/BD drives (linux, windows, bsd)
Get rid of old ioctl win32 driver.
Add detected CDROM devices to game list.
Issue #1654
2024-12-11 11:25:50 +01:00
Flyinghead f5389bcd0b battle cable: notify user when data first received from peer
Document SCIF register bits.
2024-12-11 11:04:44 +01:00
Flyinghead 67eee05c14 lightgun: wiggle the reported lightgun position in a 2x2 square
Fixes claychal not registering shots in menus.
2024-12-11 10:41:53 +01:00
Flyinghead bb1b1059d4 sh4: SCIF DR bit can't be reset if rx fifo isn't empty
Fixes manic panic periodic touchscreen errors.
2024-12-11 10:37:48 +01:00
Flyinghead 94f35ca54f arcade: add input descriptors for most games 2024-12-09 16:09:45 +01:00
Flyinghead 1507097bcd naomi: set offscreen sensor when reloading. fix wldkicks PCB inputs
Most games using analog inputs for lightguns also have an offscreen
sensor (lupinsho, mok, hotd2, confmiss). Activate it when reloading or
shooting offscreen.
Fix World Kicks PCB inputs.
2024-12-09 16:02:30 +01:00
Flyinghead e4599e36ed Merge remote-tracking branch 'origin/master' into dev 2024-12-08 10:47:31 +01:00
Flyinghead 15617c1816 ssa: fix regression
due to fff03f64a3
Issue #1773
2024-12-08 10:40:44 +01:00
scribam fb64fc418e ci: update freebsd and openbsd versions 2024-12-07 21:04:17 +01:00
scribam 5656370388 deps: update sdl to version 2.30.10 2024-12-07 21:03:49 +01:00
Flyinghead 354d649ebd Merge remote-tracking branch 'origin/master' into dev 2024-12-07 20:38:55 +01:00
Johan Mattssonandflyinghead fff03f64a3 ssa: Fix wrong opcode comparison (#1769)
* ssa: Fix wrong opcode comparison

* ssa: handle 32 bits shift case first (shld, shad)

---------

Co-authored-by: flyinghead <[email protected]>
2024-12-07 18:59:14 +01:00
Flyinghead a99aa5217b sdl: don't run null haptic effects. Stop effects instead
Tentative fix for Issue #1766
2024-12-07 18:57:40 +01:00
Flyinghead 89abb7925b Pro Pinball needs copy to vram. remove Nightmare Creatures widescreen
Issue #1763
2024-12-07 10:04:36 +01:00
Flyinghead dea8e74182 better CI job names 2024-12-04 18:19:34 +01:00
Flyinghead 92c5892d4c android: fix scaling when editing vgamepad. vgamepad code clean up 2024-12-04 18:17:11 +01:00
Flyinghead 21f9e9fbc2 android,ios: arcade vgamepad layout. android vgamepad rewrite
Refactor common virtual gamepad code.
Distinct layouts for console and arcade.
Add arcade buttons 5 and 6, insert card button, service mode toggle.
System SP support
2024-12-03 21:04:51 +01:00
Flyinghead 78fa3ee09e vulkan: delay updating imgui texture when driver has just been reset
Fixes crash when selecting a custom gamepad layout .png (android)
2024-12-03 20:42:54 +01:00
Flyinghead de1584d97c systemsp: input remapping. Input desc for mushiking, dinoking, lovebery
Allow input remapping for SystemSP.
Add input descriptors for barcode games: mushiking, dinoking and
love&berry
2024-12-03 20:16:56 +01:00
Flyinghead a40c51e682 gdrom: fix deserialization crash
regression due to c860807fef
Issue #1755
2024-12-03 18:33:58 +01:00
Flyinghead c860807fef gdrom: reduce dma buffer size. reios: schedule dma reads by chunks
Reduce gdrom dma buffer size to 16 sectors from 32.
Limit serialization of dma and pio buffers to actual content.
reios: schedule transfer of dma data in chunks.
Fixes Soul Calibuf audio drop outs at boot with HLE BIOS (RA).
Issue #1755
2024-11-30 16:37:55 +01:00
Flyinghead b91f82c7e5 win32: create screenshots path if needed. Fall back to Pictures folder
Ask Windows to create the Screenshots folder if it doesn't exist.
Fall back to Pictures folder if the Screenshots folder can't be
determined.
Issue #1761
2024-11-30 13:06:53 +01:00
github-actions[bot] d38ce47081 Fetch translations & Recreate libretro_core_options_intl.h 2024-11-29 22:21:28 +00:00
Flyinghead 5f55a9fd3f naomi: fix issue when creating eeprom. Ignore audio disable.
Byte at offset 2 was incorrect.
Ignore disable attract mode audio in BootID.
Remove ikaruga default eeprom.

Issue #1760
2024-11-29 17:11:36 +01:00
Flyinghead a085fd2f6d ui: never disable Up and Down buttons on iOS. rename source files 2024-11-28 17:45:40 +01:00
Flyinghead 36c2f5137c ios, android: load custom vgamepad image. disable unused controls
User can choose a custom vgamepad image or reset to default.
Unused controls are disabled and hidden based on the game (arcade) or
maple device (console).
2024-11-28 17:09:09 +01:00
Flyinghead 5e9c168fc9 android: add BooleanArray to jni util. refactor using templates 2024-11-28 17:01:39 +01:00
Flyinghead 59c2343cad android: add mime-type argument to addStorage() 2024-11-28 16:46:17 +01:00
Flyinghead b42387e929 naomi: vf4 and mazan input descriptors 2024-11-28 16:41:30 +01:00
Flyinghead df78a781ec allow USE_HOST_LIBCHDR with libretro 2024-11-27 10:04:23 +01:00
Flyinghead 974dc2e233 missing iOS app icon source files
Courtesy of @maxtron95
Issue #1731
2024-11-27 10:02:26 +01:00
Flyinghead 6bf38e5085 input: fix potential race condition with analog ramp up/down
Fix a potential race condition on android causing missed inputs with
analog axes bound to buttons.
2024-11-26 19:29:40 +01:00
Flyinghead 792aa38d34 input: implement ramp up/down for analog axes mapped to buttons
Full analog axes now have a ramp up/down of 100 ms when the mapped
button is pressed/released. Works also for keyboards.
Issue #1017
2024-11-26 16:06:13 +01:00
Flyinghead 516982c66c ios: allow virtual gamepad layout customization
Use same display code as android.
Support custom buttons.png.
Allow layout customization as in android.
Save layout in emu.cfg.
Allow transparency setting.
Issue #990
2024-11-26 10:46:02 +01:00
Flyinghead a9fa33c17a gl: test glBlitFramebuffer compliance and disable its use if failing 2024-11-26 10:03:33 +01:00
Flyinghead d0584b8652 vk: defer deleting framebuffers in ScreenDrawer and lr context
Defer deleting framebuffers and attachments in ScreenDrawer and lr
context when resizing.
Fixes crash when starting 240pSuite on the lr core.
2024-11-23 13:47:19 +01:00
github-actions[bot] 58c8a33d11 Fetch translations & Recreate libretro_core_options_intl.h 2024-11-22 22:20:51 +00:00
Flyinghead b4dcc64ef6 android: draw onscreen gamepad with imgui
Get rid of Renderer::DrawOSD.
gl: make a GlQuadDrawer class.
Draw on-screen gamepad using imgui.
Clean up vgamepad interface.
2024-11-22 14:42:51 +01:00
Stefan Schlosser 7531f5df20 oss: cleanup and unification
Audio and record initializations are unified by providing helper functions for
opening the audio device and for setting the device parameters. The ioctl calls
are properly checked for errors and log an appropriate error message. The init
fails if the requested parameters are not supported by the device, i.e. 44.1kHz
16bit stereo for playback. This provides consistent behavior with all the other
audio backends.
2024-11-22 13:06:18 +01:00
Stefan Schlosser c4d77165a6 lua: add build option for Lua support
The added build option USE_LUA (enabled by default) allows to enable/disable Lua
support in the build. This provides more control over the used dependencies, and
is important especially for OpenBSD and FreeBSD i386 builds, where the build
fails with enabled Lua support, see also [1].

[1] https://github.com/vinniefalco/LuaBridge/issues/302
2024-11-20 18:01:06 +01:00
Flyinghead aa95c71ead libretro build fix 2024-11-20 12:06:39 +01:00
Flyinghead 6612e3fef8 gdrom: update settings and more when swapping disks
Set forced and per game settings.
Update game id and save state file name.
Reset cheats and custom textures.
Update game title and window title.
Issue #1748

hle bios: reboot the game when "exit to bios" vector is called
2024-11-20 11:46:34 +01:00
Flyinghead fbd52a72d4 naomi: load and decrypt gd-rom on demand
Shorten load times and avoid ANR on android with libretro when loading a
GD-ROM game. Load and decrypt data by 16 KB chunk when needed.
Issue #1457
Issue #1738
2024-11-17 20:39:30 +01:00
Flyinghead d618abc320 rend: modifier volumes must be clipped
Modifier volumes should also be clipped when needed.
Implement outside clipping for non-OIT renderers.
OIT renderers are less affected since the shadowed polys themselves are
usually also clipped after shadow is applied.
Fixes overflowing shadows in baserunner cams in WSB 2K1.
2024-11-17 12:09:40 +01:00
github-actions[bot] b7163dda83 Fetch translations & Recreate libretro_core_options_intl.h 2024-11-15 22:21:36 +00:00
Stefan Schlosser d259a2e2f5 libchdr: add build option for using system library
The added build option USE_HOST_LIBCHDR allows to build with libchdr provided by
the system. The option is disabled by default.
2024-11-14 13:17:00 +01:00
Stefan Schlosser e2f8de33e8 audio: add build options for audio backend support
The added build options USE_ALSA, USE_LIBAO, USE_OSS and USE_PULSEAUDIO allow to
enable/disable specific audio backends in the build. This provides more control
over the used dependencies.

The OSS backend wasn't enabled in the previous build configuration, consequently
the build option USE_OSS is set to OFF by default.
2024-11-14 13:16:27 +01:00
vkedwardli 42ad5532b2 Update Xcode project generator (#1732)
* Do not build breakpad in Xcode project, fix `PhaseScriptExecution failed` error and fix Flycast cannot be launched from debugger

* NSLog prettyprint for Xcode 16
2024-11-14 13:15:54 +01:00
Flyinghead 1e30f483c2 New iOS app icon
Courtesy of @maxtron95
Issue #1731
2024-11-14 13:13:59 +01:00
Flyinghead bdd2d5af7f naomi: emulate dimm registers for gd cart
Emulate 1.02 dimm board without network so that the bios detects it and
shows the expected GD-ROM SYSTEM boot screen.
Fix rom board serial id issue (error 04) with ikaruga, tetkiwam,
puyofev[j] and sprtjam.
Issue #1735

Add naomigd and naomidev bios definitions (unused).
Use netdimm for mj1 and wccf* series (still not working).
2024-11-14 13:05:48 +01:00
Flyinghead 76638df001 sh4: split xffr array. Fix FPCB_PAD on windows
Split xffr[32] into xf[16] and fr[16].
Set FPCB_PAD size to 64_KB. Issue #1736
Get rid of shil_param::reg_aofs
2024-11-13 10:31:45 +01:00
Flyinghead 3efb87ba6d vk: fix crash when enabling/disabling EmulateFramebuffer
Make sure to end the current render pass with the previous setting.
Fix initial layout of color attachments in OIT.
Fix missing initial transition after Term/Init in !OIT.
Issue #1734
2024-11-11 22:15:57 +01:00
Flyinghead c9f238362d New macOS app icon
courtesy of @maxtron95
Issue #1731
2024-11-11 22:09:52 +01:00
Flyinghead 129673a84b dynarec: use sh4 ctx to get register pointers. prefer offset
Move restoreHostRoundingMode() into Sh4Context
2024-11-09 18:44:51 +01:00
Flyinghead 7c1c5817d3 sh4: move sqWrite func into sh4 context. Simplify usage
Allow passing sh4 context to dynarec canonical implementations
Reduce FPCB padding to PAGE_SIZE except on arm32
2024-11-09 16:54:47 +01:00
Flyinghead e394840344 sh4: pass sh4 context to dynarecs, sh4cycles and caches
Reorder RuntimeBlockInfo members to save space
2024-11-09 13:25:55 +01:00
Flyinghead fc33002284 network: add Golf Shiyouyo 2 ports 2024-11-09 11:44:58 +01:00
Flyinghead dff967f60a ui: notify user when loading a state fails and why 2024-11-09 11:43:10 +01:00
github-actions[bot] a3708a7e95 Fetch translations & Recreate libretro_core_options_intl.h 2024-11-08 22:20:44 +00:00
Flyinghead 1cadeb276e sh4: test fix 2024-11-08 22:01:48 +01:00
Flyinghead 2b28e819e5 sh4: build fix. rounding mode refactoring 2024-11-08 21:34:24 +01:00
Flyinghead db846ca933 sh4: pass context to interpreter funcs. get rid of sh4 regs #define 2024-11-08 18:36:08 +01:00
Flyinghead 9cb7d9e5d5 rec-arm64: fix Store Queue write handlers 2024-11-08 18:27:52 +01:00
Flyinghead 93ae9d0375 sh4: refactor interpreter and recompiler with Sh4Executor interface 2024-11-07 18:14:18 +01:00
Flyinghead e03e11b8a9 arm64: mask value operand when calling a write mem handler
AppleClang doesn't mask the operand in the callee. This must be done in
the dynarec.
See 6115a918b2
Tentative fix for barcode reader not working on macOS/arm
2024-11-07 14:10:02 +01:00
Flyinghead 52df0133f0 sh4: pass sh4 context to sq write handlers 2024-11-06 23:27:20 +01:00
Flyinghead d5aeb482d9 sh4: move sq_buffer into sh4 context 2024-11-06 22:11:27 +01:00
Flyinghead 7eec648690 sh4: move Sh4RegType & GetRegPtr into sh4/dyna
rec-arm: use getRegOffset/reg_nofs instead of GetRegPtr
2024-11-06 20:46:32 +01:00
Flyinghead 897e06b887 sh4: refactor GetDR/SetDR and access to fp registers 2024-11-06 19:26:04 +01:00
Flyinghead aa38771cd0 refactor sh4_sr_GetFull/sh4_sr_SetFull 2024-11-06 18:01:01 +01:00
Flyinghead c77ddba109 get rid of settings.dynarec.disable_nvmem 2024-11-06 17:03:01 +01:00
Flyinghead cd8a88366d get rid of TARGET_NO_EXCEPTIONS 2024-11-06 16:59:16 +01:00
Bobby Smith f892630e4b [Libretro] Add game dir as fall back for arcade BIOSes 2024-11-06 16:46:29 +01:00
Flyinghead 9d4d486524 naomi: add crc32 to GDROM and AW roms 2024-11-06 16:45:42 +01:00
Flyinghead d8a3ee9c3a android: disable openmp affinity on some devices
Disable omp affinity on POCO F5, Redmi Note 12 Turbo, Redmi Note 13 Pro
5G and POCO X6 5G to avoid crashes.
2024-11-06 16:40:11 +01:00
Flyinghead fc6142b332 maple: do a full save of the vmu after loading a state
A partial save might corrupt the vmu file system.
2024-11-04 19:19:31 +01:00
Flyinghead 5fc84acdfb maple: base vmu file name on game ID for multidisk games
Build vmu file name with game ID so that all disks of multidisk games
share the same vmu (A1 only, or all with libretro when enabled).
Rename existing vmu file to new format if none exists.
Issue #1556
2024-11-03 17:14:31 +01:00
Flyinghead 037dc3b4b9 atomiswave: set dma speed to 7 MB/s for all aw games and conversions
AW dma transfer rate seems to be lower than naomi.
Fixes Force Five freeze when starting a game.
Fixes opening sequence animation being truncated in kofxi.
Issue #1724
2024-11-03 16:55:15 +01:00
Flyinghead 96aac66133 naomi: improve performance of M1 and M4 carts dma
Limit buffer size to 1 or 2 KB instead of 32 KB.
Only decrypt right before dma transfer.
Fixes frame drops and audio underruns in vf4evoct attract mode.
Issue #1717
2024-11-02 18:46:49 +01:00
Flyinghead 4d5ced1ce6 maple: add Full Controller with 2 analog sticks and all buttons
add AllStatusReq string for purupuru device.

Issue #1171
2024-11-02 18:13:38 +01:00
scribam da3cfd9d58 deps: update sdl to version 2.30.9 2024-11-02 11:05:35 +01:00
Flyinghead 65aadb9dcb pvr: avoid infinite loop if tile array is invalid
Fixes 240pSuite freeze at boot with the libretro core.
2024-11-02 11:04:56 +01:00
github-actions[bot] cec46990ea Fetch translations & Recreate libretro_core_options_intl.h 2024-11-01 22:21:16 +00:00
Flyinghead 2e5309bfa3 aica: don't batch samples
Batching 32 samples and arm runs causes audio glitches with some wince
games. So run aica 44100 times/sec instead.
Issue #1240
Issue #1605
2024-11-01 14:31:59 +01:00
Flyinghead 742090bb1f rcheevos: disable achievements in naomi slaves 2024-10-29 17:38:52 +01:00
Flyinghead f3a64e5891 android: clean exit when using the exit hotkey
Finish the activity in dc_exit.
Issue #1713
2024-10-29 17:35:25 +01:00
Flyinghead 32ccb754ff libretro: fix translations
Issue #1688
2024-10-29 15:39:17 +01:00
Alexandre Bouvier 32d11dd89c fix missing include 2024-10-26 19:28:20 +02:00
github-actions[bot] 760813e45a Fetch translations & Recreate libretro_core_options_intl.h 2024-10-25 22:20:46 +00:00
Flyinghead e221f6fed2 deadlock when pausing/resuming quickly (android)
Race condition when pausing/resuming is done from a separate thread (android): the renderer gets a result ready status for the previous emu thread, but waits on the newly created one forever.
2024-10-25 08:10:41 +02:00
814 changed files with 181816 additions and 61730 deletions

No files matched your search

+18 -17
View File
@@ -1,29 +1,34 @@
name: Android CI
name: Android
on: [push, pull_request]
on:
push:
branches-ignore:
- 'l10n_**'
pull_request:
jobs:
build:
name: Android
runs-on: ubuntu-latest
steps:
- name: Set up build environment
run: |
sudo apt-get update
sudo apt-get -y install ccache libcurl4-openssl-dev ninja-build
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 }}
@@ -46,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
@@ -69,20 +74,16 @@ jobs:
mv libflycast.so.sym symbols/libflycast.so/$BUILD_ID
done
- name: Configure AWS Credentials
id: aws-credentials
uses: aws-actions/configure-aws-credentials@v4
- name: Setup Rclone
uses: AnimMouse/setup-rclone@v1
with:
aws-access-key-id: AKIAJOZQS4H2PHQWYFCA
aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
aws-region: us-east-2
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push'
rclone_config: ${{ secrets.RCLONE_CONFIG }}
- name: Upload to S3
run: |
cp shell/android-studio/flycast/build/outputs/bundle/release/*.aab shell/android-studio/flycast/build/outputs/apk/release/
aws s3 sync shell/android-studio/flycast/build/outputs/apk/release s3://flycast-builds/android/${GITHUB_REF#refs/}-$GITHUB_SHA --acl public-read --exclude='*.json' --follow-symlinks
if: ${{ steps.aws-credentials.outputs.aws-account-id != '' }}
rclone copy shell/android-studio/flycast/build/outputs/apk/release scaleway:flycast-builds/android/${GITHUB_REF#refs/}-$GITHUB_SHA --exclude '*.json'
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push'
- name: Setup Sentry CLI
uses: mathieu-bour/setup-sentry-cli@v2
@@ -105,4 +106,4 @@ jobs:
shell: bash
env:
SENTRY_TOKEN: ${{ secrets.SENTRY_TOKEN }}
if: ${{ env.SENTRY_TOKEN != '' }}
if: ${{ env.SENTRY_TOKEN != '' && github.event_name == 'push' }}
+18 -13
View File
@@ -1,6 +1,10 @@
name: BSD CI
name: BSD
on: [push, pull_request]
on:
push:
branches-ignore:
- 'l10n_**'
pull_request:
jobs:
build:
@@ -11,32 +15,33 @@ jobs:
matrix:
operating_system: [ freebsd, netbsd, openbsd ]
architecture: [ arm64, x86-64 ]
libretro: [ OFF, ON ]
include:
- operating_system: freebsd
version: '14.1'
pkginstall: sudo pkg install -y alsa-lib ccache cmake evdev-proto git libao libevdev libudev-devd libzip lua54 miniupnpc ninja pkgconf pulseaudio sdl2
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.0'
pkginstall: sudo pkgin update && sudo pkgin -y install alsa-lib ccache cmake gcc12 git libao libzip lua54 miniupnpc ninja-build pkgconf pulseaudio SDL2 && export PATH=/usr/pkg/gcc12/bin:$PATH
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.5'
pkginstall: sudo pkg_add ccache cmake git libao libzip miniupnpc ninja pkgconf pulseaudio sdl2
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.25.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 -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
+30 -31
View File
@@ -1,6 +1,10 @@
name: C/C++ CI
name: Linux, macOS, Windows
on: [push, pull_request]
on:
push:
branches-ignore:
- 'l10n_**'
pull_request:
jobs:
build:
@@ -19,12 +23,11 @@ jobs:
config:
- {name: i686-pc-windows-msvc, os: windows-latest, shell: cmd, arch: x86, cmakeArgs: -G Ninja, buildType: Release}
- {name: apple-darwin, os: macos-latest, shell: sh, cmakeArgs: -G Xcode -DUSE_DISCORD=ON, destDir: osx, buildType: RelWithDebInfo}
- {name: apple-ios, os: macos-latest, shell: sh, cmakeArgs: -DCMAKE_SYSTEM_NAME=iOS -G Xcode, destDir: ios, buildType: Release}
- {name: x86_64-pc-linux-gnu, os: ubuntu-20.04, shell: sh, cmakeArgs: -G Ninja -DUSE_DISCORD=ON, destDir: linux, buildType: RelWithDebInfo}
- {name: x86_64-pc-linux-gnu, os: ubuntu-22.04, shell: sh, cmakeArgs: -G Ninja -DUSE_DISCORD=ON -DUSE_LIBCDIO=ON, destDir: linux, buildType: RelWithDebInfo}
- {name: x86_64-pc-windows-msvc, os: windows-latest, shell: cmd, arch: x64, cmakeArgs: -G Ninja -DUSE_DISCORD=ON, buildType: Release}
- {name: x86_64-w64-mingw32, os: windows-latest, shell: 'msys2 {0}', cmakeArgs: -G Ninja -DUSE_DISCORD=ON, destDir: win, buildType: RelWithDebInfo}
- {name: libretro-x86_64-pc-linux-gnu, os: ubuntu-latest, shell: sh, cmakeArgs: -DLIBRETRO=ON -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE -G Ninja, buildType: Release}
- {name: libretro-x86_64-w64-mingw32, os: windows-latest, shell: 'msys2 {0}', cmakeArgs: -DLIBRETRO=ON -G Ninja, buildType: Release}
- {name: x86_64-w64-mingw32, os: windows-latest, shell: 'msys2 {0}', cmakeArgs: -G Ninja -DUSE_DISCORD=ON -DUSE_LIBCDIO=ON, destDir: win, buildType: RelWithDebInfo}
- {name: libretro-x86_64-pc-linux-gnu, os: ubuntu-22.04, shell: sh, cmakeArgs: -DLIBRETRO=ON -DUSE_LIBCDIO=ON -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE -G Ninja, buildType: Release}
- {name: libretro-x86_64-w64-mingw32, os: windows-latest, shell: 'msys2 {0}', cmakeArgs: -DLIBRETRO=ON -DUSE_LIBCDIO=ON -G Ninja, buildType: Release}
steps:
- name: Set up build environment (macOS)
@@ -32,9 +35,9 @@ jobs:
# Unlink and re-link to prevent errors when github mac runner images install python outside of brew. See https://github.com/actions/setup-python/issues/577
brew list -1 | grep python | while read formula; do brew unlink $formula; brew link --overwrite $formula; done
brew update || :
brew install libao ldid ninja pulseaudio
brew install libao ldid pulseaudio
brew uninstall --ignore-dependencies zstd
VULKAN_VER=1.3.261.1 && aria2c https://sdk.lunarg.com/sdk/download/$VULKAN_VER/mac/vulkansdk-macos-$VULKAN_VER.dmg
VULKAN_VER=1.3.290.0 && aria2c https://sdk.lunarg.com/sdk/download/$VULKAN_VER/mac/vulkansdk-macos-$VULKAN_VER.dmg
hdiutil attach ./vulkansdk-macos-*.dmg -mountpoint /Volumes/VulkanSDK
sudo /Volumes/VulkanSDK/InstallVulkan.app/Contents/MacOS/InstallVulkan --root $HOME/VulkanSDK --accept-licenses --default-answer --confirm-command install
hdiutil detach /Volumes/VulkanSDK
@@ -45,15 +48,17 @@ 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
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)
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
install: git make mingw-w64-x86_64-ccache mingw-w64-x86_64-cmake mingw-w64-x86_64-lua mingw-w64-x86_64-ninja mingw-w64-x86_64-SDL2 mingw-w64-x86_64-toolchain
install: git make mingw-w64-x86_64-ccache mingw-w64-x86_64-cmake mingw-w64-x86_64-lua mingw-w64-x86_64-ninja mingw-w64-x86_64-SDL2 mingw-w64-x86_64-toolchain mingw-w64-x86_64-libcdio
if: matrix.config.shell == 'msys2 {0}'
- name: Set up build environment (Windows, Visual Studio)
@@ -67,20 +72,16 @@ jobs:
arch: ${{ matrix.config.arch }}
if: matrix.config.shell == 'cmd'
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: recursive
- name: Compile a universal OpenMP (macOS)
run: HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew reinstall --build-from-source --formula ./shell/apple/libomp.rb
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'
- name: Compile OpenMP.xcframework (iOS)
run: ./shell/apple/emulator-ios/OpenMP/build_ios_openmp.sh --disableSimulator
if: matrix.config.name == 'apple-ios'
- uses: actions/cache@v4
- uses: actions/cache@v5
with:
path: ${{ env.CCACHE_DIR }}
key: ccache-${{ matrix.config.name }}-${{ github.sha }}
@@ -126,10 +127,12 @@ 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: artifact/bin
path: |
artifact/bin
artifact/lib/libretro
- name: Package app (macos)
run: |
@@ -143,19 +146,15 @@ jobs:
rm artifact/bin/flycast.exe
if: matrix.config.name == 'x86_64-w64-mingw32'
- name: Configure AWS Credentials
id: aws-credentials
uses: aws-actions/configure-aws-credentials@v4
- name: Setup Rclone
uses: AnimMouse/setup-rclone@v1
with:
aws-access-key-id: AKIAJOZQS4H2PHQWYFCA
aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
aws-region: us-east-2
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push' && matrix.config.destDir != ''
rclone_config: ${{ secrets.RCLONE_CONFIG }}
- name: Upload to S3
run: aws s3 sync artifact/bin s3://flycast-builds/${{ matrix.config.destDir }}/${GITHUB_REF#refs/}-$GITHUB_SHA --acl public-read --follow-symlinks
run: rclone copy artifact/bin scaleway:flycast-builds/${{ matrix.config.destDir }}/${GITHUB_REF#refs/}-$GITHUB_SHA
shell: bash
if: ${{ steps.aws-credentials.outputs.aws-account-id != '' }}
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push' && matrix.config.destDir != ''
- name: Setup Sentry CLI
uses: mathieu-bour/setup-sentry-cli@v2
@@ -178,5 +177,5 @@ jobs:
shell: bash
env:
SENTRY_TOKEN: ${{ secrets.SENTRY_TOKEN }}
if: ${{ env.SENTRY_TOKEN != '' && (matrix.config.name == 'x86_64-w64-mingw32' || matrix.config.name == 'apple-darwin' || matrix.config.name == 'x86_64-pc-linux-gnu') }}
if: ${{ env.SENTRY_TOKEN != '' && (matrix.config.name == 'x86_64-w64-mingw32' || matrix.config.name == 'apple-darwin' || matrix.config.name == 'x86_64-pc-linux-gnu') && github.event_name == 'push' }}
+3 -3
View File
@@ -15,18 +15,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Setup Java JDK
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: 18
distribution: zulu
- name: Setup Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.10'
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Upload Source
shell: bash
+4 -4
View File
@@ -12,18 +12,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Setup Java JDK
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: 18
distribution: zulu
- name: Setup Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.10'
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal access token.
fetch-depth: 0 # otherwise, there would be errors pushing refs to the destination repository.
@@ -43,7 +43,7 @@ jobs:
git commit -m "Fetch translations & Recreate libretro_core_options_intl.h"
- name: GitHub Push
uses: ad-m/github-push-action@v0.8.0
uses: ad-m/github-push-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ github.ref }}
+21 -18
View File
@@ -1,10 +1,14 @@
name: Nintendo Switch CI
name: Nintendo Switch
on: [push, pull_request]
on:
push:
branches-ignore:
- 'l10n_**'
pull_request:
jobs:
build:
name: ${{ matrix.config.name }}
name: Switch ${{ matrix.config.name }}
runs-on: ubuntu-latest
container: devkitpro/devkita64:latest
@@ -18,17 +22,17 @@ jobs:
- name: Set up build environment
run: |
sudo apt-get update
sudo apt-get -y install awscli ccache
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,24 +43,23 @@ 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: artifact/bin
path: |
artifact/bin
artifact/lib/libretro
- name: Package app
run: zip -m artifact/bin/flycast.nro.zip artifact/bin/flycast.nro
if: hashFiles('artifact/bin/flycast.nro') != ''
- name: Configure AWS Credentials
id: aws-credentials
uses: aws-actions/configure-aws-credentials@v4
- name: Setup Rclone
uses: AnimMouse/setup-rclone@v1
with:
aws-access-key-id: AKIAJOZQS4H2PHQWYFCA
aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
aws-region: us-east-2
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push' && matrix.config.destDir != ''
rclone_config: ${{ secrets.RCLONE_CONFIG }}
version: v1.69.1
- name: Upload to S3
run: aws s3 sync artifact/bin s3://flycast-builds/${{ matrix.config.destDir }}/${GITHUB_REF#refs/}-$GITHUB_SHA --acl public-read --follow-symlinks
if: ${{ steps.aws-credentials.outputs.aws-account-id != '' }}
run: rclone copy artifact/bin scaleway:flycast-builds/${{ matrix.config.destDir }}/${GITHUB_REF#refs/}-$GITHUB_SHA
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push' && matrix.config.destDir != ''
+22 -17
View File
@@ -1,20 +1,29 @@
name: Windows UWP CI
name: Xbox, UWP
on: [push, pull_request]
on:
push:
branches-ignore:
- 'l10n_**'
pull_request:
jobs:
build:
name: UWP
runs-on: windows-latest
strategy:
matrix:
arch: [ Win32, x64, ARM64 ]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: true
submodules: recursive
- name: CMake
run: |
cmake -B build -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 17 2022" -A x64 -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0.22000.0
cmake -B build -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 17 2022" -A ${{ matrix.arch }} -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0.22000.0
cmake --build build --config Release -- /m
shell: cmd
@@ -23,7 +32,7 @@ jobs:
- name: Package app
run: |
mkdir build\artifact
cd build\AppPackages\flycast\flycast_*_x64_Test
cd build\AppPackages\flycast\flycast_*_Test
mkdir tmp
ren *.msix flycast.msix
makeappx.exe unpack /p .\flycast.msix /d tmp
@@ -34,21 +43,17 @@ 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
name: flycast-uwp-${{ matrix.arch }}
path: build/artifact
- name: Configure AWS Credentials
id: aws-credentials
uses: aws-actions/configure-aws-credentials@v4
- name: Setup Rclone
uses: AnimMouse/setup-rclone@v1
with:
aws-access-key-id: AKIAJOZQS4H2PHQWYFCA
aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
aws-region: us-east-2
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push'
rclone_config: ${{ secrets.RCLONE_CONFIG }}
- name: Upload to S3
run: aws s3 sync build/artifact s3://flycast-builds/xbox/${GITHUB_REF#refs/}-$GITHUB_SHA --acl public-read --follow-symlinks
run: rclone copy build/artifact scaleway:flycast-builds/xbox/${GITHUB_REF#refs/}-$GITHUB_SHA
shell: bash
if: ${{ steps.aws-credentials.outputs.aws-account-id != '' }}
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push' && matrix.arch == 'x64'
+3
View File
@@ -17,3 +17,6 @@ cmake-build-*/
# Dependencies from .gitmodules
core/deps/breakpad/
core/deps/glslang/
.cache/
compile_commands.json
CMakeUserPresets.json
+19 -7
View File
@@ -9,39 +9,46 @@
variables:
GIT_SUBMODULE_STRATEGY: recursive
CORENAME: flycast
CORE_ARGS: -DLIBRETRO=ON -DCMAKE_BUILD_TYPE=Release
CORE_ARGS: -DLIBRETRO=ON -DUSE_LIBCDIO=ON
.core-defs-linux:
extends: .core-defs
variables:
CORE_ARGS: -DLIBRETRO=ON -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE -DCMAKE_BUILD_TYPE=Release
CORE_ARGS: -DLIBRETRO=ON -DUSE_LIBCDIO=ON -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE
CC: /usr/bin/gcc-12
CXX: /usr/bin/g++-12
.core-defs-osx-x64:
extends: .core-defs
variables:
CORE_ARGS: -DLIBRETRO=ON -G Xcode -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=x86_64
CORE_ARGS: -DLIBRETRO=ON -G Xcode -DCMAKE_OSX_ARCHITECTURES=x86_64
EXTRA_PATH: Release
.core-defs-osx-arm64:
extends: .core-defs
variables:
CORE_ARGS: -DLIBRETRO=ON -G Xcode -DCMAKE_BUILD_TYPE=Release
CORE_ARGS: -DLIBRETRO=ON -G Xcode
EXTRA_PATH: Release
.core-defs-ios-arm64:
extends: .core-defs
variables:
CORE_ARGS: -DLIBRETRO=ON -DCMAKE_BUILD_TYPE=Release
CORE_ARGS: -DLIBRETRO=ON -DUSE_OPENMP=OFF -DCMAKE_OSX_DEPLOYMENT_TARGET=13.0
IOS_MINVER: 13.0
MINVER: 13.0
.core-defs-android:
extends: .core-defs
script:
- cmake $CORE_ARGS -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:
@@ -108,18 +115,22 @@ libretro-build-linux-x64:
extends:
- .libretro-linux-cmake-x86_64
- .core-defs-linux
image: $CI_SERVER_HOST:5050/libretro-infrastructure/libretro-build-amd64-ubuntu:backports
# Linux 32-bit
libretro-build-linux-i686:
extends:
- .libretro-linux-cmake-x86
- .core-defs-linux
image: $CI_SERVER_HOST:5050/libretro-infrastructure/libretro-build-i386-ubuntu:backports
# MacOS 64-bit
libretro-build-osx-x64:
extends:
- .libretro-osx-cmake-x86
- .core-defs-osx-x64
tags:
- mac-apple-silicon
# MacOS arm 64-bit
libretro-build-osx-arm64:
@@ -169,3 +180,4 @@ libretro-build-tvos-arm64:
extends:
- .libretro-tvos-cmake-arm64
- .core-defs-ios-arm64
+19 -1
View File
@@ -3,7 +3,7 @@
url = https://github.com/libsdl-org/SDL.git
[submodule "core/deps/libchdr"]
path = core/deps/libchdr
url = https://github.com/rtissera/libchdr.git
url = https://github.com/flyinghead/libchdr.git
ignore = dirty
[submodule "core/deps/luabridge"]
path = core/deps/luabridge
@@ -41,3 +41,21 @@
[submodule "core/deps/googletest"]
path = core/deps/googletest
url = https://github.com/google/googletest.git
[submodule "core/deps/asio"]
path = core/deps/asio
url = https://github.com/flyinghead/asio.git
[submodule "core/deps/xbyak"]
path = core/deps/xbyak
url = https://github.com/herumi/xbyak.git
[submodule "core/deps/libjuice"]
path = core/deps/libjuice
url = https://github.com/paullouisageneau/libjuice.git
[submodule "core/deps/websocketpp"]
path = core/deps/websocketpp
url = https://github.com/zaphoyd/websocketpp.git
[submodule "core/deps/DreamPicoPort-API"]
path = core/deps/DreamPicoPort-API
url = https://github.com/OrangeFox86/DreamPicoPort-API.git
[submodule "core/deps/tinygettext"]
path = core/deps/tinygettext
url = https://github.com/flyinghead/tinygettext
+244 -502
View File
File diff suppressed because it is too large. Load diff
+23
View File
@@ -0,0 +1,23 @@
{
"version": 2,
"configurePresets": [
{
"name": "clang",
"generator": "Ninja",
"binaryDir": "build-clang",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}
},
{
"name": "gcc",
"generator": "Ninja",
"binaryDir": "build-gcc",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}
}
]
}
+5 -1
View File
@@ -6,7 +6,7 @@
[![Windows UWP CI](https://github.com/flyinghead/flycast/actions/workflows/uwp.yml/badge.svg)](https://github.com/flyinghead/flycast/actions/workflows/uwp.yml)
[![BSD CI](https://github.com/flyinghead/flycast/actions/workflows/bsd.yml/badge.svg)](https://github.com/flyinghead/flycast/actions/workflows/bsd.yml)
![flycast logo](https://github.com/flyinghead/flycast/raw/master/shell/linux/flycast.png)
<img src="shell/linux/flycast.png" alt="flycast logo" width="150"/>
**Flycast** is a multi-platform Sega Dreamcast, Naomi, Naomi 2, and Atomiswave emulator derived from [**reicast**](https://github.com/skmp/reicast-emulator).
@@ -38,6 +38,10 @@ Install Flycast from [**Google Play**](https://play.google.com/store/apps/detail
`brew install --cask flycast`
### iOS
Due to persistent harassment from an iOS user, support for this platform has been dropped.
### Xbox One/Series ![xbox logo](https://flyinghead.github.io/flycast-builds/xbox.png)
Grab the latest build from [**the builds page**](https://flyinghead.github.io/flycast-builds/), or the [**GitHub Actions**](https://github.com/flyinghead/flycast/actions/workflows/uwp.yml). Then install it using the **Xbox Device Portal**.
+3
View File
@@ -0,0 +1,3 @@
target_sources(${PROJECT_NAME} PRIVATE
achievements.cpp
achievements.h)
+51 -78
View File
@@ -29,6 +29,7 @@
#include "oslib/oslib.h"
#include "emulator.h"
#include "stdclass.h"
#include "oslib/i18n.h"
#include <cassert>
#include <rc_client.h>
#include <rc_hash.h>
@@ -37,8 +38,9 @@
#include <atomic>
#include <utility>
#include <xxhash.h>
#include <thread>
#include <functional>
#include "util/worker_thread.h"
#include "util/periodic_thread.h"
namespace achievements
{
@@ -74,10 +76,8 @@ private:
std::string getOrDownloadImage(const char *url);
std::pair<std::string, bool> getCachedImage(const char *url);
void diskChange();
void asyncTask(std::function<void()> f);
void startThread();
void stopThread();
void backgroundThread();
void asyncTask(std::function<void()>&& f);
void stopThreads();
static void clientLoginWithTokenCallback(int result, const char *error_message, rc_client_t *client, void *userdata);
static void clientLoginWithPasswordCallback(int result, const char *error_message, rc_client_t *client, void *userdata);
@@ -112,11 +112,12 @@ private:
std::string cachePath;
std::unordered_map<u64, std::string> cacheMap;
std::mutex cacheMutex;
std::vector<std::function<void()>> tasks;
std::mutex taskMutex;
std::thread taskThread;
cResetEvent resetEvent;
bool threadRunning = false;
WorkerThread taskThread {"RA-background"};
PeriodicThread idleThread { "RA-idle", [this]() {
if (active)
rc_client_idle(rc_client);
}};
};
bool init() {
@@ -176,6 +177,7 @@ Achievements::Achievements()
EventManager::listen(Event::Pause, emuEventCallback, this);
EventManager::listen(Event::Resume, emuEventCallback, this);
EventManager::listen(Event::DiskChange, emuEventCallback, this);
idleThread.setPeriod(1000);
}
Achievements::~Achievements()
@@ -188,44 +190,13 @@ Achievements::~Achievements()
term();
}
void Achievements::asyncTask(std::function<void()> f)
{
{
std::lock_guard<std::mutex> _(taskMutex);
tasks.emplace_back(f);
}
resetEvent.Set();
void Achievements::asyncTask(std::function<void()>&& f) {
taskThread.run(std::move(f));
}
void Achievements::startThread()
{
threadRunning = true;
taskThread = std::thread(&Achievements::backgroundThread, this);
}
void Achievements::stopThread()
{
threadRunning = false;
resetEvent.Set();
if (taskThread.joinable())
taskThread.join();
}
void Achievements::backgroundThread()
{
ThreadName _("RA-background");
while (threadRunning)
{
if (!resetEvent.Wait(1000) && active && paused)
rc_client_idle(rc_client);
std::vector<std::function<void()>> localTasks;
{
std::lock_guard<std::mutex> _(taskMutex);
std::swap(tasks, localTasks);
}
for (auto& f : localTasks)
f();
}
void Achievements::stopThreads() {
taskThread.stop();
idleThread.stop();
}
bool Achievements::init()
@@ -243,7 +214,6 @@ bool Achievements::init()
//rc_client_set_unofficial_enabled(rc_client, 0);
//rc_client_set_spectator_mode_enabled(rc_client, 0);
loadCache();
startThread();
if (!config::AchievementsUserName.get().empty() && !config::AchievementsToken.get().empty())
{
@@ -301,7 +271,7 @@ void Achievements::loadCache()
}
static u64 hashUrl(const char *url) {
return XXH64(url, strlen(url), 13);
return XXH3_64bits(url, strlen(url));
}
std::pair<std::string, bool> Achievements::getCachedImage(const char *url)
@@ -314,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);
}
@@ -334,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");
@@ -357,7 +329,7 @@ void Achievements::term()
if (rc_client == nullptr)
return;
unloadGame();
stopThread();
stopThreads();
rc_client_destroy(rc_client);
rc_client = nullptr;
}
@@ -369,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);
});
}
@@ -387,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));
@@ -422,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;
}
@@ -574,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:
@@ -608,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);
});
}
@@ -648,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)
@@ -687,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)
@@ -779,8 +750,7 @@ std::string Achievements::getGameHash()
{
if (settings.platform.isConsole())
{
const u32 diskType = libGDR_GetDiscType();
if (diskType == NoDisk || diskType == Open)
if (!gdr::isLoaded())
return {};
// Reopen the disk locally to avoid threading issues (CHD)
try {
@@ -814,15 +784,19 @@ std::string Achievements::getGameHash()
return hash;
}
void Achievements::pauseGame() {
void Achievements::pauseGame()
{
paused = true;
if (active)
idleThread.start();
}
void Achievements::resumeGame()
{
paused = false;
if (config::EnableAchievements)
if (config::EnableAchievements && !settings.naomi.slave)
{
idleThread.stop();
loadGame();
if (settings.raHardcoreMode && !config::AchievementsHardcoreMode)
{
@@ -935,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);
});
}
@@ -956,15 +929,15 @@ void Achievements::unloadGame()
paused = false;
EventManager::unlisten(Event::VBlank, emuEventCallback, this);
// wait for all async tasks before unloading the game
stopThread();
startThread();
stopThreads();
rc_client_unload_game(rc_client);
settings.raHardcoreMode = false;
}
void Achievements::diskChange()
{
if (!active)
if (!active || settings.content.path.empty())
// Don't unload the game when the lid is open while swapping disks
return;
std::string hash = getGameHash();
if (hash == "") {
@@ -974,14 +947,14 @@ void Achievements::diskChange()
rc_client_begin_change_media_from_hash(rc_client, hash.c_str(), [](int result, const char *errorMessage, rc_client_t *client, void *userdata) {
if (result == RC_HARDCORE_DISABLED) {
settings.raHardcoreMode = false;
notifier.notify(Notification::Login, "", "Hardcore mode disabled", "Unrecognized media inserted");
notifier.notify(Notification::Login, "", i18n::Ts("Hardcore mode disabled"), i18n::Ts("Unrecognized media inserted"));
}
else if (result != RC_OK)
{
settings.raHardcoreMode = false;
if (errorMessage == nullptr)
errorMessage = rc_error_str(result);
notifier.notify(Notification::Login, "", "Media change failed", errorMessage);
notifier.notify(Notification::Login, "", i18n::Ts("Media change failed"), errorMessage);
}
}, this);
}
+3 -3
View File
@@ -19,9 +19,9 @@
along with reicast. If not, see <https://www.gnu.org/licenses/>.
*/
#include "7zArchive.h"
#include "deps/lzma/7z.h"
#include "deps/lzma/7zCrc.h"
#include "deps/lzma/Alloc.h"
#include "lzma/7z.h"
#include "lzma/7zCrc.h"
#include "lzma/Alloc.h"
#include <cstring>
+2 -2
View File
@@ -21,8 +21,8 @@
#pragma once
#include "archive.h"
#include "deps/lzma/7z.h"
#include "deps/lzma/7zFile.h"
#include "lzma/7z.h"
#include "lzma/7zFile.h"
#include <algorithm>
#include <cstring>
+9
View File
@@ -0,0 +1,9 @@
target_sources(${PROJECT_NAME} PRIVATE
7zArchive.cpp
7zArchive.h
archive.cpp
archive.h
rzip.cpp
rzip.h
ZipArchive.cpp
ZipArchive.h)
+17
View File
@@ -0,0 +1,17 @@
target_sources(${PROJECT_NAME} PRIVATE
audiostream.h)
if(NOT LIBRETRO)
target_sources(${PROJECT_NAME} PRIVATE
audiobackend_alsa.cpp
audiobackend_coreaudio.cpp
audiobackend_directsound.cpp
audiobackend_libao.cpp
audiobackend_null.cpp
audiobackend_oboe.cpp
audiobackend_omx.cpp
audiobackend_oss.cpp
audiobackend_pulseaudio.cpp
audiobackend_sdl2.cpp
audiostream.cpp)
endif()
+1 -1
View File
@@ -20,7 +20,7 @@ public:
{
snd_pcm_hw_params_t *params;
std::string device = cfgLoadStr("alsa", "device", "");
std::string device = config::loadStr("alsa", "device");
int rc = -1;
if (!device.empty() && device != "auto") {
+17 -10
View File
@@ -1,5 +1,5 @@
#include "audiostream.h"
#include "oslib/i18n.h"
#include <chrono>
#include <thread>
@@ -9,7 +9,7 @@ class NullAudioBackend : public AudioBackend
public:
NullAudioBackend()
: AudioBackend("null", "No Audio") {}
: AudioBackend("null", Tnop("No Audio")) {}
bool init() override
{
@@ -19,17 +19,20 @@ public:
u32 push(const void* frame, u32 samples, bool wait) override
{
if (wait)
if (wait && last_time.time_since_epoch() != the_clock::duration::zero())
{
if (last_time.time_since_epoch() != the_clock::duration::zero())
{
auto fduration = std::chrono::nanoseconds(1000000000L * samples / 44100);
auto duration = fduration - (the_clock::now() - last_time);
auto fduration = std::chrono::nanoseconds(1'000'000'000LL * samples / 44100);
auto duration = fduration - (the_clock::now() - last_time);
if (duration > std::chrono::nanoseconds::zero())
std::this_thread::sleep_for(duration);
last_time += fduration;
}
else
if (duration < -std::chrono::milliseconds(67))
// if ~4 frames ahead, reset time (fast forward detection)
last_time = the_clock::now();
else
last_time += fduration;
}
else {
last_time = the_clock::now();
}
return 1;
}
@@ -45,6 +48,10 @@ public:
return samples;
}
std::string getName() const override {
return i18n::Ts(name);
}
private:
the_clock::time_point last_time;
};
+8 -2
View File
@@ -19,12 +19,13 @@
#ifdef USE_OBOE
#include "audiostream.h"
#include "cfg/option.h"
#include "stdclass.h"
#include "oslib/i18n.h"
#include <oboe/Oboe.h>
#include <vector>
#include <algorithm>
#include <atomic>
#include <memory>
#include "stdclass.h"
class OboeBackend : AudioBackend
{
@@ -77,7 +78,7 @@ class OboeBackend : AudioBackend
public:
OboeBackend()
: AudioBackend("Oboe", "Automatic AAudio / OpenSL selection"), audioCallback(this), errorCallback(this) {}
: AudioBackend("Oboe", Tnop("Automatic AAudio / OpenSL selection")), audioCallback(this), errorCallback(this) {}
bool init() override
{
@@ -195,6 +196,11 @@ public:
}
return std::max(0, result.value());
}
std::string getName() const override {
return i18n::Ts(name);
}
};
static OboeBackend oboeBackend;
+76 -41
View File
@@ -10,29 +10,87 @@ class OSSAudioBackend : public AudioBackend
int audioFD = -1;
int recordFD = -1;
static int openDevice(int flags)
{
const char* path = "/dev/dsp";
int fd = open(path, flags);
if (fd < 0)
ERROR_LOG(AUDIO, "OSS: open(%s) failed: %s", path, strerror(errno));
return fd;
}
static bool setRate(int fd, int rate)
{
int tmp = rate;
if (ioctl(fd, SNDCTL_DSP_SPEED, &tmp) < 0)
{
ERROR_LOG(AUDIO, "OSS: ioctl(SNDCTL_DSP_SPEED) failed: %s", strerror(errno));
return false;
}
if (tmp != rate)
{
ERROR_LOG(AUDIO, "OSS: sample rate unsupported: %d => %d", rate, tmp);
return false;
}
return true;
}
static bool setChannels(int fd, int channels)
{
int tmp = channels;
if (ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) < 0)
{
ERROR_LOG(AUDIO, "OSS: ioctl(SNDCTL_DSP_CHANNELS) failed: %s", strerror(errno));
return false;
}
if (tmp != channels)
{
ERROR_LOG(AUDIO, "OSS: channels unsupported: %d => %d", channels, tmp);
return false;
}
return true;
}
static bool setFormat(int fd, int format)
{
int tmp = format;
if (ioctl(fd, SNDCTL_DSP_SETFMT, &tmp) < 0)
{
ERROR_LOG(AUDIO, "OSS: ioctl(SNDCTL_DSP_SETFMT) failed: %s", strerror(errno));
return false;
}
if (tmp != format)
{
ERROR_LOG(AUDIO, "OSS: sample format unsupported: %#.8x => %#.8x", format, tmp);
return false;
}
return true;
}
public:
OSSAudioBackend()
: AudioBackend("oss", "Open Sound System") {}
bool init() override
{
audioFD = open("/dev/dsp", O_WRONLY);
if (audioFD < 0)
audioFD = openDevice(O_WRONLY);
if (audioFD < 0 || !setRate(audioFD, 44100) || !setChannels(audioFD, 2) || !setFormat(audioFD, AFMT_S16_LE))
{
WARN_LOG(AUDIO, "Couldn't open /dev/dsp.");
term();
return false;
}
INFO_LOG(AUDIO, "sound enabled, dsp opened for write");
int tmp=44100;
int err_ret;
err_ret=ioctl(audioFD,SNDCTL_DSP_SPEED,&tmp);
INFO_LOG(AUDIO, "set Frequency to %i, return %i (rate=%i)", 44100, err_ret, tmp);
int channels=2;
err_ret=ioctl(audioFD, SNDCTL_DSP_CHANNELS, &channels);
INFO_LOG(AUDIO, "set dsp to stereo (%i => %i)", channels, err_ret);
int format=AFMT_S16_LE;
err_ret=ioctl(audioFD, SNDCTL_DSP_SETFMT, &format);
INFO_LOG(AUDIO, "set dsp to %s audio (%i/%i => %i)", "16bits signed", AFMT_S16_LE, format, err_ret);
return true;
}
@@ -54,34 +112,11 @@ public:
bool initRecord(u32 sampling_freq) override
{
recordFD = open("/dev/dsp", O_RDONLY);
if (recordFD < 0)
recordFD = openDevice(O_RDONLY);
if (recordFD < 0 || !setRate(recordFD, sampling_freq) || !setChannels(recordFD, 1) || !setFormat(recordFD, AFMT_S16_NE /* Native 16 bits */ ))
{
INFO_LOG(AUDIO, "OSS: can't open default audio capture device");
return false;
}
int tmp = AFMT_S16_NE; // Native 16 bits
if (ioctl(recordFD, SNDCTL_DSP_SETFMT, &tmp) == -1 || tmp != AFMT_S16_NE)
{
INFO_LOG(AUDIO, "OSS: can't set sample format");
close(recordFD);
recordFD = -1;
return false;
}
tmp = 1;
if (ioctl(recordFD, SNDCTL_DSP_CHANNELS, &tmp) == -1)
{
INFO_LOG(AUDIO, "OSS: can't set channel count");
close(recordFD);
recordFD = -1;
return false;
}
tmp = sampling_freq;
if (ioctl(recordFD, SNDCTL_DSP_SPEED, &tmp) == -1)
{
INFO_LOG(AUDIO, "OSS: can't set sample rate");
close(recordFD);
recordFD = -1;
termRecord();
return false;
}
+1 -1
View File
@@ -66,7 +66,7 @@ class SDLAudioBackend : AudioBackend
public:
SDLAudioBackend()
: AudioBackend("sdl2", "Simple DirectMedia Layer 2 Audio") {}
: AudioBackend("sdl2", "Simple DirectMedia Layer 2") {}
bool init() override
{
+23 -3
View File
@@ -1,5 +1,8 @@
#include "audiostream.h"
#include "cfg/option.h"
#include "emulator.h"
static void registerForEvents();
struct SoundFrame { s16 l; s16 r; };
@@ -32,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;
}
@@ -60,6 +63,7 @@ void WriteSample(s16 r, s16 l)
void InitAudio()
{
registerForEvents();
TermAudio();
std::string slug = config::AudioBackend;
@@ -71,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;
@@ -110,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;
}
@@ -138,3 +142,19 @@ void StopAudioRecording()
currentBackend->termRecord();
audio_recording_started = false;
}
static void registerForEvents()
{
static bool done;
if (done)
return;
done = true;
// Empty the audio buffer when loading a state or terminating the game
const auto& callback = [](Event, void *) {
writePtr = 0;
};
EventManager::listen(Event::Terminate, callback);
EventManager::listen(Event::LoadState, callback);
}
+2 -1
View File
@@ -35,7 +35,7 @@ public:
virtual void termRecord() {}
std::string slug;
std::string name;
virtual std::string getName() const { return name; }
static size_t getCount() { return backends == nullptr ? 0 : backends->size(); }
static AudioBackend *getBackend(size_t index) { return backends == nullptr ? nullptr : (*backends)[index]; }
@@ -46,6 +46,7 @@ protected:
: slug(slug), name(name) {
registerAudioBackend(this);
}
std::string name;
private:
static void registerAudioBackend(AudioBackend *backend)
+15 -10
View File
@@ -15,12 +15,8 @@
//HOST_CPU
#define CPU_X86 0x20000001
#define CPU_ARM 0x20000002
#define CPU_MIPS 0x20000003
#define CPU_ARM64 0x20000003
#define CPU_X64 0x20000004
#define CPU_GENERIC 0x20000005 //used for pnacl, emscripten, etc
#define CPU_PPC 0x20000006
#define CPU_PPC64 0x20000007
#define CPU_ARM64 0x20000008
//FEAT_SHREC, FEAT_AREC, FEAT_DSPREC
#define DYNAREC_NONE 0x40000001
@@ -36,10 +32,19 @@
#define HOST_CPU CPU_ARM
#elif defined(__aarch64__) || defined(_M_ARM64)
#define HOST_CPU CPU_ARM64
#elif defined(__mips__)
#define HOST_CPU CPU_MIPS
#else
#define HOST_CPU CPU_GENERIC
#error Unsupported architecture
#endif
#if defined(__APPLE__)
#include "TargetConditionals.h"
#if TARGET_OS_SIMULATOR
// iOS simulator
#define TARGET_NO_REC
#endif
#if defined(TARGET_MAC) && HOST_CPU == CPU_ARM64
#define TARGET_ARM_MAC
#endif
#endif
#if defined(TARGET_NO_REC)
@@ -93,14 +98,14 @@
#ifdef _WIN32
#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
#define TARGET_UWP
#define TARGET_UWP 1
#endif
#ifdef HAVE_D3D11
#define USE_DX11
#endif
#endif
#if !defined(LIBRETRO) && !defined(TARGET_NO_EXCEPTIONS)
#if !defined(LIBRETRO)
#define USE_GGPO
#endif
+12
View File
@@ -0,0 +1,12 @@
target_sources(${PROJECT_NAME} PRIVATE
option.h)
if(NOT LIBRETRO)
target_sources(${PROJECT_NAME} PRIVATE
cfg.cpp
cfg.h
cl.cpp
ini.cpp
ini.h
option.cpp)
endif()
+62 -63
View File
@@ -1,37 +1,33 @@
#include "cfg.h"
#include "ini.h"
#include "stdclass.h"
#include <cerrno>
namespace config
{
static std::string cfgPath;
static bool save_config = true;
static bool autoSave = true;
static bool autoSaving = true;
static IniFile cfgdb;
static emucfg::ConfigFile cfgdb;
static void saveConfigFile()
static void saveFile()
{
FILE* cfgfile = nowide::fopen(cfgPath.c_str(), "wt");
if (!cfgfile)
{
if (!cfgfile) {
WARN_LOG(COMMON, "Error: Unable to open file '%s' for saving", cfgPath.c_str());
}
else
{
else {
cfgdb.save(cfgfile);
std::fclose(cfgfile);
}
}
void cfgSaveStr(const std::string& section, const std::string& key, const std::string& value)
{
cfgdb.set(section, key, value);
if (save_config && autoSave)
saveConfigFile();
static void autoSave() {
if (autoSaving)
saveFile();
}
bool cfgOpen()
bool open()
{
if (get_writable_config_path("").empty())
// Config dir not set (android onboarding)
@@ -41,9 +37,9 @@ bool cfgOpen()
std::string config_path_read = get_readonly_config_path(filename);
cfgPath = get_writable_config_path(filename);
FILE* cfgfile = nowide::fopen(config_path_read.c_str(), "r");
if(cfgfile != NULL) {
cfgdb.parse(cfgfile);
FILE* cfgfile = nowide::fopen(config_path_read.c_str(), "rt");
if (cfgfile != nullptr) {
cfgdb.load(cfgfile);
std::fclose(cfgfile);
}
else
@@ -55,78 +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 saveFloat(const std::string& section, const std::string& key, float value) {
cfgdb.set(section, key, value);
autoSave();
}
float loadFloat(const std::string& section, const std::string& key, float def) {
return cfgdb.getFloat(section, key, def);
}
} // namespace config
+23 -17
View File
@@ -1,22 +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 cfgSetVirtual(const std::string& section, const std::string& key, const std::string& value);
bool cfgIsVirtual(const std::string& section, const std::string& key);
namespace config
{
void ParseCommandLine(int argc, char *argv[]);
bool open();
int loadInt(const std::string& section, const std::string& key, int def = 0);
void saveInt(const std::string& section, const std::string& key, int value);
int64_t loadInt64(const std::string& section, const std::string& key, int64_t def = 0);
void saveInt64(const std::string& section, const std::string& key, int64_t value);
std::string loadStr(const std::string& section, const std::string& key, const std::string& def = {});
void saveStr(const std::string& section, const std::string& key, const std::string& value);
bool loadBool(const std::string& section, const std::string& key, bool def = false);
void saveBool(const std::string& section, const std::string& key, bool value);
float loadFloat(const std::string& section, const std::string& key, float def = 0.f);
void saveFloat(const std::string& section, const std::string& key, float value);
void setTransient(const std::string& section, const std::string& key, const std::string& value);
bool isTransient(const std::string& section, const std::string& key);
void cfgSetAutoSave(bool autoSave);
bool cfgHasSection(const std::string& section);
void cfgDeleteSection(const std::string& section);
void cfgDeleteEntry(const std::string& section, const std::string& key);
void parseCommandLine(int argc, const char * const argv[]);
void setAutoSave(bool autoSave);
bool hasSection(const std::string& section);
void deleteSection(const std::string& section);
void deleteEntry(const std::string& section, const std::string& key);
}
+91 -119
View File
@@ -1,143 +1,115 @@
/*
Command line parsing
*/
Copyright 2025 flyinghead
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#include "cfg/cfg.h"
#include "stdclass.h"
#include <cstdio>
#include <cstring>
#include "cfg/cfg.h"
#include "stdclass.h"
static int setconfig(char *arg[], int cl)
namespace config
{
int rv=0;
if (cl < 1)
static void usage(const char *exe)
{
fprintf(stderr, "Usage: %s [option]... [<rom path>]\n", exe);
fprintf(stderr, "Options:\n");
fprintf(stderr, "-config section:key=value,... set a transient config value.\n");
fprintf(stderr, " Transient config values won't be saved to emu.cfg.\n");
fprintf(stderr, "-help display this help\n");
}
static void parseConfigOption(const std::string& str)
{
size_t pos = 0;
while (pos < str.length())
{
WARN_LOG(COMMON, "-config : invalid number of parameters, format is section:key=value,section:key=value,...");
return rv;
size_t comma = str.find(',', pos);
if (comma == str.npos)
comma = str.length();
std::string option = str.substr(pos, comma - pos);
size_t colpos = option.find(':');
size_t eqpos = option.npos;
if (colpos != option.npos)
{
std::string section = option.substr(0, colpos);
eqpos = option.find('=', colpos);
if (eqpos != option.npos)
{
std::string key = option.substr(colpos + 1, eqpos - (colpos + 1));
std::string value = option.substr(eqpos + 1);
setTransient(section, key, value);
DEBUG_LOG(COMMON, "-config [%s] %s = %s", section.c_str(), key.c_str(), value.c_str());
}
}
if (colpos == option.npos || eqpos == option.npos) {
WARN_LOG(COMMON, "Invalid -config option '%s'. Format is: -config section:key=value,...", str.c_str());
return;
}
pos = comma + 1;
}
std::string value(arg[1]);
for(;;)
{
auto seppos = value.find(':');
if (seppos == std::string::npos)
{
WARN_LOG(COMMON, "-config : invalid parameter %s, format is section:key=value,section:key=value,...", value.c_str());
break;
}
auto eqpos = value.find('=', seppos);
if (eqpos == std::string::npos)
{
WARN_LOG(COMMON, "-config : invalid parameter %s, format is section:key=value,section:key=value,...", value.c_str());
break;
}
auto commapos = value.find(',', eqpos);
std::string sect = trim_ws(value.substr(0, seppos));
std::string key = trim_ws(value.substr(seppos + 1, eqpos - seppos - 1));
auto endofcurrent = (commapos == std::string::npos) ? value.size() : commapos;
std::string next = (commapos == std::string::npos) ? "" : trim_ws(value.substr(endofcurrent + 1, value.size() - endofcurrent));
value = trim_ws(value.substr(eqpos + 1, endofcurrent - eqpos - 1));
if (sect.empty() || key.empty())
{
WARN_LOG(COMMON, "-config : invalid parameter, format is section:key=value,section:key=value,...");
break;
}
INFO_LOG(COMMON, "Virtual cfg %s:%s=%s", sect.c_str(), key.c_str(), value.c_str());
cfgSetVirtual(sect, key, value);
if (commapos == std::string::npos)
rv++;
if (commapos != std::string::npos)
{
value = next;
continue;
}
else if (cl>=3 && strcmp(arg[2],",")==0)
{
cl-=2;
arg+=2;
rv++;
continue;
}
else
break;
}
return rv;
}
static int showhelp()
{
printf("Usage: flycast [OPTION]... [CONTENT]\n\n");
printf("Options:\n");
printf("-config section:key=value add a virtual config value;\n");
printf(" virtual config values won't be saved to the .cfg file\n");
printf(" unless a different value is written to them\n");
printf("-help display this help\n");
exit(0);
return 0;
}
void ParseCommandLine(int argc,char* argv[])
void parseCommandLine(int argc, const char * const argv[])
{
settings.content.path.clear();
int cl=argc-2;
char** arg=argv+1;
while(cl>=0)
const char *exe = argv[0];
for (int i = 1; i < argc; i++)
{
if (stricmp(*arg,"-help")==0 || stricmp(*arg,"--help")==0)
{
showhelp();
if (!strcmp(argv[i], "-help") || !strcmp(argv[i], "--help")) {
usage(exe);
exit(0);
}
else if (stricmp(*arg,"-config")==0 || stricmp(*arg,"--config")==0)
if (!strcmp(argv[i], "-config") || !strcmp(argv[i], "--config"))
{
int as=setconfig(arg,cl);
cl-=as;
arg+=as;
if (i < argc - 1)
parseConfigOption(argv[++i]);
continue;
}
#if defined(__APPLE__)
else if (!strncmp(*arg, "-NSDocumentRevisions", 20))
// macOS
if (!strncmp(argv[i], "-NSDocumentRevisions", 20)) {
i++;
continue;
}
if (argv[i][0] == '-') {
WARN_LOG(COMMON, "Ignoring unknown command line option '%s'", argv[i]);
continue;
}
std::string extension = get_file_extension(argv[i]);
if (extension == "cdi" || extension == "chd"
|| extension == "gdi"|| extension == "cue")
{
arg++;
cl--;
INFO_LOG(COMMON, "Using '%s' as CD image", argv[i]);
settings.content.path = argv[i];
}
#endif
else if ((*arg)[0] == '-')
else if (extension == "elf")
{
WARN_LOG(COMMON, "Ignoring unknown command line option '%s'", *arg);
INFO_LOG(COMMON, "Using '%s' as reios elf file", argv[i]);
setTransient("config", "bios.UseReios", "yes");
settings.content.path = argv[i];
}
else
{
char* extension = strrchr(*arg, '.');
if (extension
&& (stricmp(extension, ".cdi") == 0 || stricmp(extension, ".chd") == 0
|| stricmp(extension, ".gdi") == 0 || stricmp(extension, ".cue") == 0))
{
INFO_LOG(COMMON, "Using '%s' as cd image", *arg);
settings.content.path = *arg;
}
else if (extension && stricmp(extension, ".elf") == 0)
{
INFO_LOG(COMMON, "Using '%s' as reios elf file", *arg);
cfgSetVirtual("config", "bios.UseReios", "yes");
settings.content.path = *arg;
}
else
{
INFO_LOG(COMMON, "Using '%s' as rom", *arg);
settings.content.path = *arg;
}
else {
INFO_LOG(COMMON, "Using '%s' as rom", argv[i]);
settings.content.path = argv[i];
}
arg++;
cl--;
if (i < argc - 1)
WARN_LOG(COMMON, "Rest of command line ignored: '%s'...", argv[i + 1]);
break;
}
}
} // namespace config
+268 -244
View File
@@ -1,298 +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);
}
const Entry *entry = getEntry(section, name);
if (entry == nullptr)
return nullptr;
if (entry->transient)
return &entry->transientValue;
else
{
return atoi(this->value.c_str());
}
return &entry->value;
}
int64_t ConfigEntry::get_int64()
const std::string& IniFile::getString(const std::string& section, const std::string& entry, const std::string& defaultValue) const
{
int base = 10;
if (strstr(this->value.c_str(), "0x") != nullptr)
base = 16;
return (int64_t)strtoll(this->value.c_str(), nullptr, base);
const std::string *pValue = getRaw(section, entry);
if (pValue == nullptr)
return defaultValue;
else
return *pValue;
}
bool ConfigEntry::get_bool()
bool IniFile::getBool(const std::string& section, const std::string& entry, bool defaultValue) const
{
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)
{
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;
}
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;
}
}
/* ConfigSection */
bool ConfigSection::has_entry(const std::string& name)
static std::string handleEscapeSeq(const std::string& s)
{
return (this->entries.count(name) == 1);
}
ConfigEntry* ConfigSection::get_entry(const std::string& name)
{
if(this->has_entry(name))
std::string ret;
ret.reserve(s.length());
for (size_t i = 0; i < s.length(); i++)
{
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)
char c = s[i];
if (c == '\\' && i < s.length() - 1)
{
return &this->virtual_sections[name];
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;
}
}
else
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++)
{
if (this->sections.count(name) == 1)
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] == '[')
{
return &this->sections[name];
// 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);
}
}
return NULL;
}
ConfigEntry* ConfigFile::get_entry(const std::string& section_name, const std::string& entry_name)
void IniFile::load(FILE *file, bool cEscape)
{
ConfigSection* section = this->get_section(section_name, true);
if(section != NULL && section->has_entry(entry_name))
std::string data;
for (;;)
{
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();
}
}
void ConfigFile::set(const std::string& section_name, const std::string& entry_name, const std::string& value, bool is_virtual)
{
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);
}
void ConfigFile::set_int(const std::string& section_name, const std::string& entry_name, int 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_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::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)
char buffer[4096];
int n = fread(buffer, 1, sizeof(buffer), file);
if (n <= 0)
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);
data += std::string(buffer, buffer + n);
}
load(data, cEscape);
}
void ConfigFile::save(FILE* file)
void IniFile::save(std::string& data) const
{
for (const auto& [section_name, section] : this->sections)
std::ostringstream ss;
for (const auto& [sectionName, section] : sections)
{
if (!section_name.empty())
std::fprintf(file, "[%s]\n", section_name.c_str());
for (const auto& [entry_name, entry] : section.entries)
bool sectionSaved = sectionName.empty();
for (const auto& [name, entry] : section.entries)
{
std::fprintf(file, "%s = %s\n", entry_name.c_str(), entry.get_string().c_str());
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;
}
if (!section_name.empty())
std::fputc('\n', file);
ss << std::endl;
}
data = ss.str();
}
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)
void IniFile::save(FILE *file) const
{
ConfigSection *section = get_section(section_name, false);
if (section != NULL)
section->delete_entry(entry_name);
std::string data;
save(data);
fwrite(data.c_str(), 1, data.length(), file);
}
bool ConfigFile::is_virtual(const std::string& section_name, const std::string& entry_name)
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
{
ConfigSection *section = get_section(section_name, true);
if (section == nullptr)
const Entry *entry = getEntry(section, name);
if (entry == nullptr)
return false;
return section->has_entry(entry_name);
else
return entry->transient;
}
} // namespace emucfg
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 -49
View File
@@ -1,59 +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();
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);
/* 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 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
+48 -12
View File
@@ -40,6 +40,7 @@ Option<bool> ForceFreePlay("ForceFreePlay", true);
Option<bool, false> FetchBoxart("FetchBoxart", true);
Option<bool, false> BoxartDisplayMode("BoxartDisplayMode", true);
Option<int, false> UIScaling("UIScaling", 100);
Option<int, false> UITheme("UITheme", 0);
// Sound
@@ -71,11 +72,13 @@ Option<bool> ShowFPS("rend.ShowFPS");
Option<bool> RenderToTextureBuffer("rend.RenderToTextureBuffer");
Option<bool> TranslucentPolygonDepthMask("rend.TranslucentPolygonDepthMask");
Option<bool> ModifierVolumes("rend.ModifierVolumes", true);
Option<int> TextureUpscale("rend.TextureUpscale", 1);
Option<int> TextureUpscale("rend.TextureUpscale2", 1);
Option<int> MaxFilteredTextureSize("rend.MaxFilteredTextureSize", 256);
Option<float> ExtraDepthScale("rend.ExtraDepthScale", 1.f);
Option<bool> CustomTextures("rend.CustomTextures");
Option<bool> PreloadCustomTextures("rend.PreloadCustomTextures");
Option<bool> DumpTextures("rend.DumpTextures");
Option<bool> DumpReplacedTextures("rend.DumpReplacedTextures");
Option<int> ScreenStretching("rend.ScreenStretching", 100);
Option<bool> Fog("rend.Fog", true);
Option<bool> FloatVMUs("rend.FloatVMUs");
@@ -98,6 +101,8 @@ Option<int> SkipFrame("ta.skip");
Option<int> MaxThreads("pvr.MaxThreads", 3);
Option<int> AutoSkipFrame("pvr.AutoSkipFrame", 0);
Option<int> RenderResolution("rend.Resolution", 480);
Option<bool> IntegerScale("rend.IntegerScale", false);
Option<bool> LinearInterpolation("rend.LinearInterpolation", true);
Option<bool> VSync("rend.vsync", true);
Option<int64_t> PixelBufferSize("rend.PixelBufferSize", 512_MB);
Option<int> AnisotropicFiltering("rend.AnisotropicFiltering", 1);
@@ -105,7 +110,11 @@ Option<int> TextureFiltering("rend.TextureFiltering", 0); // Default
Option<bool> ThreadedRendering("rend.ThreadedRendering", true);
Option<bool> DupeFrames("rend.DupeFrames", false);
Option<int> PerPixelLayers("rend.PerPixelLayers", 32);
#ifdef TARGET_UWP
Option<bool> NativeDepthInterpolation("rend.NativeDepthInterpolation", true);
#else
Option<bool> NativeDepthInterpolation("rend.NativeDepthInterpolation", false);
#endif
Option<bool> EmulateFramebuffer("rend.EmulateFramebuffer", false);
Option<bool> FixUpscaleBleedingEdge("rend.FixUpscaleBleedingEdge", true);
Option<bool> CustomGpuDriver("rend.CustomGpuDriver", false);
@@ -129,12 +138,22 @@ Option<bool> RamMod32MB("Dreamcast.RamMod32MB", false);
Option<bool> OpenGlChecks("OpenGlChecks", false, "validate");
Option<std::vector<std::string>, false> ContentPath("Dreamcast.ContentPath");
Option<std::vector<std::string>, false> BiosPath("Dreamcast.BiosPath");
Option<std::string, false> VMUPath("Dreamcast.VMUPath");
Option<std::vector<std::string>, false> SavestatePath("Dreamcast.SavestatePath");
Option<std::string, false> SavePath("Dreamcast.SavePath");
Option<std::vector<std::string>, false> TexturePath("Dreamcast.TexturePath");
Option<std::string, false> TextureDumpPath("Dreamcast.TextureDumpPath");
Option<std::string, false> BoxartPath("Dreamcast.BoxartPath");
Option<std::vector<std::string>, false> MappingsPath("Dreamcast.MappingsPath");
Option<std::vector<std::string>, false> CheatPath("Dreamcast.CheatPath");
Option<bool, false> HideLegacyNaomiRoms("Dreamcast.HideLegacyNaomiRoms", true);
Option<bool, false> UploadCrashLogs("UploadCrashLogs", true);
Option<bool, false> DiscordPresence("DiscordPresence", true);
#if defined(__ANDROID__) && !defined(LIBRETRO)
Option<bool, false> UseSafFilePicker("UseSafFilePicker", true);
#endif
OptionString LogServer("LogServer", "", "log");
// Profiler
Option<bool> ProfilerEnabled("Profiler.Enabled");
@@ -161,6 +180,8 @@ Option<int> GGPOChatTimeout("GGPOChatTimeout", 10, "network");
Option<bool> NetworkOutput("NetworkOutput", false, "network");
Option<int> MultiboardSlaves("MultiboardSlaves", 1, "network");
Option<bool> BattleCableEnable("BattleCable", false, "network");
Option<bool> UseDCNet("DCNet", false, "network");
OptionString ISPUsername("ISPUsername", "flycast1", "network");
#ifdef USE_OMX
Option<int> OmxAudioLatency("audio_latency", 100, "omx");
@@ -179,23 +200,38 @@ std::array<Option<MapleDeviceType>, 4> MapleMainDevices {
Option<MapleDeviceType>("device3", MDT_None, "input"),
Option<MapleDeviceType>("device4", MDT_None, "input"),
};
std::array<std::array<Option<MapleDeviceType>, 2>, 4> MapleExpansionDevices {
Option<MapleDeviceType>("device1.1", MDT_SegaVMU, "input"),
Option<MapleDeviceType>("device1.2", MDT_SegaVMU, "input"),
std::array<std::array<Option<MapleDeviceType>, 2>, 4> MapleExpansionDevices {{
{{Option<MapleDeviceType>("device1.1", MDT_SegaVMU, "input"),
Option<MapleDeviceType>("device1.2", MDT_SegaVMU, "input")}},
Option<MapleDeviceType>("device2.1", MDT_None, "input"),
Option<MapleDeviceType>("device2.2", MDT_None, "input"),
{{Option<MapleDeviceType>("device2.1", MDT_None, "input"),
Option<MapleDeviceType>("device2.2", MDT_None, "input")}},
Option<MapleDeviceType>("device3.1", MDT_None, "input"),
Option<MapleDeviceType>("device3.2", MDT_None, "input"),
{{Option<MapleDeviceType>("device3.1", MDT_None, "input"),
Option<MapleDeviceType>("device3.2", MDT_None, "input")}},
Option<MapleDeviceType>("device4.1", MDT_None, "input"),
Option<MapleDeviceType>("device4.2", MDT_None, "input"),
};
Option<bool> PerGameVmu("PerGameVmu", false, "config");
{{Option<MapleDeviceType>("device4.1", MDT_None, "input"),
Option<MapleDeviceType>("device4.2", MDT_None, "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<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", false);
#ifdef USE_LUA
Option<std::string, false> LuaFileName("LuaFileName", "flycast.lua");
+37 -24
View File
@@ -56,7 +56,7 @@ public:
{
if (gameId.empty())
return;
if (!cfgHasSection(gameId))
if (!hasSection(gameId))
return;
perGameConfig = true;
}
@@ -65,10 +65,10 @@ public:
}
void save() {
cfgSetAutoSave(false);
setAutoSave(false);
for (const auto& o : options)
o->save();
cfgSetAutoSave(true);
setAutoSave(true);
}
void setGameId(const std::string& gameId) {
@@ -82,7 +82,7 @@ public:
this->perGameConfig = perGameConfig;
if (!perGameConfig) {
if (!gameId.empty())
cfgDeleteSection(gameId);
deleteSection(gameId);
reset();
}
}
@@ -122,7 +122,7 @@ public:
else
{
set(doLoad(section, name));
if (cfgIsVirtual(section, name))
if (isTransient(section, name))
override(value);
}
}
@@ -141,7 +141,7 @@ public:
if (value == doLoad(section, name))
{
// delete existing per-game option if any
cfgDeleteEntry(settings.gameId, section + "." + name);
deleteEntry(settings.gameId, section + "." + name);
return;
}
}
@@ -173,14 +173,14 @@ protected:
std::enable_if_t<std::is_same_v<U, bool>, T>
doLoad(const std::string& section, const std::string& name) const
{
return cfgLoadBool(section, name, value);
return loadBool(section, name, value);
}
template <typename U = T>
std::enable_if_t<std::is_same<U, int64_t>::value, T>
doLoad(const std::string& section, const std::string& name) const
{
return cfgLoadInt64(section, name, value);
return loadInt64(section, name, value);
}
template <typename U = T>
@@ -188,32 +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
{
std::string strValue = cfgLoadStr(section, name, "");
if (strValue.empty())
return value;
else
return (float)atof(strValue.c_str());
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;
@@ -272,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>
@@ -287,23 +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
{
char buf[64];
snprintf(buf, sizeof(buf), "%f", value);
cfgSaveStr(section, name, buf);
saveFloat(section, name, value);
}
template <typename U = T>
@@ -338,7 +332,7 @@ protected:
else
s += v;
}
cfgSaveStr(section, name, s);
saveStr(section, name, s);
}
std::string section;
@@ -373,6 +367,7 @@ extern Option<bool> ForceFreePlay;
extern Option<bool, false> FetchBoxart;
extern Option<bool, false> BoxartDisplayMode;
extern Option<int, false> UIScaling;
extern Option<int, false> UITheme; // 0 -> Dark, 1 -> Light, 2 -> Dreamcast, 3 -> High Contrast, 4 -> Nintendo, 5 -> Aqua Chill
// Sound
@@ -451,7 +446,9 @@ extern Option<int> PerPixelLayers;
#endif
extern Option<float> ExtraDepthScale;
extern Option<bool> CustomTextures;
extern Option<bool> PreloadCustomTextures;
extern Option<bool> DumpTextures;
extern Option<bool> DumpReplacedTextures;
extern Option<int> ScreenStretching; // in percent. 150 means stretch from 4/3 to 6/3
extern Option<bool> Fog;
extern Option<bool> FloatVMUs;
@@ -465,6 +462,8 @@ extern Option<int> SkipFrame;
extern Option<int> MaxThreads;
extern Option<int> AutoSkipFrame; // 0: none, 1: some, 2: more
extern Option<int> RenderResolution;
extern Option<bool> IntegerScale;
extern Option<bool> LinearInterpolation;
extern Option<bool> VSync;
extern Option<int64_t> PixelBufferSize;
extern Option<int> AnisotropicFiltering;
@@ -495,12 +494,22 @@ extern Option<bool> RamMod32MB;
extern Option<bool> OpenGlChecks;
extern Option<std::vector<std::string>, false> ContentPath;
extern Option<std::vector<std::string>, false> BiosPath;
extern Option<std::string, false> VMUPath;
extern Option<std::vector<std::string>, false> SavestatePath;
extern Option<std::string, false> SavePath;
extern Option<std::vector<std::string>, false> TexturePath;
extern Option<std::string, false> TextureDumpPath;
extern Option<std::string, false> BoxartPath;
extern Option<std::vector<std::string>, false> MappingsPath;
extern Option<std::vector<std::string>, false> CheatPath;
extern Option<bool, false> HideLegacyNaomiRoms;
extern Option<bool, false> UploadCrashLogs;
extern Option<bool, false> DiscordPresence;
#if defined(__ANDROID__) && !defined(LIBRETRO)
extern Option<bool, false> UseSafFilePicker;
#endif
extern OptionString LogServer;
// Profiling
extern Option<bool> ProfilerEnabled;
@@ -527,6 +536,8 @@ extern Option<int> GGPOChatTimeout;
extern Option<bool> NetworkOutput;
extern Option<int> MultiboardSlaves;
extern Option<bool> BattleCableEnable;
extern Option<bool> UseDCNet;
extern OptionString ISPUsername;
#ifdef USE_OMX
extern Option<int> OmxAudioLatency;
@@ -540,12 +551,14 @@ 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<std::array<Option<int>, 2>, 4> NetworkExpansionDevices;
extern Option<bool> PerGameVmu;
#ifdef _WIN32
extern Option<bool, false> UseRawInput;
#else
constexpr bool UseRawInput = false;
#endif
extern Option<bool> UsePhysicalVmuMemory;
#ifdef USE_LUA
extern Option<std::string, false> LuaFileName;
+226 -54
View File
@@ -28,6 +28,8 @@
#include "cfg/option.h"
#include "emulator.h"
#include "oslib/storage.h"
#include "oslib/i18n.h"
using namespace i18n;
const WidescreenCheat CheatManager::widescreen_cheats[] =
{
@@ -83,7 +85,7 @@ const WidescreenCheat CheatManager::widescreen_cheats[] =
{ "T30006M", nullptr, { 0x4CF42C, 0x4CF45C, 0x3E1A36, 0x3E1A34, 0x3E1A3C, 0x3E1A54, 0x3E1A5C },
{ 0x43F00000, 0x3F400000, 0x08010000, 0, 0, 0, 0 } },
{ "MK-5103750", nullptr, { 0x1FE270 }, { 0x43700000 } }, // Daytona USA (PAL)
{ "MK-51037", nullptr, { 0x1FC6D0 }, { 0x43700000 } }, // Daytona USA (USA)
// breaks online connection { "MK-51037", nullptr, { 0x1FC6D0 }, { 0x43700000 } }, // Daytona USA (USA)
{ "T9501N-50", nullptr, { 0x9821D4 }, { 0x3F400000 } }, // Deadly Skies (PAL)
{ "T8116D 50", nullptr, { 0x2E5530 }, { 0x43700000 } }, // Dead or Alive 2 (PAL)
{ "T3601N", nullptr, { 0x2F0670 }, { 0x43700000 } }, // Dead or Alive 2 (USA)
@@ -165,10 +167,11 @@ const WidescreenCheat CheatManager::widescreen_cheats[] =
{ 0x43F00000, 0x3F400000, 0x3F800000 } }, // Napple Tale: Arsia in Daydream (JP)
//crash { "MK-51178", nullptr, { 0x23AF00, 0x23B160, 0x144D40, 0x2105B4, 0x705B40 },
// { 0xBFAAAAAB, 0xBFAAAAAB, 0xBFAAAAAB, 0xBFAAAAAB, 0x44558000 } }, // NBA 2K2
{ "T9504M", nullptr, { 0xCDE848, 0xCDE844 }, { 0x3F400000, 0x3FA00000 } }, // Nightmare Creatures II (USA)
{ "T-9502D-50", nullptr, { 0xBDE9B0, 0xBDE9C4 }, { 0x3F400000, 0x3FA00000 } }, // Nightmare Creatures II (PAL)
// { "T9504M", nullptr, { 0xCDE848, 0xCDE844 }, { 0x3F400000, 0x3FA00000 } }, // Nightmare Creatures II (USA)
// { "T-9502D-50", nullptr, { 0xBDE9B0, 0xBDE9C4 }, { 0x3F400000, 0x3FA00000 } }, // Nightmare Creatures II (PAL)
{ "MK-5110250", nullptr, { 0x87B5A4 }, { 0x43700000 } }, // Outtrigger (PAL)
{ "HDR-0118", nullptr, { 0x83E284 }, { 0x43700000 } }, // Outtrigger (JP)
{ "MK-51102", nullptr, { 0x83E284 }, { 0x43700000 } }, // Outtrigger (US)
{ "T15103D 50", nullptr, { 0x1EEE78 }, { 0x3F400000 } }, // PenPen (PAL)
{ "T17001M", nullptr, { 0x1C3828 }, { 0x3F400000 } }, // PenPen TriIcelon (JP)
{ "MK-5110050", nullptr, { 0x548E04, 0x0923C0 }, { 0x43E80000, 0x3F966666 } }, // Phantasy Star Online (PAL) TODO
@@ -394,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)
@@ -413,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
}
@@ -445,13 +448,42 @@ void CheatManager::reset(const std::string& gameId)
cheats.clear();
setActive(false);
this->gameId = 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
{
// Try to auto-locate a cheat file in user-defined CheatPath
std::string romName = settings.content.fileName;
const char* exts[] = { ".cht", ".txt" };
for (const auto& base : config::CheatPath.get())
{
if (base.empty())
continue;
for (const char* ext : exts)
{
try
{
std::string candidate = hostfs::storage().getSubPath(base, romName + ext);
if (hostfs::storage().exists(candidate))
{
loadCheatFile(candidate);
config::saveStr("cheats", gameId, candidate);
goto found_cheats;
}
}
catch (const hostfs::StorageException&)
{
}
}
}
}
}
found_cheats:
#endif
size_t cheatCount = cheats.size();
if (gameId == "Fixed BOOT strapper") // Extreme Hunting 2
@@ -481,6 +513,82 @@ void CheatManager::reset(const std::string& gameId)
else if (gameId == "SAMURAI SPIRITS 6" || gameId == "T0002M") {
cheats.emplace_back(Cheat::Type::setValue, "fix depth", true, 16, 0x0003e602, 0x0009, true); // nop (shift by 8 bits instead of 10)
}
else if (gameId == "T-8107N") { // Fur Fighters (US)
// force logging on to use more cycles
cheats.emplace_back(Cheat::Type::setValue, "enable logging", true, 32, 0x00314248, 1, true);
}
else if (gameId == "T-8113D-50") { // Fur Fighters (EU)
// force logging on to use more cycles
cheats.emplace_back(Cheat::Type::setValue, "enable logging", true, 32, 0x00314228, 1, true);
}
// Dricas auth bypass
else if (gameId == "T6807M") // Aero Dancing i
{
// modem
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass auth ifeq", true, 32, 0x0004b7a0, 0x2fd62fe6, true);
cheats.emplace_back(Cheat::Type::setValue, "bypass dricas auth", true, 32, 0x0004b7a0, 0xe000000b, true); // rts, _mov #0, r0
// BBA
cheats.emplace_back(Cheat::Type::runNextIfEq, "bba bypass auth ifeq", true, 32, 0x0004af5c, 0x2fd62fe6, true);
cheats.emplace_back(Cheat::Type::setValue, "bba bypass dricas auth", true, 32, 0x0004af5c, 0xe000000b, true);
// IP check
cheats.emplace_back(Cheat::Type::runNextIfEq, "ip check ifeq", true, 32, 0x00020860, 0x4f222fe6, true);
cheats.emplace_back(Cheat::Type::setValue, "ip check ok", true, 32, 0x00020860, 0xe000000b, true);
}
else if (gameId == "T6809M") // Aero Dancing i - Jikai Saku Made Matemasen
{
// modem
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass auth ifeq", true, 32, 0x0004b940, 0x2fd62fe6, true);
cheats.emplace_back(Cheat::Type::setValue, "bypass dricas auth", true, 32, 0x0004b940, 0xe000000b, true);
// BBA
cheats.emplace_back(Cheat::Type::runNextIfEq, "bba bypass auth ifeq", true, 32, 0x0004f848, 0x2fd62fe6, true);
cheats.emplace_back(Cheat::Type::setValue, "bba bypass dricas auth", true, 32, 0x0004f848, 0xe000000b, true);
// IP check
cheats.emplace_back(Cheat::Type::runNextIfEq, "ip check ifeq", true, 32, 0x00020980, 0x4f222fe6, true);
cheats.emplace_back(Cheat::Type::setValue, "ip check ok", true, 32, 0x00020980, 0xe000000b, true);
}
else if (gameId == "T6805M") { // Aero Dancing F - Todoroki Tsubasa no Hatsu Hikou
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass auth ifeq", true, 32, 0x0003ed10, 0x2fd62fe6, true);
cheats.emplace_back(Cheat::Type::setValue, "bypass dricas auth", true, 32, 0x0003ed10, 0xe000000b, true);
}
else if (gameId == "HDR-0106") { // Daytona USA (JP)
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass auth ifeq", true, 32, 0x0003ad30, 0x2fd62fe6, true);
cheats.emplace_back(Cheat::Type::setValue, "bypass dricas auth", true, 32, 0x0003ad30, 0xe000000b, true);
}
else if (gameId == "HDR-0073") { // Sega Tetris
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass auth ifeq", true, 32, 0x000a56f8, 0x2fd62fe6, true);
cheats.emplace_back(Cheat::Type::setValue, "bypass dricas auth", true, 32, 0x000a56f8, 0xe000000b, true);
}
else if (gameId == "T44501M") { // Golf Shiyou Yo 2
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass auth ifeq", true, 32, 0x0013f150, 0x2fd62fe6, true);
cheats.emplace_back(Cheat::Type::setValue, "bypass dricas auth", true, 32, 0x0013f150, 0xe000000b, true);
}
else if (gameId == "HDR-0124") { // Hundred Swords
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass auth ifeq", true, 32, 0x006558ac, 0x1f414f22, true);
cheats.emplace_back(Cheat::Type::setValue, "bypass dricas auth", true, 32, 0x006558ac, 0xe000000b, true);
}
else if (gameId == "T43903M") { // Culdcept II
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 == "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 == "HDR-0113") // Power Smash
{
cheats.emplace_back(Cheat::Type::runNextIfEq, "no dupe SYN ifeq", true, 16, 0x14d258, 0xbbce, true); // bsr SendNormalSYN
cheats.emplace_back(Cheat::Type::setValue, "no dupe SYN set", true, 16, 0x14d258, 0x0009, true); // nop
cheats.emplace_back(Cheat::Type::runNextIfEq, "no dupe ACK ifeq", true, 16, 0x14af42, 0x430b, true); // jsr TCPInternalSendPacket
cheats.emplace_back(Cheat::Type::setValue, "no dupe ACK set", true, 16, 0x14af42, 0x0009, true); // nop
}
else if (gameId == "HDR-0091") // Pro Yakyuu Team de Asobou Net!
{
cheats.emplace_back(Cheat::Type::runNextIfEq, "no dupe SYN ifeq", true, 16, 0xe13e0c, 0xbbce, true); // bsr SendNormalSYN
cheats.emplace_back(Cheat::Type::setValue, "no dupe SYN set", true, 16, 0xe13e0c, 0x0009, true); // nop
cheats.emplace_back(Cheat::Type::runNextIfEq, "no dupe ACK ifeq", true, 16, 0xe11af6, 0x430b, true); // jsr TCPInternalSendPacket
cheats.emplace_back(Cheat::Type::setValue, "no dupe ACK set", true, 16, 0xe11af6, 0x0009, true); // nop
}
if (cheats.size() > cheatCount)
setActive(true);
}
@@ -660,18 +768,54 @@ 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));
}
return codes;
}
// Decrypt (some) Action Replay/Gameshark/Codebreaker/Xploder encrypted codes
// Master codes change the encryption method and thus are not supported (07xxxxxx)
// Taken from DCcrypt
constexpr u32 Seeds[16] = {
0xA53A8888,
0xA1427921, 0xAC9528B1, 0xC5892354, 0x49671B12,
0xACC56121, 0xACB5381E, 0x765436E1, 0x9F2C3E54,
0x1133E312, 0xAC5E7894, 0xE9F208B1, 0x4E87DCFE,
0x43174312, 0x1D7A6C99, 0x874224A2
};
static u32 decryptCode(u32 v)
{
constexpr u32 something = 6;
if ((v & 0xf0000000) == 0)
return v;
u32 seed = Seeds[v >> 28];
if (something & 4)
v = (((v << 1) & 0x0FFFFFFE) | ((v >> 27) & 1));
if (something & 2) {
v = (((v << 1) & 0x0FFFFFFE) | ((v >> 27) & 1));
v = (((v << 8) & 0x0FFFFF00) | ((v >> 20) & 0xFF));
}
if (something & 1)
seed >>= 4;
u32 ret = (seed & 0x0FFFFFFF) ^ v;
//if ((ret & 0x0FF00000) != 0x07100000)
// something = (ret & 0x0f) + 6;
return ret;
}
static u32 decryptArg(u32 v) {
return (((v + 0x543700D0) >> 29) | ((v + 0x543700D0) * 8)) ^ Seeds[0];
}
void CheatManager::addGameSharkCheat(const std::string& name, const std::string& s)
{
std::vector<u32> codes = parseCodes(s);
@@ -687,6 +831,22 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
Cheat cheat{};
cheat.description = name;
u32 code = (codes[i] & 0xff000000) >> 24;
if (code & 0xf0)
{
// encrypted code
codes[i] = decryptCode(codes[i]);
code = (codes[i] & 0xff000000) >> 24;
if (code == 4 && i + 2 < codes.size()) {
// following args are also encrypted
codes[i + 1] = decryptArg(codes[i + 1]);
codes[i + 2] = decryptArg(codes[i + 2]);
}
else if (code == 0xd && i + 2 < codes.size()) {
// following arg is also encrypted
codes[i + 1] = decryptArg(codes[i + 1]);
}
// TODO Which ops have encrypted args (3, 5, E, F)? Couldn't find any
}
switch (code)
{
case 0:
@@ -695,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;
@@ -713,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;
@@ -734,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;
@@ -747,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;
@@ -760,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;
@@ -769,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;
@@ -777,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;
@@ -791,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;
@@ -800,14 +960,22 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
cheats.push_back(cheat);
}
break;
// TODO 7 change decryption type
// TODO 0xb delay applying codes
// TODO 0xc global enable test
case 7:
// change decryption type: 071000XX (example: 07100005)
throw FlycastException(Ts("Master codes aren't supported"));
// TODO 0xb delay applying codes: 0b0xxxxx
// Delay putting on codes for xxxxx cycles. Default 1000 (0x3e7)
// TODO 0xc global enable test: 0cxxxxxx vvvvvvvv
// If the value at address 8Cxxxxxx is equal to vvvvvvvv, execute ALL codes;
// otherwise no codes are executed. Useful for waiting until game has loaded.
case 0xd:
{
// 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)
@@ -825,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);
@@ -835,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);
@@ -854,22 +1022,26 @@ 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;
}
break;
// TODO 0xF: 0F-XXXXXX 0000YYYY
// 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
@@ -883,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)
@@ -891,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:
@@ -922,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
+8 -8
View File
@@ -21,7 +21,7 @@
#include "emulator.h"
#include "hw/sh4/sh4_if.h"
#include "hw/sh4/sh4_mem.h"
#include "hw/sh4/sh4_interpreter.h"
#include "hw/sh4/dyna/shil.h"
#include "cfg/option.h"
#include <array>
#include <signal.h>
@@ -122,9 +122,9 @@ public:
for (u32 i = 0; i < Sh4RegList.size(); i++)
{
if (Sh4RegList[i] == reg_sr_status)
allregs[i] = sh4_sr_GetFull();
allregs[i] = Sh4cntx.sr.getFull();
else if (Sh4RegList[i] != NoReg)
allregs[i] = *GetRegPtr(Sh4RegList[i]);
allregs[i] = *GetRegPtr(Sh4cntx, Sh4RegList[i]);
}
*regs = &allregs[0];
return allregs.size();
@@ -134,7 +134,7 @@ public:
{
for (u32 i = 0; i < Sh4RegList.size(); i++)
if (Sh4RegList[i] != NoReg)
*GetRegPtr(Sh4RegList[i]) = regs[i];
*GetRegPtr(Sh4cntx, Sh4RegList[i]) = regs[i];
}
u32 readReg(u32 regNum)
@@ -143,9 +143,9 @@ public:
return 0;
Sh4RegType reg = Sh4RegList[regNum];
if (reg == reg_sr_status)
return sh4_sr_GetFull();
return Sh4cntx.sr.getFull();
if (reg != NoReg)
return *GetRegPtr(reg);
return *GetRegPtr(Sh4cntx, reg);
return 0;
}
void writeReg(u32 regNum, u32 value)
@@ -154,9 +154,9 @@ public:
return;
Sh4RegType reg = Sh4RegList[regNum];
if (reg == reg_sr_status)
sh4_sr_SetFull(value);
Sh4cntx.sr.setFull(value);
else if (reg != NoReg)
*GetRegPtr(reg) = value;
*GetRegPtr(Sh4cntx, reg) = value;
}
const u8 *readMem(u32 addr, u32 len)
+403 -390
View File
File diff suppressed because it is too large. Load diff
+1
Submodule core/deps/asio added at d3402006e8.
+1 -1
View File
@@ -56,7 +56,7 @@ public:
*/
#if defined(_WIN32)
# include "platform_windows.h"
#elif defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__)
#elif defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__) || defined(__HAIKU__)
# include "platform_linux.h"
#else
# error Unsupported platform
+1 -1
View File
@@ -4,7 +4,7 @@
* Use of this software is governed by the MIT license that can be found
* in the LICENSE file.
*/
#if defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__)
#if defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__) || defined(__HAIKU__)
#include "platform_linux.h"
#include <time.h>
+1 -1
View File
@@ -13,7 +13,7 @@
template<class T, int N> class RingBuffer
{
public:
RingBuffer<T, N>() :
RingBuffer() :
_head(0),
_tail(0),
_size(0) {
+1 -1
View File
@@ -13,7 +13,7 @@
template<class T, int N> class StaticBuffer
{
public:
StaticBuffer<T, N>() :
StaticBuffer() :
_size(0) {
}
+2 -2
View File
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
cmake_policy(VERSION 3.2)
cmake_minimum_required(VERSION 3.6 FATAL_ERROR)
cmake_policy(VERSION 3.6)
set(GLM_VERSION "0.9.9")
project(glm VERSION ${GLM_VERSION} LANGUAGES CXX)
@@ -222,7 +222,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
glcache.Scissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT)
// Bind texture, Draw
glcache.BindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
glcache.BindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID());
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)));
}
}
@@ -255,7 +255,7 @@ static bool ImGui_ImplOpenGL3_CreateFontsTexture()
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Store our identifier
io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture;
io.Fonts->SetTexID((ImTextureID)g_FontTexture);
return true;
}
@@ -266,7 +266,6 @@ static void ImGui_ImplOpenGL3_DestroyFontsTexture()
{
ImGuiIO& io = ImGui::GetIO();
glcache.DeleteTextures(1, &g_FontTexture);
io.Fonts->TexID = 0;
g_FontTexture = 0;
}
}
@@ -586,14 +586,14 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
// Bind DescriptorSet with font or user texture
VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->TextureId };
VkDescriptorSet desc_set = (VkDescriptorSet)pcmd->GetTexID();
if (sizeof(ImTextureID) < sizeof(ImU64))
{
// We don't support texture switches if ImTextureID hasn't been redefined to be 64-bit. Do a flaky check that other textures haven't been used.
IM_ASSERT(pcmd->TextureId == (ImTextureID)bd->FontDescriptorSet);
desc_set[0] = bd->FontDescriptorSet;
IM_ASSERT(pcmd->GetTexID() == (ImTextureID)bd->FontDescriptorSet);
desc_set = bd->FontDescriptorSet;
}
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, desc_set, 0, nullptr);
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, &desc_set, 0, nullptr);
// Draw
vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
@@ -820,7 +820,6 @@ void ImGui_ImplVulkan_DestroyFontsTexture()
{
ImGui_ImplVulkan_RemoveTexture(bd->FontDescriptorSet);
bd->FontDescriptorSet = VK_NULL_HANDLE;
io.Fonts->SetTexID(0);
}
if (bd->FontView) { vkDestroyImageView(v->Device, bd->FontView, v->Allocator); bd->FontView = VK_NULL_HANDLE; }
Regular → Executable
+26 -10
View File
@@ -15,20 +15,21 @@
#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
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
//#define IMGUI_API __declspec( dllexport )
//#define IMGUI_API __declspec( dllimport )
// - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
//#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export
//#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import
//#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
@@ -42,21 +43,29 @@
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded font (ProggyClean.ttf), remove ~9.5 KB from output binary. AddFontDefault() will assert.
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
//---- Enable Test Engine / Automation features.
//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details.
//---- Include imgui_user.h at the end of imgui.h as a convenience
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
//#define IMGUI_INCLUDE_IMGUI_USER_H
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
//---- Pack vertex colors as BGRA8 instead of RGBA8 (to avoid converting from one to another). Need dedicated backend support.
//#define IMGUI_USE_BGRA_PACKED_COLOR
//---- Use legacy CRC32-adler tables (used before 1.91.6), in order to preserve old .ini data that you cannot afford to invalidate.
//#define IMGUI_USE_LEGACY_CRC32_ADLER
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
//#define IMGUI_USE_WCHAR32
@@ -75,13 +84,16 @@
//---- 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
//---- Use FreeType+lunasvg library to render OpenType SVG fonts (SVGinOT)
// Requires lunasvg headers to be available in the include path + program to be linked with the lunasvg library (not provided).
//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
// Only works in combination with IMGUI_ENABLE_FREETYPE.
// (implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
// - plutosvg is currently easier to install, as e.g. it is part of vcpkg. It will support more fonts and may load them faster. See misc/freetype/README for instructions.
// - Both require headers to be available in the include path + program to be linked with the library code (not provided).
// - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG
//#define IMGUI_ENABLE_FREETYPE_LUNASVG
//---- Use stb_truetype to build and rasterize the font atlas (default)
@@ -119,6 +131,10 @@
//#define IM_DEBUG_BREAK IM_ASSERT(0)
//#define IM_DEBUG_BREAK __debugbreak()
//---- Debug Tools: Enable highlight ID conflicts _before_ hovering items. When io.ConfigDebugHighlightIdConflicts is set.
// (THIS WILL SLOW DOWN DEAR IMGUI. Only use occasionally and disable after use)
//#define IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS
//---- Debug Tools: Enable slower asserts
//#define IMGUI_DEBUG_PARANOID
+4017 -1973
View File
File diff suppressed because it is too large. Load diff
+1309 -516
View File
File diff suppressed because it is too large. Load diff
Regular → Executable
+4078 -1949
View File
File diff suppressed because it is too large. Load diff
Regular → Executable
+2826 -1106
View File
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+16 -1
View File
@@ -1,13 +1,26 @@
// 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"
#ifndef IMGUI_DISABLE
#include "imgui_stdlib.h"
// Clang warnings with -Weverything
@@ -83,3 +96,5 @@ bool ImGui::InputTextWithHint(const char* label, const char* hint, std::string*
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#endif // #ifndef IMGUI_DISABLE
+16
View File
@@ -1,14 +1,28 @@
// 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
#pragma once
#ifndef IMGUI_DISABLE
#include <string>
namespace ImGui
@@ -19,3 +33,5 @@ namespace ImGui
IMGUI_API bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr);
IMGUI_API bool InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr);
}
#endif // #ifndef IMGUI_DISABLE
Regular → Executable
+244 -123
View File
@@ -1,4 +1,4 @@
// dear imgui, v1.90.8
// 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
@@ -221,6 +221,7 @@ Index of this file:
#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.
#pragma clang diagnostic ignored "-Wformat" // warning: format specifies type 'int' but the argument has type 'unsigned int'
#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
@@ -229,9 +230,15 @@ Index of this file:
#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
#pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
#pragma clang diagnostic ignored "-Wswitch-default" // warning: 'switch' missing 'default' label
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe
#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
#pragma GCC diagnostic ignored "-Wstrict-overflow"
#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
#endif
@@ -328,13 +335,14 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
// - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[].
const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0;
const ImVec2 avail_size = GetContentRegionAvail();
const ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f);
const ImVec2 actual_outer_size = ImTrunc(CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f));
const ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size);
const bool outer_window_is_measuring_size = (outer_window->AutoFitFramesX > 0) || (outer_window->AutoFitFramesY > 0); // Doesn't apply to AlwaysAutoResize windows!
if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size)
{
ItemSize(outer_rect);
ItemAdd(outer_rect, id);
g.NextWindowData.ClearFlags();
return false;
}
@@ -369,6 +377,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
table->ColumnsCount = columns_count;
table->IsLayoutLocked = false;
table->InnerWidth = inner_width;
table->NavLayer = (ImS8)outer_window->DC.NavLayerCurrent;
temp_data->UserOuterSize = outer_size;
// Instance data (for instance 0, TableID == TableInstanceID)
@@ -409,11 +418,15 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
// Reset scroll if we are reactivating it
if ((previous_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0)
SetNextWindowScroll(ImVec2(0.0f, 0.0f));
if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasScroll) == 0)
SetNextWindowScroll(ImVec2(0.0f, 0.0f));
// Create scrolling region (without border and zero window padding)
ImGuiWindowFlags child_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None;
BeginChildEx(name, instance_id, outer_rect.GetSize(), false, child_flags);
ImGuiChildFlags child_child_flags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : ImGuiChildFlags_None;
ImGuiWindowFlags child_window_flags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasWindowFlags) ? g.NextWindowData.WindowFlags : ImGuiWindowFlags_None;
if (flags & ImGuiTableFlags_ScrollX)
child_window_flags |= ImGuiWindowFlags_HorizontalScrollbar;
BeginChildEx(name, instance_id, outer_rect.GetSize(), child_child_flags, child_window_flags);
table->InnerWindow = g.CurrentWindow;
table->WorkRect = table->InnerWindow->WorkRect;
table->OuterRect = table->InnerWindow->Rect();
@@ -424,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;
@@ -438,6 +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 across a table
}
// Push a standardized ID for both child-using and not-child-using tables
@@ -450,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;
@@ -460,16 +475,27 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size;
inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
// Make left and top borders not overlap our contents by offsetting HostClipRect (#6765)
// Make borders not overlap our contents by offsetting HostClipRect (#6765, #7428, #3752)
// (we normally shouldn't alter HostClipRect as we rely on TableMergeDrawChannels() expanding non-clipped column toward the
// limits of that rectangle, in order for ImDrawListSplitter::Merge() to merge the draw commands. However since the overlap
// problem only affect scrolling tables in this case we can get away with doing it without extra cost).
if (inner_window != outer_window)
{
// FIXME: Because inner_window's Scrollbar doesn't know about border size, since it's not encoded in window->WindowBorderSize,
// it already overlaps it and doesn't need an extra offset. Ideally we should be able to pass custom border size with
// different x/y values to BeginChild().
if (flags & ImGuiTableFlags_BordersOuterV)
{
table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + TABLE_BORDER_SIZE, table->HostClipRect.Max.x);
if (inner_window->DecoOuterSizeX2 == 0.0f)
table->HostClipRect.Max.x = ImMax(table->HostClipRect.Max.x - TABLE_BORDER_SIZE, table->HostClipRect.Min.x);
}
if (flags & ImGuiTableFlags_BordersOuterH)
{
table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + TABLE_BORDER_SIZE, table->HostClipRect.Max.y);
if (inner_window->DecoOuterSizeY2 == 0.0f)
table->HostClipRect.Max.y = ImMax(table->HostClipRect.Max.y - TABLE_BORDER_SIZE, table->HostClipRect.Min.y);
}
}
// Padding and Spacing
@@ -497,7 +523,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
table->InnerClipRect = (inner_window == outer_window) ? table->WorkRect : inner_window->ClipRect;
table->InnerClipRect.ClipWith(table->WorkRect); // We need this to honor inner_width
table->InnerClipRect.ClipWithFull(table->HostClipRect);
table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : inner_window->ClipRect.Max.y;
table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : table->HostClipRect.Max.y;
table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow
table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow()
@@ -517,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;
@@ -555,6 +581,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
// Initialize
table->SettingsOffset = -1;
table->IsSortSpecsDirty = true;
table->IsSettingsDirty = true; // Records itself into .ini file even when in default state (#7934)
table->InstanceInteracted = -1;
table->ContextPopupColumn = -1;
table->ReorderColumn = table->ResizedColumn = table->LastResizedColumn = -1;
@@ -855,7 +882,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// Calculate ideal/auto column width (that's the width required for all contents to be visible without clipping)
// Combine width from regular rows + width from headers unless requested not to.
if (!column->IsPreserveWidthAuto)
if (!column->IsPreserveWidthAuto && table->InstanceCurrent == 0)
column->WidthAuto = TableGetColumnWidthAuto(table, column);
// Non-resizable columns keep their requested width (apply user value regardless of IsPreserveWidthAuto)
@@ -920,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?
@@ -954,7 +981,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// [Part 4] Apply final widths based on requested widths
const ImRect work_rect = table->WorkRect;
const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1);
const float width_removed = (table->HasScrollbarYPrev && !table->InnerWindow->ScrollbarY) ? g.Style.ScrollbarSize : 0.0f; // To synchronize decoration width of synched tables with mismatching scrollbar state (#5920)
const float width_removed = (table->HasScrollbarYPrev && !table->InnerWindow->ScrollbarY) ? g.Style.ScrollbarSize : 0.0f; // To synchronize decoration width of synced tables with mismatching scrollbar state (#5920)
const float width_avail = ImMax(1.0f, (((table->Flags & ImGuiTableFlags_ScrollX) && table->InnerWidth == 0.0f) ? table->InnerClipRect.GetWidth() : work_rect.GetWidth()) - width_removed);
const float width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests;
float width_remaining_for_stretched_columns = width_avail_for_stretched_columns;
@@ -1033,7 +1060,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
const int column_n = table->DisplayOrderToIndex[order_n];
ImGuiTableColumn* column = &table->Columns[column_n];
column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main); // Use Count NOT request so Header line changes layer when frozen
// Initial nav layer: using FreezeRowsCount, NOT FreezeRowsRequest, so Header line changes layer when frozen
column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : (ImGuiNavLayer)table->NavLayer);
if (offset_x_frozen && table->FreezeColumnsCount == visible_n)
{
@@ -1059,16 +1087,12 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
continue;
}
// Detect hovered column
if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x)
table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n;
// Lock start position
column->MinX = offset_x;
// Lock width based on start position and minimum/maximum width for this position
float max_width = TableGetMaxColumnWidth(table, column_n);
column->WidthGiven = ImMin(column->WidthGiven, max_width);
column->WidthMax = TableCalcMaxColumnWidth(table, column_n);
column->WidthGiven = ImMin(column->WidthGiven, column->WidthMax);
column->WidthGiven = ImMax(column->WidthGiven, ImMin(column->WidthRequest, table->MinColumnWidth));
column->MaxX = offset_x + column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f;
@@ -1117,8 +1141,13 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
column->Flags |= ImGuiTableColumnFlags_IsVisible;
if (column->SortOrder != -1)
column->Flags |= ImGuiTableColumnFlags_IsSorted;
if (table->HoveredColumnBody == column_n)
// Detect hovered column
if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x)
{
column->Flags |= ImGuiTableColumnFlags_IsHovered;
table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n;
}
// Alignment
// FIXME-TABLE: This align based on the whole column width, not per-cell, and therefore isn't useful in
@@ -1148,7 +1177,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
}
// Don't decrement auto-fit counters until container window got a chance to submit its items
if (table->HostSkipItems == false)
if (table->HostSkipItems == false && table->InstanceCurrent == 0)
{
column->AutoFitQueue >>= 1;
column->CannotSkipItemsQueue >>= 1;
@@ -1162,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;
@@ -1223,7 +1252,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// [Part 11] Default context menu
// - To append to this menu: you can call TableBeginContextMenuPopup()/.../EndPopup().
// - To modify or replace this: set table->IsContextPopupNoDefaultContents = true, then call TableBeginContextMenuPopup()/.../EndPopup().
// - To modify or replace this: set table->DisableDefaultContextMenu = true, then call TableBeginContextMenuPopup()/.../EndPopup().
// - You may call TableDrawDefaultContextMenu() with selected flags to display specific sections of the default menu,
// e.g. TableDrawDefaultContextMenu(table, table->Flags & ~ImGuiTableFlags_Hideable) will display everything EXCEPT columns visibility options.
if (table->DisableDefaultContextMenu == false && TableBeginContextMenuPopup(table))
@@ -1249,7 +1278,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
if (table->Flags & ImGuiTableFlags_NoClip)
table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP);
else
inner_window->DrawList->PushClipRect(inner_window->ClipRect.Min, inner_window->ClipRect.Max, false);
inner_window->DrawList->PushClipRect(inner_window->InnerClipRect.Min, inner_window->InnerClipRect.Max, false); // FIXME: use table->InnerClipRect?
}
// Process hit-testing on resizing borders. Actual size change will be applied in EndTable()
@@ -1319,7 +1348,11 @@ void ImGui::EndTable()
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL && "Only call EndTable() if BeginTable() returns true!");
if (table == NULL)
{
IM_ASSERT_USER_ERROR(table != NULL, "EndTable() call should only be done while in BeginTable() scope!");
return;
}
// This assert would be very useful to catch a common error... unfortunately it would probably trigger in some
// cases, and for consistency user may sometimes output empty tables (and still benefit from e.g. outer border)
@@ -1334,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)
@@ -1361,7 +1394,7 @@ void ImGui::EndTable()
// Setup inner scrolling range
// FIXME: This ideally should be done earlier, in BeginTable() SetNextWindowContentSize call, just like writing to inner_window->DC.CursorMaxPos.y,
// but since the later is likely to be impossible to do we'd rather update both axises together.
// but since the later is likely to be impossible to do we'd rather update both axes together.
if (table->Flags & ImGuiTableFlags_ScrollX)
{
const float outer_padding_for_border = (table->Flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f;
@@ -1471,12 +1504,15 @@ void ImGui::EndTable()
if (inner_window != outer_window)
{
short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask;
inner_window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; // So empty table don't appear to navigate differently.
inner_window->DC.NavLayersActiveMask |= 1 << table->NavLayer; // So empty table don't appear to navigate differently.
g.CurrentTable = NULL; // To avoid error recovery recursing
EndChild();
g.CurrentTable = table;
inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask;
}
else
{
table->InnerWindow->DC.TreeDepth--;
ItemSize(table->OuterRect.GetSize());
ItemAdd(table->OuterRect, 0);
}
@@ -1524,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;
@@ -1534,14 +1570,43 @@ void ImGui::EndTable()
NavUpdateCurrentWindowIsScrollPushableX();
}
// Called in TableSetupColumn() when initializing and in TableLoadSettings() for defaults before applying stored settings.
// 'init_mask' specify which fields to initialize.
static void TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags init_mask)
{
ImGuiTableColumnFlags flags = column->Flags;
if (init_mask & ImGuiTableFlags_Resizable)
{
float init_width_or_weight = column->InitStretchWeightOrWidth;
column->WidthRequest = ((flags & ImGuiTableColumnFlags_WidthFixed) && init_width_or_weight > 0.0f) ? init_width_or_weight : -1.0f;
column->StretchWeight = (init_width_or_weight > 0.0f && (flags & ImGuiTableColumnFlags_WidthStretch)) ? init_width_or_weight : -1.0f;
if (init_width_or_weight > 0.0f) // Disable auto-fit if an explicit width/weight has been specified
column->AutoFitQueue = 0x00;
}
if (init_mask & ImGuiTableFlags_Reorderable)
column->DisplayOrder = (ImGuiTableColumnIdx)table->Columns.index_from_ptr(column);
if (init_mask & ImGuiTableFlags_Hideable)
column->IsUserEnabled = column->IsUserEnabledNextFrame = (flags & ImGuiTableColumnFlags_DefaultHide) ? 0 : 1;
if (init_mask & ImGuiTableFlags_Sortable)
{
// Multiple columns using _DefaultSort will be reassigned unique SortOrder values when building the sort specs.
column->SortOrder = (flags & ImGuiTableColumnFlags_DefaultSort) ? 0 : -1;
column->SortDirection = (flags & ImGuiTableColumnFlags_DefaultSort) ? ((flags & ImGuiTableColumnFlags_PreferSortDescending) ? (ImS8)ImGuiSortDirection_Descending : (ImU8)(ImGuiSortDirection_Ascending)) : (ImS8)ImGuiSortDirection_None;
}
}
// See "COLUMNS SIZING POLICIES" comments at the top of this file
// If (init_width_or_weight <= 0.0f) it is ignored
void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_id)
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL && "Need to call TableSetupColumn() after BeginTable()!");
IM_ASSERT(table->IsLayoutLocked == false && "Need to call call TableSetupColumn() before first row!");
if (table == NULL)
{
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
return;
}
IM_ASSERT(table->IsLayoutLocked == false && "Need to call TableSetupColumn() before first row!");
IM_ASSERT((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && "Illegal to pass StatusMask values to TableSetupColumn()");
if (table->DeclColumnsCount >= table->ColumnsCount)
{
@@ -1558,7 +1623,7 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
IM_ASSERT(init_width_or_weight <= 0.0f && "Can only specify width/weight if sizing policy is set explicitly in either Table or Column.");
// When passing a width automatically enforce WidthFixed policy
// (whereas TableSetupColumnFlags would default to WidthAuto if table is not Resizable)
// (whereas TableSetupColumnFlags would default to WidthAuto if table is not resizable)
if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0 && init_width_or_weight > 0.0f)
if ((table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedFit || (table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame)
flags |= ImGuiTableColumnFlags_WidthFixed;
@@ -1576,27 +1641,10 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
column->InitStretchWeightOrWidth = init_width_or_weight;
if (table->IsInitializing)
{
// Init width or weight
ImGuiTableFlags init_flags = ~table->SettingsLoadedFlags;
if (column->WidthRequest < 0.0f && column->StretchWeight < 0.0f)
{
if ((flags & ImGuiTableColumnFlags_WidthFixed) && init_width_or_weight > 0.0f)
column->WidthRequest = init_width_or_weight;
if (flags & ImGuiTableColumnFlags_WidthStretch)
column->StretchWeight = (init_width_or_weight > 0.0f) ? init_width_or_weight : -1.0f;
// Disable auto-fit if an explicit width/weight has been specified
if (init_width_or_weight > 0.0f)
column->AutoFitQueue = 0x00;
}
// Init default visibility/sort state
if ((flags & ImGuiTableColumnFlags_DefaultHide) && (table->SettingsLoadedFlags & ImGuiTableFlags_Hideable) == 0)
column->IsUserEnabled = column->IsUserEnabledNextFrame = false;
if (flags & ImGuiTableColumnFlags_DefaultSort && (table->SettingsLoadedFlags & ImGuiTableFlags_Sortable) == 0)
{
column->SortOrder = 0; // Multiple columns using _DefaultSort will be reassigned unique SortOrder values when building the sort specs.
column->SortDirection = (column->Flags & ImGuiTableColumnFlags_PreferSortDescending) ? (ImS8)ImGuiSortDirection_Descending : (ImU8)(ImGuiSortDirection_Ascending);
}
init_flags |= ImGuiTableFlags_Resizable;
TableInitColumnDefaults(table, column, init_flags);
}
// Store name (append with zero-terminator in contiguous buffer)
@@ -1605,7 +1653,7 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
if (label != NULL && label[0] != 0)
{
column->NameOffset = (ImS16)table->ColumnsNames.size();
table->ColumnsNames.append(label, label + strlen(label) + 1);
table->ColumnsNames.append(label, label + ImStrlen(label) + 1);
}
}
@@ -1614,7 +1662,11 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows)
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL && "Need to call TableSetupColumn() after BeginTable()!");
if (table == NULL)
{
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
return;
}
IM_ASSERT(table->IsLayoutLocked == false && "Need to call TableSetupColumn() before first row!");
IM_ASSERT(columns >= 0 && columns < IMGUI_TABLE_MAX_COLUMNS);
IM_ASSERT(rows >= 0 && rows < 128); // Arbitrary limit
@@ -1691,9 +1743,11 @@ void ImGui::TableSetColumnEnabled(int column_n, bool enabled)
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL);
if (!table)
if (table == NULL)
{
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
return;
}
IM_ASSERT(table->Flags & ImGuiTableFlags_Hideable); // See comments above
if (column_n < 0)
column_n = table->CurrentColumn;
@@ -1772,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;
@@ -1900,7 +1959,10 @@ void ImGui::TableEndRow(ImGuiTable* table)
IM_ASSERT(table->IsInsideRow);
if (table->CurrentColumn != -1)
{
TableEndCell(table);
table->CurrentColumn = -1;
}
// Logging
if (g.LogEnabled)
@@ -1993,37 +2055,41 @@ 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)
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main;
if (unfreeze_rows_actual)
{
IM_ASSERT(table->IsUnfrozenRows == false);
const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y);
table->IsUnfrozenRows = true;
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);
table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y;
// BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y);
table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y;
table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;
IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y);
float row_height = table->RowPosY2 - table->RowPosY1;
table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y;
table->RowPosY1 = table->RowPosY2 - row_height;
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
if (unfreeze_rows_actual)
{
ImGuiTableColumn* column = &table->Columns[column_n];
column->DrawChannelCurrent = column->DrawChannelUnfrozen;
column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y;
}
IM_ASSERT(table->IsUnfrozenRows == false);
table->IsUnfrozenRows = true;
// Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y
SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect);
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent);
// BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, table->InnerClipRect.Max.y);
table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = table->InnerClipRect.Max.y;
table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;
IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y);
float row_height = table->RowPosY2 - table->RowPosY1;
table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y;
table->RowPosY1 = table->RowPosY2 - row_height;
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{
ImGuiTableColumn* column = &table->Columns[column_n];
column->DrawChannelCurrent = column->DrawChannelUnfrozen;
column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y;
}
// Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y
SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect);
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent);
}
}
if (!(table->RowFlags & ImGuiTableRowFlags_Headers))
@@ -2062,7 +2128,11 @@ bool ImGui::TableSetColumnIndex(int column_n)
{
if (table->CurrentColumn != -1)
TableEndCell(table);
IM_ASSERT(column_n >= 0 && table->ColumnsCount);
if ((column_n >= 0 && column_n < table->ColumnsCount) == false)
{
IM_ASSERT_USER_ERROR(column_n >= 0 && column_n < table->ColumnsCount, "TableSetColumnIndex() invalid column index!");
return false;
}
TableBeginCell(table, column_n);
}
@@ -2133,6 +2203,7 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n)
g.LastItemData.StatusFlags = 0;
}
// Also see TablePushColumnChannel()
if (table->Flags & ImGuiTableFlags_NoClip)
{
// FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed.
@@ -2192,8 +2263,8 @@ void ImGui::TableEndCell(ImGuiTable* table)
// Note that actual columns widths are computed in TableUpdateLayout().
//-------------------------------------------------------------------------
// Maximum column content width given current layout. Use column->MinX so this value on a per-column basis.
float ImGui::TableGetMaxColumnWidth(const ImGuiTable* table, int column_n)
// Maximum column content width given current layout. Use column->MinX so this value differs on a per-column basis.
float ImGui::TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n)
{
const ImGuiTableColumn* column = &table->Columns[column_n];
float max_width = FLT_MAX;
@@ -2255,7 +2326,7 @@ void ImGui::TableSetColumnWidth(int column_n, float width)
// Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded)
IM_ASSERT(table->MinColumnWidth > 0.0f);
const float min_width = table->MinColumnWidth;
const float max_width = ImMax(min_width, TableGetMaxColumnWidth(table, column_n));
const float max_width = ImMax(min_width, column_0->WidthMax); // Don't use TableCalcMaxColumnWidth() here as it would rely on MinX from last instance (#7933)
column_0_width = ImClamp(column_0_width, min_width, max_width);
if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width)
return;
@@ -2406,10 +2477,38 @@ void ImGui::TablePopBackgroundChannel()
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImGuiTable* table = g.CurrentTable;
ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
// Optimization: avoid PopClipRect() + SetCurrentChannel()
SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect);
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[table->CurrentColumn].DrawChannelCurrent);
}
// Also see TableBeginCell()
void ImGui::TablePushColumnChannel(int column_n)
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
// Optimization: avoid SetCurrentChannel() + PushClipRect()
if (table->Flags & ImGuiTableFlags_NoClip)
return;
ImGuiWindow* window = g.CurrentWindow;
const ImGuiTableColumn* column = &table->Columns[column_n];
SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
}
void ImGui::TablePopColumnChannel()
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
// Optimization: avoid PopClipRect() + SetCurrentChannel()
if ((table->Flags & ImGuiTableFlags_NoClip) || (table->CurrentColumn == -1)) // Calling TreePop() after TableNextRow() is supported.
return;
ImGuiWindow* window = g.CurrentWindow;
const ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
}
@@ -2427,7 +2526,7 @@ void ImGui::TablePopBackgroundChannel()
// - 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)
{
@@ -2720,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);
}
@@ -2740,7 +2844,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
const ImU32 outer_col = table->BorderColorStrong;
if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter)
{
inner_drawlist->AddRect(outer_border.Min, outer_border.Max + ImVec2(1, 1), outer_col, 0.0f, 0, border_size);
inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, 0, border_size);
}
else if (table->Flags & ImGuiTableFlags_BordersOuterV)
{
@@ -2784,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.
@@ -3004,15 +3106,21 @@ float ImGui::TableGetHeaderAngledMaxLabelWidth()
// The intent is that advanced users willing to create customized headers would not need to use this helper
// and can create their own! For example: TableHeader() may be preceded by Checkbox() or other custom widgets.
// See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this.
// This code is constructed to not make much use of internal functions, as it is intended to be a template to copy.
// This code is intentionally written to not make much use of internal functions, to give you better direction
// if you need to write your own.
// FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public.
void ImGui::TableHeadersRow()
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!");
if (table == NULL)
{
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
return;
}
// Layout if not already done (this is automatically done by TableNextRow, we do it here solely to facilitate stepping in debugger as it is frequent to step in TableUpdateLayout)
// Call layout if not already done. This is automatically done by TableNextRow: we do it here _only_ to make
// it easier to debug-step in TableUpdateLayout(). Your own version of this function doesn't need this.
if (!table->IsLayoutLocked)
TableUpdateLayout(table);
@@ -3029,8 +3137,7 @@ void ImGui::TableHeadersRow()
if (!TableSetColumnIndex(column_n))
continue;
// Push an id to allow unnamed labels (generally accidental, but let's behave nicely with them)
// In your own code you may omit the PushID/PopID all-together, provided you know they won't collide.
// Push an id to allow empty/unnamed headers. This is also idiomatic as it ensure there is a consistent ID path to access columns (for e.g. automation)
const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? "" : TableGetColumnName(column_n);
PushID(column_n);
TableHeader(name);
@@ -3055,7 +3162,12 @@ void ImGui::TableHeader(const char* label)
return;
ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL && "Need to call TableHeader() after BeginTable()!");
if (table == NULL)
{
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
return;
}
IM_ASSERT(table->CurrentColumn != -1);
const int column_n = table->CurrentColumn;
ImGuiTableColumn* column = &table->Columns[column_n];
@@ -3121,7 +3233,7 @@ void ImGui::TableHeader(const char* label)
if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0)
TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn);
}
RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_Compact | ImGuiNavHighlightFlags_NoRounding);
RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding);
if (held)
table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n;
window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f;
@@ -3176,7 +3288,7 @@ void ImGui::TableHeader(const char* label)
// Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will
// be merged into a single draw call.
//window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE);
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size);
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, bb.Max.y), ellipsis_max, label, label_end, &label_size);
const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);
if (text_clipped && hovered && g.ActiveId == 0)
@@ -3230,7 +3342,11 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
ImGuiTable* table = g.CurrentTable;
ImGuiWindow* window = g.CurrentWindow;
ImDrawList* draw_list = window->DrawList;
IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!");
if (table == NULL)
{
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
return;
}
IM_ASSERT(table->CurrentRow == -1 && "Must be first row");
if (max_label_width == 0.0f)
@@ -3269,13 +3385,13 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
ButtonBehavior(row_r, row_id, NULL, NULL);
KeepAliveID(row_id);
const float ascent_scaled = g.Font->Ascent * (g.FontSize / g.Font->FontSize); // FIXME: Standardize those scaling factors better
const float ascent_scaled = g.FontBaked->Ascent * g.FontBakedScale; // FIXME: Standardize those scaling factors better
const float line_off_for_ascent_x = (ImMax((g.FontSize - ascent_scaled) * 0.5f, 0.0f) / -sin_a) * (flip_label ? -1.0f : 1.0f);
const ImVec2 padding = g.Style.CellPadding; // We will always use swapped component
const ImVec2 align = g.Style.TableAngledHeadersTextAlign;
// Draw background and labels in first pass, then all borders.
float max_x = 0.0f;
float max_x = -FLT_MAX;
for (int pass = 0; pass < 2; pass++)
for (int order_n = 0; order_n < data_count; order_n++)
{
@@ -3305,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
@@ -3324,7 +3440,7 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height));
int vtx_idx_begin = draw_list->_VtxCurrentIdx;
PushStyleColor(ImGuiCol_Text, request->TextColor);
RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, clip_r.Max.x, label_name, label_name_eol, &label_size);
RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, label_name, label_name_eol, &label_size);
PopStyleColor();
int vtx_idx_end = draw_list->_VtxCurrentIdx;
@@ -3471,7 +3587,7 @@ void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags
Separator();
want_separator = true;
PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true);
PushItemFlag(ImGuiItemFlags_AutoClosePopups, false);
for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++)
{
ImGuiTableColumn* other_column = &table->Columns[other_column_n];
@@ -3661,6 +3777,14 @@ void ImGui::TableLoadSettings(ImGuiTable* table)
table->SettingsLoadedFlags = settings->SaveFlags;
table->RefScale = settings->RefScale;
// Initialize default columns settings
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{
ImGuiTableColumn* column = &table->Columns[column_n];
TableInitColumnDefaults(table, column, ~0);
column->AutoFitQueue = 0x00;
}
// Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn
ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings();
ImU64 display_order_mask = 0;
@@ -3677,14 +3801,12 @@ void ImGui::TableLoadSettings(ImGuiTable* table)
column->StretchWeight = column_settings->WidthOrWeight;
else
column->WidthRequest = column_settings->WidthOrWeight;
column->AutoFitQueue = 0x00;
}
if (settings->SaveFlags & ImGuiTableFlags_Reorderable)
column->DisplayOrder = column_settings->DisplayOrder;
else
column->DisplayOrder = (ImGuiTableColumnIdx)column_n;
display_order_mask |= (ImU64)1 << column->DisplayOrder;
column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled;
if ((settings->SaveFlags & ImGuiTableFlags_Hideable) && column_settings->IsEnabled != -1)
column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled == 1;
column->SortOrder = column_settings->SortOrder;
column->SortDirection = column_settings->SortDirection;
}
@@ -3780,8 +3902,7 @@ static void TableSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandle
const bool save_visible = (settings->SaveFlags & ImGuiTableFlags_Hideable) != 0;
const bool save_order = (settings->SaveFlags & ImGuiTableFlags_Reorderable) != 0;
const bool save_sort = (settings->SaveFlags & ImGuiTableFlags_Sortable) != 0;
if (!save_size && !save_visible && !save_order && !save_sort)
continue;
// We need to save the [Table] entry even if all the bools are false, since this records a table with "default settings".
buf->reserve(buf->size() + 30 + settings->ColumnsCount * 50); // ballpark reserve
buf->appendf("[%s][0x%08X,%d]\n", handler->TypeName, settings->ID, settings->ColumnsCount);
@@ -3828,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);
@@ -3907,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)
@@ -3963,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))
@@ -4393,7 +4514,7 @@ void ImGui::EndColumns()
{
ButtonBehavior(column_hit_rect, column_id, &hovered, &held);
if (hovered || held)
g.MouseCursor = ImGuiMouseCursor_ResizeEW;
SetMouseCursor(ImGuiMouseCursor_ResizeEW);
if (held && !(column->Flags & ImGuiOldColumnFlags_NoResize))
dragging_column = n;
}
@@ -4425,12 +4546,12 @@ void ImGui::EndColumns()
NavUpdateCurrentWindowIsScrollPushableX();
}
void ImGui::Columns(int columns_count, const char* id, bool border)
void ImGui::Columns(int columns_count, const char* id, bool borders)
{
ImGuiWindow* window = GetCurrentWindow();
IM_ASSERT(columns_count >= 1);
ImGuiOldColumnFlags flags = (border ? 0 : ImGuiOldColumnFlags_NoBorder);
ImGuiOldColumnFlags flags = (borders ? 0 : ImGuiOldColumnFlags_NoBorder);
//flags |= ImGuiOldColumnFlags_NoPreserveWidths; // NB: Legacy behavior
ImGuiOldColumns* columns = window->DC.CurrentColumns;
if (columns != NULL && columns->Count == columns_count && columns->Flags == flags)
File diff suppressed because it is too large. Load diff
Regular → Executable
View File
File mode changed.
Regular → Executable
+160 -74
View File
@@ -3,7 +3,10 @@
// Those changes would need to be pushed into nothings/stb:
// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
// Grep for [DEAR IMGUI] to find the changes.
// - 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)
// - 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
@@ -37,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
@@ -139,12 +143,14 @@
// with previous char)
// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character
// (return type is int, -1 means not valid to insert)
// (not supported if you want to use UTF-8, see below)
// STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based
// STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize
// 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
//
@@ -176,6 +182,13 @@
// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text
// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text
//
// To support UTF-8:
//
// 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.
//
// 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
// be a bitflag, so we can test the shifted state of cursor movements to allow selection,
@@ -209,6 +222,7 @@
// int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
// int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key)
// void stb_textedit_text(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int text_len)
//
// Each of these functions potentially updates the string and updates the
// state.
@@ -243,7 +257,14 @@
// various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit
// set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is
// clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to
// anything other type you wante before including.
// anything other type you want before including.
// if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are
// transformed into text and stb_textedit_text() is automatically called.
//
// text: (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()
// cannot infer text length.
//
//
// When rendering, you can read the cursor position and selection state from
@@ -318,7 +339,7 @@ typedef struct
int undo_char_point, redo_char_point;
} StbUndoState;
typedef struct
typedef struct STB_TexteditState
{
/////////////////////
//
@@ -392,6 +413,16 @@ typedef struct
#define IMSTB_TEXTEDIT_memmove memmove
#endif
// [DEAR IMGUI]
// Functions must be implemented for UTF8 support
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(OBJ, IDX) ((IDX) - 1)
#endif
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(OBJ, IDX) ((IDX) + 1)
#endif
/////////////////////////////////////////////////////////////////////////////
//
@@ -399,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);
@@ -409,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) {
@@ -428,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)
@@ -438,13 +473,14 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
if (x < r.x1) {
// search characters in row for one that straddles 'x'
prev_x = r.x0;
for (k=0; k < r.num_chars; ++k) {
for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) {
float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
if (x < prev_x+w) {
*out_side_on_line = (k == 0) ? 0 : 1;
if (x < prev_x+w/2)
return k+i;
else
return k+i+1;
return IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k);
}
prev_x += w;
}
@@ -452,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
@@ -463,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;
@@ -470,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
@@ -493,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);
}
/////////////////////////////////////////////////////////////////////////////
@@ -544,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;
@@ -563,7 +606,7 @@ static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING
// now scan to find xpos
find->x = r.x0;
for (i=0; first+i < n; ++i)
for (i=0; first+i < n; i = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, first + i) - first)
find->x += STB_TEXTEDIT_GETWIDTH(str, first, i);
}
@@ -640,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 )
{
@@ -649,9 +721,9 @@ static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
{
--c; // always move at least one character
while( c >= 0 && !is_word_boundary( str, c ) )
--c;
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX( str, c ); // always move at least one character
while (c >= 0 && !is_word_boundary(str, c))
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, c);
if( c < 0 )
c = 0;
@@ -665,9 +737,9 @@ static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c
static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
{
const int len = STB_TEXTEDIT_STRINGLEN(str);
++c; // always move at least one character
c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c); // always move at least one character
while( c < len && !is_word_boundary( str, c ) )
++c;
c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c);
if( c > len )
c = len;
@@ -706,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;
@@ -720,36 +793,47 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS
#define STB_TEXTEDIT_KEYTYPE int
#endif
// API key: process text input
// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
{
// can't add newline in single-line mode
if (text[0] == '\n' && state->single_line)
return;
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
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
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;
}
}
}
// API key: process a keyboard input
static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
{
retry:
switch (key) {
default: {
#ifdef STB_TEXTEDIT_KEYTOTEXT
// This is not suitable for UTF-8 support.
int c = STB_TEXTEDIT_KEYTOTEXT(key);
if (c > 0) {
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE) c;
// can't add newline in single-line mode
if (c == '\n' && state->single_line)
break;
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
++state->cursor;
state->has_preferred_x = 0;
}
} else {
stb_textedit_delete_selection(str,state); // implicitly clamps
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
stb_text_makeundo_insert(state, state->cursor, 1);
++state->cursor;
state->has_preferred_x = 0;
}
}
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
stb_textedit_text(str, state, &ch, 1);
}
#endif
break;
}
@@ -775,7 +859,7 @@ retry:
stb_textedit_move_to_first(state);
else
if (state->cursor > 0)
--state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
state->has_preferred_x = 0;
break;
@@ -784,7 +868,7 @@ retry:
if (STB_TEXT_HAS_SELECTION(state))
stb_textedit_move_to_last(str, state);
else
++state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
stb_textedit_clamp(str, state);
state->has_preferred_x = 0;
break;
@@ -794,7 +878,7 @@ retry:
stb_textedit_prep_selection_at_cursor(state);
// move selection left
if (state->select_end > 0)
--state->select_end;
state->select_end = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->select_end);
state->cursor = state->select_end;
state->has_preferred_x = 0;
break;
@@ -844,7 +928,7 @@ retry:
case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT:
stb_textedit_prep_selection_at_cursor(state);
// move selection right
++state->select_end;
state->select_end = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->select_end);
stb_textedit_clamp(str, state);
state->cursor = state->select_end;
state->has_preferred_x = 0;
@@ -884,15 +968,16 @@ 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;
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
x = row.x0;
for (i=0; i < row.num_chars; ++i) {
for (i=0; i < row.num_chars; ) {
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break;
@@ -900,10 +985,13 @@ retry:
x += dx;
if (x > goal_x)
break;
++state->cursor;
i += next - state->cursor;
state->cursor = next;
}
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;
@@ -953,8 +1041,9 @@ retry:
state->cursor = find.prev_first;
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
x = row.x0;
for (i=0; i < row.num_chars; ++i) {
for (i=0; i < row.num_chars; ) {
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break;
@@ -962,10 +1051,15 @@ retry:
x += dx;
if (x > goal_x)
break;
++state->cursor;
i += next - state->cursor;
state->cursor = next;
}
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;
@@ -975,10 +1069,15 @@ retry:
// go to previous line
// (we need to scan previous line the hard way. maybe we could expose this as a new API function?)
prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0;
while (prev_scan > 0 && STB_TEXTEDIT_GETCHAR(str, prev_scan - 1) != STB_TEXTEDIT_NEWLINE)
--prev_scan;
while (prev_scan > 0)
{
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, prev_scan);
if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
break;
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;
}
@@ -990,7 +1089,7 @@ retry:
else {
int n = STB_TEXTEDIT_STRINGLEN(str);
if (state->cursor < n)
stb_textedit_delete(str, state, state->cursor, 1);
stb_textedit_delete(str, state, state->cursor, IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor) - state->cursor);
}
state->has_preferred_x = 0;
break;
@@ -1002,8 +1101,9 @@ retry:
else {
stb_textedit_clamp(str, state);
if (state->cursor > 0) {
stb_textedit_delete(str, state, state->cursor-1, 1);
--state->cursor;
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
stb_textedit_delete(str, state, prev, state->cursor - prev);
state->cursor = prev;
}
}
state->has_preferred_x = 0;
@@ -1051,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;
state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);
state->has_preferred_x = 0;
break;
@@ -1062,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;
stb_textedit_move_to_last(str, state);
state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);
state->has_preferred_x = 0;
break;
}
@@ -1079,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;
state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);
state->select_end = state->cursor;
state->has_preferred_x = 0;
break;
@@ -1091,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;
state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);
state->select_end = state->cursor;
state->has_preferred_x = 0;
break;
@@ -1272,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;
}
@@ -1323,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
+2 -2
View File
@@ -4516,8 +4516,8 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex
q2[0] = (float)x2;
q2[1] = (float)y2;
if (equal(q0,q1) || equal(q1,q2)) {
x0 = (int)verts[i-1].x;
y0 = (int)verts[i-1].y;
x0 = (int)verts[i-1].x; //-V1048
y0 = (int)verts[i-1].y; //-V1048
x1 = (int)verts[i ].x;
y1 = (int)verts[i ].y;
if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
Submodule core/deps/libjuice added at 77daa8befd.
@@ -31,7 +31,7 @@
#endif
#endif
#ifdef _WIN32
#if defined(_WIN32) && !defined(USE_LIBCDIO) // libcdio has its equivalent version
#undef fopen
void *fopen_utf8(const char * filename, const char * mode)
+17 -10
View File
@@ -190,7 +190,9 @@ int main(int argc, char *argv[]) { }" HAVE_NULLABLE)
test_big_endian(WORDS_BIGENDIAN)
find_package(ZLIB 1.1.2 REQUIRED)
if(NOT TARGET zlibstatic)
find_package(ZLIB 1.1.2 REQUIRED)
endif()
# so developers on systems where zlib is named differently (Windows, sometimes)
# can override the name used in the pkg-config file
if (NOT ZLIB_LINK_LIBRARY_NAME)
@@ -239,17 +241,22 @@ if(ENABLE_LZMA)
endif(ENABLE_LZMA)
if(ENABLE_ZSTD)
find_package(zstd 1.3.6)
if(zstd_FOUND)
if(TARGET libzstd_static)
set(zstd_TARGET libzstd_static)
set(HAVE_LIBZSTD 1)
if(TARGET zstd::libzstd_shared)
set(zstd_TARGET zstd::libzstd_shared)
else()
set(zstd_TARGET zstd::libzstd_static)
endif()
else()
message(WARNING "-- zstd library not found; zstandard support disabled")
endif(zstd_FOUND)
find_package(zstd 1.3.6)
if(zstd_FOUND)
set(HAVE_LIBZSTD 1)
if(TARGET zstd::libzstd_shared)
set(zstd_TARGET zstd::libzstd_shared)
else()
set(zstd_TARGET zstd::libzstd_static)
endif()
else()
message(WARNING "-- zstd library not found; zstandard support disabled")
endif()
endif()
endif(ENABLE_ZSTD)
if (COMMONCRYPTO_FOUND)
+5 -1
View File
@@ -175,7 +175,11 @@ if(SHARED_LIB_VERSIONNING)
set_target_properties(zip PROPERTIES VERSION 5.5 SOVERSION 5)
endif()
target_link_libraries(zip PRIVATE ZLIB::ZLIB)
if (TARGET zlibstatic)
target_link_libraries(zip PRIVATE zlibstatic)
else()
target_link_libraries(zip PRIVATE ZLIB::ZLIB)
endif()
target_include_directories(zip
PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/lib>
+1 -1
View File
@@ -338,7 +338,7 @@ receiveDevicesFromMiniSSDPD(int s, int * error)
#ifdef DEBUG
printf(" usnsize=%u\n", usnsize);
#endif /* DEBUG */
tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize);
tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize+3);
if(tmp == NULL) {
if (error)
*error = MINISSDPC_MEMORY_ERROR;
@@ -7,6 +7,8 @@
#include <windows.h>
#pragma pack(pop)
#define PICO_SUPPORT_THREADING
#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
#else
@@ -54,6 +56,15 @@ static inline void PICO_IDLE(void)
#define alloca _alloca
#ifdef PICO_SUPPORT_THREADING
#define PICO_SUPPORT_MUTEX
/* mutex implementations */
extern void* pico_mutex_init(void);
extern void pico_mutex_lock(void* mux);
extern void pico_mutex_unlock(void* mux);
extern void pico_mutex_deinit(void* mux);
#endif
#endif /* PICO_SUPPORT_MSVC */
+1
View File
@@ -170,6 +170,7 @@ struct pico_ip_mreq_source {
#define PICO_SOCK_EV_CLOSE 8u
#define PICO_SOCK_EV_FIN 0x10u
#define PICO_SOCK_EV_ERR 0x80u
#define PICO_SOCK_EV_DEL 0x100u
struct pico_msginfo {
struct pico_device *dev;
+1 -1
View File
@@ -9,7 +9,7 @@
#include "pico_frame.h"
#include "pico_constants.h"
#define PICO_MAX_TIMERS 20
#define PICO_MAX_TIMERS 50
#define PICO_ETH_MRU (1514u)
#define PICO_IP_MRU (1500u)
+1 -1
View File
@@ -319,7 +319,7 @@ static void pppdump(uint8_t *buf, int len, int egress)
last_time_ms = PICO_TIME_MS();
}
uint32_t delta = (PICO_TIME_MS() - last_time_ms) / 100;
uint32_t delta = PICO_TIME_MS() / 100 - last_time_ms / 100;
if (delta < 256)
{
fputc(6, dumpfp); // Time step (short)
+5 -4
View File
@@ -295,7 +295,7 @@ static int pico_ethernet_ipv6_dst(struct pico_frame *f, struct pico_eth *const d
/* Ethernet send, first attempt: try our own address.
* Returns 0 if the packet is not for us.
* Returns 1 if the packet is cloned to our own receive queue, so the caller can discard the original frame.
* Returns 1 if the packet is cloned to our own receive queue and the original frame is dicarded.
* */
static int32_t pico_ethsend_local(struct pico_frame *f, struct pico_eth_hdr *hdr)
{
@@ -308,7 +308,9 @@ static int32_t pico_ethsend_local(struct pico_frame *f, struct pico_eth_hdr *hdr
dbg("sending out packet destined for our own mac\n");
if (pico_ethernet_receive(clone) < 0) {
dbg("pico_ethernet_receive() failed\n");
return 0;
}
pico_frame_discard(f);
return 1;
}
@@ -317,13 +319,12 @@ static int32_t pico_ethsend_local(struct pico_frame *f, struct pico_eth_hdr *hdr
/* Ethernet send, second attempt: try bcast.
* Returns 0 if the packet is not bcast, so it will be handled somewhere else.
* Returns 1 if the packet is handled by the pico_device_broadcast() function, so it can be discarded.
* Returns 1 if the packet is handled by the pico_device_broadcast() function and is discarded.
* */
static int32_t pico_ethsend_bcast(struct pico_frame *f)
{
if (IS_LIMITED_BCAST(f)) {
(void)pico_device_broadcast(f); /* We can discard broadcast even if it's not sent. */
return 1;
return (pico_device_broadcast(f) > 0); // Return 1 on success, ret > 0
}
return 0;
+4
View File
@@ -242,6 +242,10 @@ static int8_t pico_icmp4_send_echo(struct pico_icmp4_ping_cookie *cookie)
if (!dev)
return -1;
// prevent overflow
if (cookie->size > PICO_ICMP_MAXCOOKIE)
return -1;
echo = pico_proto_ipv4.alloc(&pico_proto_ipv4, dev, (uint16_t)(PICO_ICMPHDR_UN_SIZE + cookie->size));
if (!echo)
return -1;
+1 -1
View File
@@ -92,7 +92,7 @@ PACKED_STRUCT_DEF pico_icmp4_hdr {
#define PICO_ICMP_MASKREPLY 18
#define PICO_ICMP_MAXTYPE 18
#define PICO_ICMP_MAXCOOKIE 65528
#define PICO_ICMP_UNREACH_NET 0
#define PICO_ICMP_UNREACH_HOST 1
@@ -190,7 +190,6 @@ static int socket_tcp_do_deliver(struct pico_socket *s, struct pico_frame *f)
return 0;
}
dbg("TCP SOCKET> Not s.\n");
return -1;
}
+23 -6
View File
@@ -826,6 +826,8 @@ static void tcp_rcv_sack(struct pico_socket_tcp *t, uint8_t *opt, int len)
static int tcpopt_len_check(uint32_t *idx, uint8_t len, uint8_t expected)
{
if (len != expected) {
if (len < 2)
return -1;
*idx = *idx + len - 2;
return -1;
}
@@ -880,7 +882,7 @@ static inline void tcp_parse_option_timestamp(struct pico_socket_tcp *t, struct
t->ts_nxt = long_be(tsval);
}
static void tcp_parse_options(struct pico_frame *f)
static int tcp_parse_options(struct pico_frame *f)
{
struct pico_socket_tcp *t = (struct pico_socket_tcp *)f->sock;
uint8_t *opt = f->transport_hdr + PICO_SIZE_TCPHDR;
@@ -897,6 +899,10 @@ static void tcp_parse_options(struct pico_frame *f)
if (f->payload && ((opt + i) > f->payload))
break;
if (len == 0) {
return -1;
}
tcp_dbg_options("Received option '%d', len = %d \n", type, len);
switch (type) {
case PICO_TCP_OPTION_NOOP:
@@ -924,6 +930,7 @@ static void tcp_parse_options(struct pico_frame *f)
i = i + len - 2;
}
}
return 0;
}
static inline void tcp_send_add_tcpflags(struct pico_socket_tcp *ts, struct pico_frame *f)
@@ -1755,7 +1762,8 @@ static int tcp_data_in(struct pico_socket *s, struct pico_frame *f)
(void)hdr;
if (((hdr->len & 0xf0u) >> 2u) <= f->transport_len) {
tcp_parse_options(f);
if (tcp_parse_options(f) < 0)
return -1;
f->payload = f->transport_hdr + ((hdr->len & 0xf0u) >> 2u);
f->payload_len = payload_len;
tcp_dbg("TCP> Received segment. (exp: %x got: %x)\n", t->rcv_nxt, SEQN(f));
@@ -2130,7 +2138,8 @@ static int tcp_ack(struct pico_socket *s, struct pico_frame *f)
tcp_ack_dbg(s, f);
#endif
tcp_parse_options(f);
if (tcp_parse_options(f) < 0)
return -1;
t->recv_wnd = short_be(hdr->rwnd);
acked = (uint16_t)tcp_ack_advance_una(t, f, &acked_timestamp);
@@ -2446,7 +2455,8 @@ static int tcp_syn(struct pico_socket *s, struct pico_frame *f)
f->sock = &new->sock;
mtu = (uint16_t)pico_socket_get_mss(&new->sock);
new->mss = (uint16_t)(mtu - PICO_SIZE_TCPHDR);
tcp_parse_options(f);
if (tcp_parse_options(f) < 0)
return -1;
new->tcpq_in.max_size = PICO_DEFAULT_SOCKETQ;
new->tcpq_out.max_size = PICO_DEFAULT_SOCKETQ;
new->tcpq_hold.max_size = 2u * mtu;
@@ -2793,7 +2803,7 @@ static uint8_t invalid_flags(struct pico_socket *s, uint8_t flags)
{ /* PICO_SOCKET_STATE_TCP_UNDEF */ 0, },
{ /* PICO_SOCKET_STATE_TCP_CLOSED */ 0, },
{ /* PICO_SOCKET_STATE_TCP_LISTEN */ PICO_TCP_SYN, PICO_TCP_SYN | PICO_TCP_PSH },
{ /* PICO_SOCKET_STATE_TCP_SYN_SENT */ PICO_TCP_SYNACK, PICO_TCP_RST, PICO_TCP_RSTACK},
{ /* PICO_SOCKET_STATE_TCP_SYN_SENT */ PICO_TCP_SYNACK, PICO_TCP_RST, PICO_TCP_RSTACK, PICO_TCP_SYNACK | PICO_TCP_PSH },
{ /* PICO_SOCKET_STATE_TCP_SYN_RECV */ PICO_TCP_SYN, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST},
{ /* PICO_SOCKET_STATE_TCP_ESTABLISHED*/ PICO_TCP_SYN, PICO_TCP_SYNACK, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FIN, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST, PICO_TCP_RSTACK},
{ /* PICO_SOCKET_STATE_TCP_CLOSE_WAIT */ PICO_TCP_SYNACK, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FIN, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST},
@@ -2864,6 +2874,11 @@ int pico_tcp_input(struct pico_socket *s, struct pico_frame *f)
// tcp_dbg("[sam] TCP> s->state >> 8 = %u\n", s->state >> 8);
tcp_dbg("[tcp input] socket: %p state: %d <-- local port:%u remote port: %u seq: 0x%08x ack: 0x%08x flags: 0x%02x t_len: %u, hdr: %u payload: %d\n", s, s->state >> 8, short_be(hdr->trans.dport), short_be(hdr->trans.sport), SEQN(f), ACKN(f), hdr->flags, f->transport_len, (hdr->len & 0xf0) >> 2, f->payload_len );
if ((f->payload + f->payload_len) > (f->buffer + f->buffer_len)) {
tcp_dbg("TCP> Invalid payload len %04x\n", f->payload_len);
pico_frame_discard(f);
return -1;
}
/* This copy of the frame has the current socket as owner */
f->sock = s;
s->timestamp = TCP_TIME;
@@ -2874,7 +2889,9 @@ int pico_tcp_input(struct pico_socket *s, struct pico_frame *f)
}
else if (flags == PICO_TCP_SYN || flags == (PICO_TCP_SYN | PICO_TCP_PSH)) {
tcp_action_call(action->syn, s, f);
} else if (flags == (PICO_TCP_SYN | PICO_TCP_ACK)) {
} else if (flags == (PICO_TCP_SYN | PICO_TCP_ACK)
// Windows CE / DirectPlay sets the PSH flag on SYN|ACK packets, which is normally invalid
|| flags == (PICO_TCP_SYN | PICO_TCP_ACK | PICO_TCP_PSH)) {
tcp_action_call(action->synack, s, f);
} else {
ret = tcp_action_by_flags(action, s, f, flags);
+16 -1
View File
@@ -451,6 +451,7 @@ int32_t pico_device_broadcast(struct pico_frame *f)
{
struct pico_tree_node *index;
int32_t ret = -1;
int sent = 0;
pico_tree_foreach(index, &Device_tree)
{
@@ -463,14 +464,28 @@ int32_t pico_device_broadcast(struct pico_frame *f)
break;
copy->dev = dev;
copy->dev->send(copy->dev, copy->start, (int)copy->len);
ret = copy->dev->send(copy->dev, copy->start, (int)copy->len);
/* FIXME: If a device driver returns zero (which means the device
* driver is currently busy) there is no means to retry the
* broadcast operation later. */
pico_frame_discard(copy);
}
else
{
ret = f->dev->send(f->dev, f->start, (int)f->len);
/* FIXME: If a device driver returns zero (which means the device
* driver is currently busy) there is no means to retry the
* broadcast operation later. */
}
/* FIXME: If at least one device driver was able to sent the frame on
* the wire, the broadcast operation will be considered successful. */
if (ret > 0) {
sent = 1;
}
}
ret = sent ? f->len : -1;
pico_frame_discard(f);
return ret;
}
+2
View File
@@ -473,6 +473,8 @@ static void socket_garbage_collect(pico_time now, void *arg)
struct pico_socket *s = (struct pico_socket *) arg;
IGNORE_PARAMETER(now);
if (s->wakeup)
s->wakeup(PICO_SOCK_EV_DEL, s);
socket_clean_queues(s);
PICO_FREE(s);
}
+18 -3
View File
@@ -1,6 +1,21 @@
===========
|Changelog|
===========
xBRZ 1.8 [2019-11-28]
---------------------
Consider ARGB outside area as transparent
Fixed ARGB scaling issue on image borders
xBRZ 1.7 [2019-07-04]
---------------------
Fixed asymmetric color distance
New parameter: "Center direction bias"
xBRZ 1.6 [2018-02-27]
---------------------
Added bilinear scaling
Option to skip color buffer creation
Updated license info
xBRZ 1.5 [2017-08-07]
---------------------
+439 -204
View File
@@ -1,24 +1,25 @@
// ****************************************************************************
// * This file is part of the HqMAME project. It is distributed under *
// * This file is part of the xBRZ project. It is distributed under *
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// * *
// * Additionally and as a special exception, the author gives permission *
// * to link the code of this program with the MAME library (or with modified *
// * versions of MAME that use the same license as MAME), and distribute *
// * linked combinations including the two. You must obey the GNU General *
// * Public License in all respects for all of the code used other than MAME. *
// * to link the code of this program with the following libraries *
// * (or with modified versions that use the same licenses), and distribute *
// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe *
// * You must obey the GNU General Public License in all respects for all of *
// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. *
// * If you modify this file, you may extend this exception to your version *
// * of the file, but you are not obligated to do so. If you do not wish to *
// * do so, delete this exception statement from your version. *
// ****************************************************************************
#include "xbrz.h"
#include "xbrz_tools.h"
#include <cassert>
#include <algorithm>
#include <vector>
#include <cmath>
#include <algorithm>
#include <cmath> //std::sqrt
#include "xbrz_tools.h"
using namespace xbrz;
@@ -26,9 +27,9 @@ using namespace xbrz;
namespace
{
template <unsigned int M, unsigned int N> inline
uint32_t gradientRGB(uint32_t pixFront, uint32_t pixBack) //blend front color with opacity M / N over opaque background: http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
uint32_t gradientRGB(uint32_t pixFront, uint32_t pixBack) //blend front color with opacity M / N over opaque background: https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
{
static_assert(0 < M && M < N && N <= 1000, "");
static_assert(0 < M && M < N && N <= 1000);
auto calcColor = [](unsigned char colFront, unsigned char colBack) -> unsigned char { return (colFront * M + colBack * (N - M)) / N; };
@@ -41,7 +42,7 @@ uint32_t gradientRGB(uint32_t pixFront, uint32_t pixBack) //blend front color wi
template <unsigned int M, unsigned int N> inline
uint32_t gradientARGB(uint32_t pixFront, uint32_t pixBack) //find intermediate color between two colors with alpha channels (=> NO alpha blending!!!)
{
static_assert(0 < M && M < N && N <= 1000, "");
static_assert(0 < M && M < N && N <= 1000);
const unsigned int weightFront = getAlpha(pixFront) * M;
const unsigned int weightBack = getAlpha(pixBack) * (N - M);
@@ -135,7 +136,7 @@ template <class T> inline
T square(T value) { return value * value; }
#if 0
inline
double distRGB(uint32_t pix1, uint32_t pix2)
{
@@ -146,12 +147,13 @@ double distRGB(uint32_t pix1, uint32_t pix2)
//euklidean RGB distance
return std::sqrt(square(r_diff) + square(g_diff) + square(b_diff));
}
#endif
inline
double distYCbCr(uint32_t pix1, uint32_t pix2, double lumaWeight)
{
//http://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion
//https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion
//YCbCr conversion is a matrix multiplication => take advantage of linearity by subtracting first!
const int r_diff = static_cast<int>(getRed (pix1)) - getRed (pix2); //we may delay division by 255 to after matrix multiplication
const int g_diff = static_cast<int>(getGreen(pix1)) - getGreen(pix2); //
@@ -186,9 +188,9 @@ double distYCbCrBuffered(uint32_t pix1, uint32_t pix2)
for (uint32_t i = 0; i < 256 * 256 * 256; ++i) //startup time: 114 ms on Intel Core i5 (four cores)
{
const int r_diff = getByte<2>(i) * 2 - 0xFF;
const int g_diff = getByte<1>(i) * 2 - 0xFF;
const int b_diff = getByte<0>(i) * 2 - 0xFF;
const int r_diff = static_cast<signed char>(getByte<2>(i)) * 2;
const int g_diff = static_cast<signed char>(getByte<1>(i)) * 2;
const int b_diff = static_cast<signed char>(getByte<0>(i)) * 2;
const double k_b = 0.0593; //ITU-R BT.2020 conversion
const double k_r = 0.2627; //
@@ -210,24 +212,32 @@ double distYCbCrBuffered(uint32_t pix1, uint32_t pix2)
// return 0;
//if (pix1 < pix2)
// std::swap(pix1, pix2); -> 30% perf degradation!!!
#if 1
const int r_diff = static_cast<int>(getRed (pix1)) - getRed (pix2);
const int g_diff = static_cast<int>(getGreen(pix1)) - getGreen(pix2);
const int b_diff = static_cast<int>(getBlue (pix1)) - getBlue (pix2);
return diffToDist[(((r_diff + 0xFF) / 2) << 16) | //slightly reduce precision (division by 2) to squeeze value into single byte
(((g_diff + 0xFF) / 2) << 8) |
(( b_diff + 0xFF) / 2)];
#else //not noticeably faster:
const int r_diff_tmp = ((pix1 & 0xFF0000) + 0xFF0000 - (pix2 & 0xFF0000)) / 2;
const int g_diff_tmp = ((pix1 & 0x00FF00) + 0x00FF00 - (pix2 & 0x00FF00)) / 2; //slightly reduce precision (division by 2) to squeeze value into single byte
const int b_diff_tmp = ((pix1 & 0x0000FF) + 0x0000FF - (pix2 & 0x0000FF)) / 2;
const size_t index = (static_cast<unsigned char>(r_diff / 2) << 16) | //slightly reduce precision (division by 2) to squeeze value into single byte
(static_cast<unsigned char>(g_diff / 2) << 8) |
(static_cast<unsigned char>(b_diff / 2));
return diffToDist[(r_diff_tmp & 0xFF0000) | (g_diff_tmp & 0x00FF00) | (b_diff_tmp & 0x0000FF)];
#if 0 //attention: the following calculation creates an asymmetric color distance!!! (e.g. r_diff=46 will be unpacked as 45, but r_diff=-46 unpacks to -47
const size_t index = (((r_diff + 0xFF) / 2) << 16) | //slightly reduce precision (division by 2) to squeeze value into single byte
(((g_diff + 0xFF) / 2) << 8) |
(( b_diff + 0xFF) / 2);
#endif
return diffToDist[index];
}
#if defined _MSC_VER && !defined NDEBUG
const int debugPixelX = -1;
const int debugPixelY = 58;
thread_local bool breakIntoDebugger = false;
#endif
enum BlendType
{
BLEND_NONE = 0,
@@ -244,24 +254,32 @@ struct BlendResult
};
struct Kernel_3x3
{
uint32_t
a, b, c,
d, e, f,
g, h, i;
};
struct Kernel_4x4 //kernel for preprocessing step
{
uint32_t
/**/a, b, c, d,
/**/e, f, g, h,
/**/i, j, k, l,
/**/m, n, o, p;
a, b, c, //
e, f, g, // support reinterpret_cast from Kernel_4x4 => Kernel_3x3
i, j, k, //
m, n, o,
d, h, l, p;
};
/*
input kernel area naming convention:
/* input kernel area naming convention:
-----------------
| A | B | C | D |
----|---|---|---|
| E | F | G | H | //evaluate the four corners between F, G, J, K
----|---|---|---| //input pixel is at position F
|---|---|---|---|
| E | F | G | H | evaluate the four corners between F, G, J, K
|---|---|---|---| input pixel is at position F
| I | J | K | L |
----|---|---|---|
|---|---|---|---|
| M | N | O | P |
-----------------
*/
@@ -269,6 +287,11 @@ template <class ColorDistance>
FORCE_INLINE //detect blend direction
BlendResult preProcessCorners(const Kernel_4x4& ker, const xbrz::ScalerCfg& cfg) //result: F, G, J, K corners of "GradientType"
{
#if defined _MSC_VER && !defined NDEBUG
if (breakIntoDebugger)
__debugbreak(); //__asm int 3;
#endif
BlendResult result = {};
if ((ker.f == ker.g &&
@@ -279,9 +302,8 @@ BlendResult preProcessCorners(const Kernel_4x4& ker, const xbrz::ScalerCfg& cfg)
auto dist = [&](uint32_t pix1, uint32_t pix2) { return ColorDistance::dist(pix1, pix2, cfg.luminanceWeight); };
const int weight = 4;
double jg = dist(ker.i, ker.f) + dist(ker.f, ker.c) + dist(ker.n, ker.k) + dist(ker.k, ker.h) + weight * dist(ker.j, ker.g);
double fk = dist(ker.e, ker.j) + dist(ker.j, ker.o) + dist(ker.b, ker.g) + dist(ker.g, ker.l) + weight * dist(ker.f, ker.k);
double jg = dist(ker.i, ker.f) + dist(ker.f, ker.c) + dist(ker.n, ker.k) + dist(ker.k, ker.h) + cfg.centerDirectionBias * dist(ker.j, ker.g);
double fk = dist(ker.e, ker.j) + dist(ker.j, ker.o) + dist(ker.b, ker.g) + dist(ker.g, ker.l) + cfg.centerDirectionBias * dist(ker.f, ker.k);
if (jg < fk) //test sample: 70% of values max(jg, fk) / min(jg, fk) are between 1.1 and 3.7 with median being 1.8
{
@@ -304,14 +326,6 @@ BlendResult preProcessCorners(const Kernel_4x4& ker, const xbrz::ScalerCfg& cfg)
return result;
}
struct Kernel_3x3
{
uint32_t
/**/a, b, c,
/**/d, e, f,
/**/g, h, i;
};
#define DEF_GETTER(x) template <RotationDegree rotDeg> uint32_t inline get_##x(const Kernel_3x3& ker) { return ker.x; }
//we cannot and NEED NOT write "ker.##x" since ## concatenates preprocessor tokens but "." is not a token
DEF_GETTER(a) DEF_GETTER(b) DEF_GETTER(c)
@@ -339,17 +353,21 @@ DEF_GETTER(g, a) DEF_GETTER(h, d) DEF_GETTER(i, g)
//compress four blend types into a single byte
inline BlendType getTopL (unsigned char b) { return static_cast<BlendType>(0x3 & b); }
//inline BlendType getTopL (unsigned char b) { return static_cast<BlendType>(0x3 & b); }
inline BlendType getTopR (unsigned char b) { return static_cast<BlendType>(0x3 & (b >> 2)); }
inline BlendType getBottomR(unsigned char b) { return static_cast<BlendType>(0x3 & (b >> 4)); }
inline BlendType getBottomL(unsigned char b) { return static_cast<BlendType>(0x3 & (b >> 6)); }
inline void setTopL (unsigned char& b, BlendType bt) { b |= bt; } //buffer is assumed to be initialized before preprocessing!
inline void setTopR (unsigned char& b, BlendType bt) { b |= (bt << 2); }
inline void setBottomR(unsigned char& b, BlendType bt) { b |= (bt << 4); }
inline void setBottomL(unsigned char& b, BlendType bt) { b |= (bt << 6); }
inline void clearAddTopL(unsigned char& b, BlendType bt) { b = static_cast<unsigned char>(bt); }
inline void addTopR (unsigned char& b, BlendType bt) { b |= (bt << 2); } //buffer is assumed to be initialized before preprocessing!
inline void addBottomR (unsigned char& b, BlendType bt) { b |= (bt << 4); } //e.g. via clearAddTopL()
inline void addBottomL (unsigned char& b, BlendType bt) { b |= (bt << 6); } //
inline bool blendingNeeded(unsigned char b) { return b != 0; }
inline bool blendingNeeded(unsigned char b)
{
static_assert(BLEND_NONE == 0);
return b != 0;
}
template <RotationDegree rotDeg> inline
unsigned char rotateBlendInfo(unsigned char b) { return b; }
@@ -358,20 +376,12 @@ template <> inline unsigned char rotateBlendInfo<ROT_180>(unsigned char b) { ret
template <> inline unsigned char rotateBlendInfo<ROT_270>(unsigned char b) { return ((b << 6) | (b >> 2)) & 0xff; }
#if defined _MSC_VER && !defined NDEBUG
int debugPixelX = -1;
int debugPixelY = 12;
__declspec(thread) bool breakIntoDebugger = false;
#endif
/*
input kernel area naming convention:
/* input kernel area naming convention:
-------------
| A | B | C |
----|---|---|
| D | E | F | //input pixel is at position E
----|---|---|
|---|---|---|
| D | E | F | input pixel is at position E
|---|---|---|
| G | H | I |
-------------
*/
@@ -382,7 +392,7 @@ void blendPixel(const Kernel_3x3& ker,
unsigned char blendInfo, //result of preprocessing all four corners of pixel "e"
const xbrz::ScalerCfg& cfg)
{
#define a get_a<rotDeg>(ker)
//#define a get_a<rotDeg>(ker)
#define b get_b<rotDeg>(ker)
#define c get_c<rotDeg>(ker)
#define d get_d<rotDeg>(ker)
@@ -410,7 +420,7 @@ void blendPixel(const Kernel_3x3& ker,
return true;
//make sure there is no second blending in an adjacent rotation for this pixel: handles insular pixels, mario eyes
if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90� corners
if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90° corners
return false;
if (getBottomL(blend) != BLEND_NONE && !eq(e, c))
return false;
@@ -453,7 +463,7 @@ void blendPixel(const Kernel_3x3& ker,
Scaler::blendCorner(px, out);
}
#undef a
//#undef a
#undef b
#undef c
#undef d
@@ -465,7 +475,72 @@ void blendPixel(const Kernel_3x3& ker,
}
template <class Scaler, class ColorDistance> //scaler policy: see "Scaler2x" reference implementation
class OobReaderTransparent
{
public:
OobReaderTransparent(const uint32_t* src, int srcWidth, int srcHeight, int y) :
s_m1(0 <= y - 1 && y - 1 < srcHeight ? src + srcWidth * (y - 1) : nullptr),
s_0 (0 <= y && y < srcHeight ? src + srcWidth * y : nullptr),
s_p1(0 <= y + 1 && y + 1 < srcHeight ? src + srcWidth * (y + 1) : nullptr),
s_p2(0 <= y + 2 && y + 2 < srcHeight ? src + srcWidth * (y + 2) : nullptr),
srcWidth_(srcWidth) {}
void readDhlp(Kernel_4x4& ker, int x) const //(x, y) is at kernel position F
{
[[likely]] if (const int x_p2 = x + 2; 0 <= x_p2 && x_p2 < srcWidth_)
{
ker.d = s_m1 ? s_m1[x_p2] : 0;
ker.h = s_0 ? s_0 [x_p2] : 0;
ker.l = s_p1 ? s_p1[x_p2] : 0;
ker.p = s_p2 ? s_p2[x_p2] : 0;
}
else
{
ker.d = 0;
ker.h = 0;
ker.l = 0;
ker.p = 0;
}
}
private:
const uint32_t* const s_m1;
const uint32_t* const s_0;
const uint32_t* const s_p1;
const uint32_t* const s_p2;
const int srcWidth_;
};
class OobReaderDuplicate
{
public:
OobReaderDuplicate(const uint32_t* src, int srcWidth, int srcHeight, int y) :
s_m1(src + srcWidth * std::clamp(y - 1, 0, srcHeight - 1)),
s_0 (src + srcWidth * std::clamp(y, 0, srcHeight - 1)),
s_p1(src + srcWidth * std::clamp(y + 1, 0, srcHeight - 1)),
s_p2(src + srcWidth * std::clamp(y + 2, 0, srcHeight - 1)),
srcWidth_(srcWidth) {}
void readDhlp(Kernel_4x4& ker, int x) const //(x, y) is at kernel position F
{
const int x_p2 = std::clamp(x + 2, 0, srcWidth_ - 1);
ker.d = s_m1[x_p2];
ker.h = s_0 [x_p2];
ker.l = s_p1[x_p2];
ker.p = s_p2[x_p2];
}
private:
const uint32_t* const s_m1;
const uint32_t* const s_0;
const uint32_t* const s_p1;
const uint32_t* const s_p2;
const int srcWidth_;
};
template <class Scaler, class ColorDistance, class OobReader> //scaler policy: see "Scaler2x" reference implementation
void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, const xbrz::ScalerCfg& cfg, int yFirst, int yLast)
{
yFirst = std::max(yFirst, 0);
@@ -475,64 +550,72 @@ void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight,
const int trgWidth = srcWidth * Scaler::scale;
//"use" space at the end of the image as temporary buffer for "on the fly preprocessing": we even could use larger area of
//"sizeof(uint32_t) * srcWidth * (yLast - yFirst)" bytes without risk of accidental overwriting before accessing
const int bufferSize = srcWidth;
unsigned char* preProcBuffer = reinterpret_cast<unsigned char*>(trg + yLast * Scaler::scale * trgWidth) - bufferSize;
std::fill(preProcBuffer, preProcBuffer + bufferSize, '\0');
static_assert(BLEND_NONE == 0, "");
//(ab)use space of "sizeof(uint32_t) * srcWidth * Scaler::scale" at the end of the image as temporary
//buffer for "on the fly preprocessing" without risk of accidental overwriting before accessing
unsigned char* const preProcBuf = reinterpret_cast<unsigned char*>(trg + yLast * Scaler::scale * trgWidth) - srcWidth;
//initialize preprocessing buffer for first row of current stripe: detect upper left and right corner blending
//this cannot be optimized for adjacent processing stripes; we must not allow for a memory race condition!
if (yFirst > 0)
{
const int y = yFirst - 1;
const OobReader oobReader(src, srcWidth, srcHeight, yFirst - 1);
const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0);
const uint32_t* s_0 = src + srcWidth * y; //center line
const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1);
const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1);
//initialize at position x = -1
Kernel_4x4 ker4 = {};
oobReader.readDhlp(ker4, -4); //hack: read a, e, i, m at x = -1
ker4.a = ker4.d;
ker4.e = ker4.h;
ker4.i = ker4.l;
ker4.m = ker4.p;
oobReader.readDhlp(ker4, -3);
ker4.b = ker4.d;
ker4.f = ker4.h;
ker4.j = ker4.l;
ker4.n = ker4.p;
oobReader.readDhlp(ker4, -2);
ker4.c = ker4.d;
ker4.g = ker4.h;
ker4.k = ker4.l;
ker4.o = ker4.p;
oobReader.readDhlp(ker4, -1);
{
const BlendResult res = preProcessCorners<ColorDistance>(ker4, cfg);
clearAddTopL(preProcBuf[0], res.blend_k); //set 1st known corner for (0, yFirst)
}
for (int x = 0; x < srcWidth; ++x)
{
const int x_m1 = std::max(x - 1, 0);
const int x_p1 = std::min(x + 1, srcWidth - 1);
const int x_p2 = std::min(x + 2, srcWidth - 1);
ker4.a = ker4.b; //shift previous kernel to the left
ker4.e = ker4.f; // -----------------
ker4.i = ker4.j; // | A | B | C | D |
ker4.m = ker4.n; // |---|---|---|---|
/**/ // | E | F | G | H | (x, yFirst - 1) is at position F
ker4.b = ker4.c; // |---|---|---|---|
ker4.f = ker4.g; // | I | J | K | L |
ker4.j = ker4.k; // |---|---|---|---|
ker4.n = ker4.o; // | M | N | O | P |
/**/ // -----------------
ker4.c = ker4.d;
ker4.g = ker4.h;
ker4.k = ker4.l;
ker4.o = ker4.p;
Kernel_4x4 ker = {}; //perf: initialization is negligible
ker.a = s_m1[x_m1]; //read sequentially from memory as far as possible
ker.b = s_m1[x];
ker.c = s_m1[x_p1];
ker.d = s_m1[x_p2];
oobReader.readDhlp(ker4, x);
ker.e = s_0[x_m1];
ker.f = s_0[x];
ker.g = s_0[x_p1];
ker.h = s_0[x_p2];
/* preprocessing blend result:
---------
| F | G | evaluate corner between F, G, J, K
|---+---| current input pixel is at position F
| J | K |
--------- */
const BlendResult res = preProcessCorners<ColorDistance>(ker4, cfg);
addTopR(preProcBuf[x], res.blend_j); //set 2nd known corner for (x, yFirst)
ker.i = s_p1[x_m1];
ker.j = s_p1[x];
ker.k = s_p1[x_p1];
ker.l = s_p1[x_p2];
ker.m = s_p2[x_m1];
ker.n = s_p2[x];
ker.o = s_p2[x_p1];
ker.p = s_p2[x_p2];
const BlendResult res = preProcessCorners<ColorDistance>(ker, cfg);
/*
preprocessing blend result:
---------
| F | G | //evalute corner between F, G, J, K
----|---| //input pixel is at position F
| J | K |
---------
*/
setTopR(preProcBuffer[x], res.blend_j);
if (x + 1 < bufferSize)
setTopL(preProcBuffer[x + 1], res.blend_k);
if (x + 1 < srcWidth)
clearAddTopL(preProcBuf[x + 1], res.blend_k); //set 1st known corner for (x + 1, yFirst)
}
}
//------------------------------------------------------------------------------------
@@ -541,91 +624,92 @@ void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight,
{
uint32_t* out = trg + Scaler::scale * y * trgWidth; //consider MT "striped" access
const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0);
const uint32_t* s_0 = src + srcWidth * y; //center line
const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1);
const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1);
const OobReader oobReader(src, srcWidth, srcHeight, y);
//initialize at position x = -1
Kernel_4x4 ker4 = {};
oobReader.readDhlp(ker4, -4); //hack: read a, e, i, m at x = -1
ker4.a = ker4.d;
ker4.e = ker4.h;
ker4.i = ker4.l;
ker4.m = ker4.p;
oobReader.readDhlp(ker4, -3);
ker4.b = ker4.d;
ker4.f = ker4.h;
ker4.j = ker4.l;
ker4.n = ker4.p;
oobReader.readDhlp(ker4, -2);
ker4.c = ker4.d;
ker4.g = ker4.h;
ker4.k = ker4.l;
ker4.o = ker4.p;
oobReader.readDhlp(ker4, -1);
unsigned char blend_xy1 = 0; //corner blending for current (x, y + 1) position
{
const BlendResult res = preProcessCorners<ColorDistance>(ker4, cfg);
clearAddTopL(blend_xy1, res.blend_k); //set 1st known corner for (0, y + 1) and buffer for use on next column
addBottomL(preProcBuf[0], res.blend_g); //set 3rd known corner for (0, y)
}
for (int x = 0; x < srcWidth; ++x, out += Scaler::scale)
{
#if defined _MSC_VER && !defined NDEBUG
breakIntoDebugger = debugPixelX == x && debugPixelY == y;
#endif
//all those bounds checks have only insignificant impact on performance!
const int x_m1 = std::max(x - 1, 0); //perf: prefer array indexing to additional pointers!
const int x_p1 = std::min(x + 1, srcWidth - 1);
const int x_p2 = std::min(x + 2, srcWidth - 1);
ker4.a = ker4.b; //shift previous kernel to the left
ker4.e = ker4.f; // -----------------
ker4.i = ker4.j; // | A | B | C | D |
ker4.m = ker4.n; // |---|---|---|---|
/**/ // | E | F | G | H | (x, y) is at position F
ker4.b = ker4.c; // |---|---|---|---|
ker4.f = ker4.g; // | I | J | K | L |
ker4.j = ker4.k; // |---|---|---|---|
ker4.n = ker4.o; // | M | N | O | P |
/**/ // -----------------
ker4.c = ker4.d;
ker4.g = ker4.h;
ker4.k = ker4.l;
ker4.o = ker4.p;
Kernel_4x4 ker4 = {}; //perf: initialization is negligible
ker4.a = s_m1[x_m1]; //read sequentially from memory as far as possible
ker4.b = s_m1[x];
ker4.c = s_m1[x_p1];
ker4.d = s_m1[x_p2];
ker4.e = s_0[x_m1];
ker4.f = s_0[x];
ker4.g = s_0[x_p1];
ker4.h = s_0[x_p2];
ker4.i = s_p1[x_m1];
ker4.j = s_p1[x];
ker4.k = s_p1[x_p1];
ker4.l = s_p1[x_p2];
ker4.m = s_p2[x_m1];
ker4.n = s_p2[x];
ker4.o = s_p2[x_p1];
ker4.p = s_p2[x_p2];
oobReader.readDhlp(ker4, x);
//evaluate the four corners on bottom-right of current pixel
unsigned char blend_xy = 0; //for current (x, y) position
unsigned char blend_xy = preProcBuf[x]; //for current (x, y) position
{
/* preprocessing blend result:
---------
| F | G | evaluate corner between F, G, J, K
|---+---| current input pixel is at position F
| J | K |
--------- */
const BlendResult res = preProcessCorners<ColorDistance>(ker4, cfg);
/*
preprocessing blend result:
---------
| F | G | //evalute corner between F, G, J, K
----|---| //current input pixel is at position F
| J | K |
---------
*/
blend_xy = preProcBuffer[x];
setBottomR(blend_xy, res.blend_f); //all four corners of (x, y) have been determined at this point due to processing sequence!
addBottomR(blend_xy, res.blend_f); //all four corners of (x, y) have been determined at this point due to processing sequence!
setTopR(blend_xy1, res.blend_j); //set 2nd known corner for (x, y + 1)
preProcBuffer[x] = blend_xy1; //store on current buffer position for use on next row
addTopR(blend_xy1, res.blend_j); //set 2nd known corner for (x, y + 1)
preProcBuf[x] = blend_xy1; //store on current buffer position for use on next row
blend_xy1 = 0;
setTopL(blend_xy1, res.blend_k); //set 1st known corner for (x + 1, y + 1) and buffer for use on next column
[[likely]] if (x + 1 < srcWidth)
{
//blend_xy1 -> blend_x1y1
clearAddTopL(blend_xy1, res.blend_k); //set 1st known corner for (x + 1, y + 1) and buffer for use on next column
if (x + 1 < bufferSize) //set 3rd known corner for (x + 1, y)
setBottomL(preProcBuffer[x + 1], res.blend_g);
addBottomL(preProcBuf[x + 1], res.blend_g); //set 3rd known corner for (x + 1, y)
}
}
//fill block of size scale * scale with the given color
fillBlock(out, trgWidth * sizeof(uint32_t), ker4.f, Scaler::scale, Scaler::scale);
//place *after* preprocessing step, to not overwrite the results while processing the the last pixel!
//place *after* preprocessing step, to not overwrite the results while processing the last pixel!
//blend four corners of current pixel
if (blendingNeeded(blend_xy)) //good 5% perf-improvement
//blend all four corners of current pixel
if (blendingNeeded(blend_xy))
{
Kernel_3x3 ker3 = {}; //perf: initialization is negligible
ker3.a = ker4.a;
ker3.b = ker4.b;
ker3.c = ker4.c;
ker3.d = ker4.e;
ker3.e = ker4.f;
ker3.f = ker4.g;
ker3.g = ker4.i;
ker3.h = ker4.j;
ker3.i = ker4.k;
const auto& ker3 = reinterpret_cast<const Kernel_3x3&>(ker4); //"The Things We Do for Perf"
blendPixel<Scaler, ColorDistance, ROT_0 >(ker3, out, trgWidth, blend_xy, cfg);
blendPixel<Scaler, ColorDistance, ROT_90 >(ker3, out, trgWidth, blend_xy, cfg);
blendPixel<Scaler, ColorDistance, ROT_180>(ker3, out, trgWidth, blend_xy, cfg);
@@ -1037,6 +1121,22 @@ struct ColorDistanceARGB
};
struct ColorDistanceUnbufferedARGB
{
static double dist(uint32_t pix1, uint32_t pix2, double luminanceWeight)
{
const double a1 = getAlpha(pix1) / 255.0 ;
const double a2 = getAlpha(pix2) / 255.0 ;
const double d = distYCbCr(pix1, pix2, luminanceWeight);
if (a1 < a2)
return a1 * d + 255 * (a2 - a1);
else
return a2 * d + 255 * (a1 - a2);
}
};
struct ColorGradientRGB
{
template <unsigned int M, unsigned int N>
@@ -1059,37 +1159,60 @@ struct ColorGradientARGB
void xbrz::scale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, ColorFormat colFmt, const xbrz::ScalerCfg& cfg, int yFirst, int yLast)
{
if (factor == 1)
{
std::copy(src + yFirst * srcWidth, src + yLast * srcWidth, trg);
return;
}
static_assert(SCALE_FACTOR_MAX == 6);
switch (colFmt)
{
case ColorFormat::ARGB:
switch (factor)
{
case 2:
return scaleImage<Scaler2x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
case 3:
return scaleImage<Scaler3x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
case 4:
return scaleImage<Scaler4x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
case 5:
return scaleImage<Scaler5x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
case 6:
return scaleImage<Scaler6x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
}
break;
case ColorFormat::RGB:
switch (factor)
{
case 2:
return scaleImage<Scaler2x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
return scaleImage<Scaler2x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
case 3:
return scaleImage<Scaler3x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
return scaleImage<Scaler3x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
case 4:
return scaleImage<Scaler4x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
return scaleImage<Scaler4x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
case 5:
return scaleImage<Scaler5x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
return scaleImage<Scaler5x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
case 6:
return scaleImage<Scaler6x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
return scaleImage<Scaler6x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
}
break;
case ColorFormat::ARGB:
switch (factor)
{
case 2:
return scaleImage<Scaler2x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
case 3:
return scaleImage<Scaler3x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
case 4:
return scaleImage<Scaler4x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
case 5:
return scaleImage<Scaler5x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
case 6:
return scaleImage<Scaler6x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
}
break;
case ColorFormat::ARGB_UNBUFFERED:
switch (factor)
{
case 2:
return scaleImage<Scaler2x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
case 3:
return scaleImage<Scaler3x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
case 4:
return scaleImage<Scaler4x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
case 5:
return scaleImage<Scaler5x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
case 6:
return scaleImage<Scaler6x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
}
break;
}
@@ -1101,21 +1224,133 @@ bool xbrz::equalColorTest(uint32_t col1, uint32_t col2, ColorFormat colFmt, doub
{
switch (colFmt)
{
case ColorFormat::ARGB:
return ColorDistanceARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
case ColorFormat::RGB:
return ColorDistanceRGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
case ColorFormat::ARGB:
return ColorDistanceARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
case ColorFormat::ARGB_UNBUFFERED:
return ColorDistanceUnbufferedARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
}
assert(false);
return false;
}
void xbrz::bilinearScale(const uint32_t* src, int srcWidth, int srcHeight,
/**/ uint32_t* trg, int trgWidth, int trgHeight)
{
bilinearScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t),
0, trgHeight, [](uint32_t pix) { return pix; });
}
void xbrz::nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight,
uint32_t* trg, int trgWidth, int trgHeight)
/**/ uint32_t* trg, int trgWidth, int trgHeight)
{
nearestNeighborScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t),
SliceType::TARGET, 0, trgHeight, [](uint32_t pix) { return pix; });
0, trgHeight, [](uint32_t pix) { return pix; });
}
#if 0
//#include <ppl.h>
void bilinearScaleCpu(const uint32_t* src, int srcWidth, int srcHeight,
/**/ uint32_t* trg, int trgWidth, int trgHeight)
{
const int TASK_GRANULARITY = 16;
concurrency::task_group tg;
for (int i = 0; i < trgHeight; i += TASK_GRANULARITY)
tg.run([=]
{
const int iLast = std::min(i + TASK_GRANULARITY, trgHeight);
xbrz::bilinearScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t),
i, iLast, [](uint32_t pix) { return pix; });
});
tg.wait();
}
//Perf: AMP vs CPU: merely ~10% shorter runtime (scaling 1280x800 -> 1920x1080)
//#include <amp.h>
void bilinearScaleAmp(const uint32_t* src, int srcWidth, int srcHeight, //throw concurrency::runtime_exception
/**/ uint32_t* trg, int trgWidth, int trgHeight)
{
//C++ AMP reference: https://msdn.microsoft.com/en-us/library/hh289390.aspx
//introduction to C++ AMP: https://msdn.microsoft.com/en-us/magazine/hh882446.aspx
using namespace concurrency;
//TODO: pitch
if (srcHeight <= 0 || srcWidth <= 0) return;
const float scaleX = static_cast<float>(trgWidth ) / srcWidth;
const float scaleY = static_cast<float>(trgHeight) / srcHeight;
array_view<const uint32_t, 2> srcView(srcHeight, srcWidth, src);
array_view< uint32_t, 2> trgView(trgHeight, trgWidth, trg);
trgView.discard_data();
parallel_for_each(trgView.extent, [=](index<2> idx) restrict(amp) //throw ?
{
const int y = idx[0];
const int x = idx[1];
//Perf notes:
// -> float-based calculation is (almost) 2x as fas as double!
// -> no noticeable improvement via tiling: https://msdn.microsoft.com/en-us/magazine/hh882447.aspx
// -> no noticeable improvement with restrict(amp,cpu)
// -> iterating over y-axis only is significantly slower!
// -> pre-calculating x,y-dependent variables in a buffer + array_view<> is ~ 20 % slower!
const int y1 = srcHeight * y / trgHeight;
int y2 = y1 + 1;
if (y2 == srcHeight) --y2;
const float yy1 = y / scaleY - y1;
const float y2y = 1 - yy1;
//-------------------------------------
const int x1 = srcWidth * x / trgWidth;
int x2 = x1 + 1;
if (x2 == srcWidth) --x2;
const float xx1 = x / scaleX - x1;
const float x2x = 1 - xx1;
//-------------------------------------
const float x2xy2y = x2x * y2y;
const float xx1y2y = xx1 * y2y;
const float x2xyy1 = x2x * yy1;
const float xx1yy1 = xx1 * yy1;
auto interpolate = [=](int offset)
{
/*
https://en.wikipedia.org/wiki/Bilinear_interpolation
(c11(x2 - x) + c21(x - x1)) * (y2 - y ) +
(c12(x2 - x) + c22(x - x1)) * (y - y1)
*/
const auto c11 = (srcView(y1, x1) >> (8 * offset)) & 0xff;
const auto c21 = (srcView(y1, x2) >> (8 * offset)) & 0xff;
const auto c12 = (srcView(y2, x1) >> (8 * offset)) & 0xff;
const auto c22 = (srcView(y2, x2) >> (8 * offset)) & 0xff;
return c11 * x2xy2y + c21 * xx1y2y +
c12 * x2xyy1 + c22 * xx1yy1;
};
const float bi = interpolate(0);
const float gi = interpolate(1);
const float ri = interpolate(2);
const float ai = interpolate(3);
const auto b = static_cast<uint32_t>(bi + 0.5f);
const auto g = static_cast<uint32_t>(gi + 0.5f);
const auto r = static_cast<uint32_t>(ri + 0.5f);
const auto a = static_cast<uint32_t>(ai + 0.5f);
trgView(y, x) = (a << 24) | (r << 16) | (g << 8) | b;
});
trgView.synchronize(); //throw ?
}
#endif
+13 -11
View File
@@ -1,13 +1,14 @@
// ****************************************************************************
// * This file is part of the HqMAME project. It is distributed under *
// * This file is part of the xBRZ project. It is distributed under *
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// * *
// * Additionally and as a special exception, the author gives permission *
// * to link the code of this program with the MAME library (or with modified *
// * versions of MAME that use the same license as MAME), and distribute *
// * linked combinations including the two. You must obey the GNU General *
// * Public License in all respects for all of the code used other than MAME. *
// * to link the code of this program with the following libraries *
// * (or with modified versions that use the same licenses), and distribute *
// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe *
// * You must obey the GNU General Public License in all respects for all of *
// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. *
// * If you modify this file, you may extend this exception to your version *
// * of the file, but you are not obligated to do so. If you do not wish to *
// * do so, delete this exception statement from your version. *
@@ -21,10 +22,6 @@
#include <limits>
#include "xbrz_config.h"
// Guard against msvc stupidity
#ifdef max
#undef max
#endif
namespace xbrz
{
@@ -47,11 +44,13 @@ enum class ColorFormat //from high bits -> low bits, 8 bit per channel
{
RGB, //8 bit for each red, green, blue, upper 8 bits unused
ARGB, //including alpha channel, BGRA byte order on little-endian machines
ARGB_UNBUFFERED, //like ARGB, but without the one-time buffer creation overhead (ca. 100 - 300 ms) at the expense of a slightly slower scaling time
};
const int SCALE_FACTOR_MAX = 6;
/*
-> map source (srcWidth * srcHeight) to target (scale * width x scale * height) image, optionally processing a half-open slice of rows [yFirst, yLast) only
-> support for source/target pitch in bytes!
-> if your emulator changes only a few image slices during each cycle (e.g. DOSBox) then there's no need to run xBRZ on the complete image:
Just make sure you enlarge the source image slice by 2 rows on top and 2 on bottom (this is the additional range the xBRZ algorithm is using during analysis)
CAVEAT: If there are multiple changed slices, make sure they do not overlap after adding these additional rows in order to avoid a memory race condition
@@ -60,12 +59,15 @@ enum class ColorFormat //from high bits -> low bits, 8 bit per channel
THREAD-SAFETY: - parts of the same image may be scaled by multiple threads as long as the [yFirst, yLast) ranges do not overlap!
- there is a minor inefficiency for the first row of a slice, so avoid processing single rows only; suggestion: process at least 8-16 rows
*/
void scale(size_t factor, //valid range: 2 - 6
void scale(size_t factor, //valid range: 2 - SCALE_FACTOR_MAX
const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight,
ColorFormat colFmt,
const ScalerCfg& cfg = ScalerCfg(),
int yFirst = 0, int yLast = std::numeric_limits<int>::max()); //slice of source image
void bilinearScale(const uint32_t* src, int srcWidth, int srcHeight,
/**/ uint32_t* trg, int trgWidth, int trgHeight);
void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight,
/**/ uint32_t* trg, int trgWidth, int trgHeight);
+7 -5
View File
@@ -1,13 +1,14 @@
// ****************************************************************************
// * This file is part of the HqMAME project. It is distributed under *
// * This file is part of the xBRZ project. It is distributed under *
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// * *
// * Additionally and as a special exception, the author gives permission *
// * to link the code of this program with the MAME library (or with modified *
// * versions of MAME that use the same license as MAME), and distribute *
// * linked combinations including the two. You must obey the GNU General *
// * Public License in all respects for all of the code used other than MAME. *
// * to link the code of this program with the following libraries *
// * (or with modified versions that use the same licenses), and distribute *
// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe *
// * You must obey the GNU General Public License in all respects for all of *
// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. *
// * If you modify this file, you may extend this exception to your version *
// * of the file, but you are not obligated to do so. If you do not wish to *
// * do so, delete this exception statement from your version. *
@@ -24,6 +25,7 @@ struct ScalerCfg
{
double luminanceWeight = 1;
double equalColorTolerance = 30;
double centerDirectionBias = 4;
double dominantDirectionThreshold = 3.6;
double steepDirectionThreshold = 2.2;
double newTestAttribute = 0; //unused; test new parameters
+182 -71
View File
@@ -1,13 +1,14 @@
// ****************************************************************************
// * This file is part of the HqMAME project. It is distributed under *
// * This file is part of the xBRZ project. It is distributed under *
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// * *
// * Additionally and as a special exception, the author gives permission *
// * to link the code of this program with the MAME library (or with modified *
// * versions of MAME that use the same license as MAME), and distribute *
// * linked combinations including the two. You must obey the GNU General *
// * Public License in all respects for all of the code used other than MAME. *
// * to link the code of this program with the following libraries *
// * (or with modified versions that use the same licenses), and distribute *
// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe *
// * You must obey the GNU General Public License in all respects for all of *
// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. *
// * If you modify this file, you may extend this exception to your version *
// * of the file, but you are not obligated to do so. If you do not wish to *
// * do so, delete this exception statement from your version. *
@@ -25,14 +26,14 @@ namespace xbrz
{
template <uint32_t N> inline
unsigned char getByte(uint32_t val) { return static_cast<unsigned char>((val >> (8 * N)) & 0xff); }
// Note that we use RGBA, not ARGB
inline unsigned char getAlpha(uint32_t pix) { return getByte<3>(pix); }
inline unsigned char getRed (uint32_t pix) { return getByte<0>(pix); }
inline unsigned char getGreen(uint32_t pix) { return getByte<1>(pix); }
inline unsigned char getBlue (uint32_t pix) { return getByte<2>(pix); }
inline uint32_t makePixel(unsigned char a, unsigned char r, unsigned char g, unsigned char b) { return (a << 24) | (b << 16) | (g << 8) | r; }
inline uint32_t makePixel( unsigned char r, unsigned char g, unsigned char b) { return 0xFF000000 | (b << 16) | (g << 8) | r; }
inline unsigned char getAlpha(uint32_t pix) { return getByte<3>(pix); }
inline unsigned char getRed (uint32_t pix) { return getByte<2>(pix); }
inline unsigned char getGreen(uint32_t pix) { return getByte<1>(pix); }
inline unsigned char getBlue (uint32_t pix) { return getByte<0>(pix); }
inline uint32_t makePixel(unsigned char a, unsigned char r, unsigned char g, unsigned char b) { return (a << 24) | (r << 16) | (g << 8) | b; }
inline uint32_t makePixel( unsigned char r, unsigned char g, unsigned char b) { return (r << 16) | (g << 8) | b; }
inline uint32_t rgb555to888(uint16_t pix) { return ((pix & 0x7C00) << 9) | ((pix & 0x03E0) << 6) | ((pix & 0x001F) << 3); }
inline uint32_t rgb565to888(uint16_t pix) { return ((pix & 0xF800) << 8) | ((pix & 0x07E0) << 5) | ((pix & 0x001F) << 3); }
@@ -55,7 +56,7 @@ Pix* byteAdvance(Pix* ptr, int bytes)
//fill block with the given color
template <class Pix> inline
void fillBlock(Pix* trg, int pitch, Pix col, int blockWidth, int blockHeight)
void fillBlock(Pix* trg, int pitch /*[bytes]*/, Pix col, int blockWidth, int blockHeight)
{
//for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch))
// std::fill(trg, trg + blockWidth, col);
@@ -66,16 +67,11 @@ void fillBlock(Pix* trg, int pitch, Pix col, int blockWidth, int blockHeight)
}
enum class SliceType
{
SOURCE,
TARGET,
};
//nearest-neighbor (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!)
template <class PixSrc, class PixTrg, class PixConverter>
void nearestNeighborScale(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch,
/**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch,
SliceType st, int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/)
void nearestNeighborScale(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch /*[bytes]*/,
/**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch /*[bytes]*/,
int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/)
{
static_assert(std::is_integral<PixSrc>::value, "PixSrc* is expected to be cast-able to char*");
static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
@@ -89,65 +85,180 @@ void nearestNeighborScale(const PixSrc* src, int srcWidth, int srcHeight, int sr
return;
}
switch (st)
yFirst = std::max(yFirst, 0);
yLast = std::min(yLast, trgHeight);
if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return;
for (int y = yFirst; y < yLast; ++y)
{
case SliceType::SOURCE:
//nearest-neighbor (going over source image - fast for upscaling, since source is read only once
yFirst = std::max(yFirst, 0);
yLast = std::min(yLast, srcHeight);
if (yFirst >= yLast || trgWidth <= 0 || trgHeight <= 0) return;
const int ySrc = srcHeight * y / trgHeight;
const PixSrc* const srcLine = byteAdvance(src, ySrc * srcPitch);
PixTrg* const trgLine = byteAdvance(trg, y * trgPitch);
for (int y = yFirst; y < yLast; ++y)
for (int x = 0; x < trgWidth; ++x)
{
const int xSrc = srcWidth * x / trgWidth;
trgLine[x] = pixCvrt(srcLine[xSrc]);
}
}
}
//nearest-neighbor (going over source image - fast for upscaling, since source is read only once
template <class PixSrc, class PixTrg, class PixConverter>
void nearestNeighborScaleOverSource(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch /*[bytes]*/,
/**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch /*[bytes]*/,
int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/)
{
static_assert(std::is_integral<PixSrc>::value, "PixSrc* is expected to be cast-able to char*");
static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
static_assert(std::is_same<decltype(pixCvrt(PixSrc())), PixTrg>::value, "PixConverter returning wrong pixel format");
if (srcPitch < srcWidth * static_cast<int>(sizeof(PixSrc)) ||
trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg)))
{
assert(false);
return;
}
yFirst = std::max(yFirst, 0);
yLast = std::min(yLast, srcHeight);
if (yFirst >= yLast || trgWidth <= 0 || trgHeight <= 0) return;
for (int y = yFirst; y < yLast; ++y)
{
//mathematically: ySrc = floor(srcHeight * yTrg / trgHeight)
// => search for integers in: [ySrc, ySrc + 1) * trgHeight / srcHeight
//keep within for loop to support MT input slices!
const int yTrgFirst = ( y * trgHeight + srcHeight - 1) / srcHeight; //=ceil(y * trgHeight / srcHeight)
const int yTrgLast = ((y + 1) * trgHeight + srcHeight - 1) / srcHeight; //=ceil(((y + 1) * trgHeight) / srcHeight)
const int blockHeight = yTrgLast - yTrgFirst;
if (blockHeight > 0)
{
const PixSrc* srcLine = byteAdvance(src, y * srcPitch);
/**/ PixTrg* trgLine = byteAdvance(trg, yTrgFirst * trgPitch);
int xTrgFirst = 0;
for (int x = 0; x < srcWidth; ++x)
{
//mathematically: ySrc = floor(srcHeight * yTrg / trgHeight)
// => search for integers in: [ySrc, ySrc + 1) * trgHeight / srcHeight
//keep within for loop to support MT input slices!
const int yTrg_first = ( y * trgHeight + srcHeight - 1) / srcHeight; //=ceil(y * trgHeight / srcHeight)
const int yTrg_last = ((y + 1) * trgHeight + srcHeight - 1) / srcHeight; //=ceil(((y + 1) * trgHeight) / srcHeight)
const int blockHeight = yTrg_last - yTrg_first;
if (blockHeight > 0)
const int xTrgLast = ((x + 1) * trgWidth + srcWidth - 1) / srcWidth;
const int blockWidth = xTrgLast - xTrgFirst;
if (blockWidth > 0)
{
const PixSrc* srcLine = byteAdvance(src, y * srcPitch);
/**/ PixTrg* trgLine = byteAdvance(trg, yTrg_first * trgPitch);
int xTrg_first = 0;
xTrgFirst = xTrgLast;
for (int x = 0; x < srcWidth; ++x)
{
const int xTrg_last = ((x + 1) * trgWidth + srcWidth - 1) / srcWidth;
const int blockWidth = xTrg_last - xTrg_first;
if (blockWidth > 0)
{
xTrg_first = xTrg_last;
const auto trgPix = pixCvrt(srcLine[x]);
fillBlock(trgLine, trgPitch, trgPix, blockWidth, blockHeight);
trgLine += blockWidth;
}
}
const auto trgPix = pixCvrt(srcLine[x]);
fillBlock(trgLine, trgPitch, trgPix, blockWidth, blockHeight);
trgLine += blockWidth;
}
}
break;
}
}
}
case SliceType::TARGET:
//nearest-neighbor (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!)
yFirst = std::max(yFirst, 0);
yLast = std::min(yLast, trgHeight);
if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return;
for (int y = yFirst; y < yLast; ++y)
template <class PixTrg, class PixConverter>
void bilinearScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch,
/**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch,
int yFirst, int yLast, PixConverter pixCvrt /*convert uint32_t to PixTrg*/)
{
static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
static_assert(std::is_same<decltype(pixCvrt(uint32_t())), PixTrg>::value, "PixConverter returning wrong pixel format");
if (srcPitch < srcWidth * static_cast<int>(sizeof(uint32_t)) ||
trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg)))
{
assert(false);
return;
}
yFirst = std::max(yFirst, 0);
yLast = std::min(yLast, trgHeight);
if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return;
const double scaleX = static_cast<double>(trgWidth ) / srcWidth;
const double scaleY = static_cast<double>(trgHeight) / srcHeight;
//perf notes:
// -> double-based calculation is (slightly) faster than float
// -> pre-calculation gives significant boost; std::vector<> memory allocation is negligible!
struct CoeffsX
{
int x1 = 0;
int x2 = 0;
double xx1 = 0;
double x2x = 0;
};
std::vector<CoeffsX> buf(trgWidth);
for (int x = 0; x < trgWidth; ++x)
{
const int x1 = srcWidth * x / trgWidth;
int x2 = x1 + 1;
if (x2 == srcWidth) --x2;
const double xx1 = x / scaleX - x1;
const double x2x = 1 - xx1;
buf[x] = { x1, x2, xx1, x2x };
}
for (int y = yFirst; y < yLast; ++y)
{
const int y1 = srcHeight * y / trgHeight;
int y2 = y1 + 1;
if (y2 == srcHeight) --y2;
const double yy1 = y / scaleY - y1;
const double y2y = 1 - yy1;
const uint32_t* const srcLine = byteAdvance(src, y1 * srcPitch);
const uint32_t* const srcLineNext = byteAdvance(src, y2 * srcPitch);
PixTrg* const trgLine = byteAdvance(trg, y * trgPitch);
for (int x = 0; x < trgWidth; ++x)
{
//perf: do NOT "simplify" the variable layout without measurement!
const int x1 = buf[x].x1;
const int x2 = buf[x].x2;
const double xx1 = buf[x].xx1;
const double x2x = buf[x].x2x;
const double x2xy2y = x2x * y2y;
const double xx1y2y = xx1 * y2y;
const double x2xyy1 = x2x * yy1;
const double xx1yy1 = xx1 * yy1;
auto interpolate = [=](int offset)
{
PixTrg* trgLine = byteAdvance(trg, y * trgPitch);
const int ySrc = srcHeight * y / trgHeight;
const PixSrc* srcLine = byteAdvance(src, ySrc * srcPitch);
for (int x = 0; x < trgWidth; ++x)
{
const int xSrc = srcWidth * x / trgWidth;
trgLine[x] = pixCvrt(srcLine[xSrc]);
}
}
break;
/* https://en.wikipedia.org/wiki/Bilinear_interpolation
(c11(x2 - x) + c21(x - x1)) * (y2 - y ) +
(c12(x2 - x) + c22(x - x1)) * (y - y1) */
const auto c11 = (srcLine [x1] >> (8 * offset)) & 0xff;
const auto c21 = (srcLine [x2] >> (8 * offset)) & 0xff;
const auto c12 = (srcLineNext[x1] >> (8 * offset)) & 0xff;
const auto c22 = (srcLineNext[x2] >> (8 * offset)) & 0xff;
return c11 * x2xy2y + c21 * xx1y2y +
c12 * x2xyy1 + c22 * xx1yy1;
};
const double bi = interpolate(0);
const double gi = interpolate(1);
const double ri = interpolate(2);
const double ai = interpolate(3);
const auto b = static_cast<uint32_t>(bi + 0.5);
const auto g = static_cast<uint32_t>(gi + 0.5);
const auto r = static_cast<uint32_t>(ri + 0.5);
const auto a = static_cast<uint32_t>(ai + 0.5);
const uint32_t trgPix = (a << 24) | (r << 16) | (g << 8) | b;
trgLine[x] = pixCvrt(trgPix);
}
}
}
}
+1
Submodule core/deps/xbyak added at 0d67fd1530.
-11
View File
@@ -1,11 +0,0 @@
name: test
on: [push]
jobs:
build:
name: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: sudo apt install nasm yasm g++-multilib tcsh
- run: make test
-1
View File
@@ -1 +0,0 @@
/build* # cmake
-46
View File
@@ -1,46 +0,0 @@
cmake_minimum_required(VERSION 2.6...3.0.2)
project(xbyak CXX)
file(GLOB headers xbyak/*.h)
if (DEFINED CMAKE_VERSION AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.2)
include(GNUInstallDirs)
add_library(${PROJECT_NAME} INTERFACE)
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
target_include_directories(
${PROJECT_NAME} INTERFACE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)
install(
TARGETS ${PROJECT_NAME}
EXPORT ${PROJECT_NAME}-targets
)
configure_file(
cmake/config.cmake.in
${PROJECT_NAME}Config.cmake
@ONLY
)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
)
install(
EXPORT ${PROJECT_NAME}-targets
NAMESPACE ${PROJECT_NAME}::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
)
elseif(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR)
set(CMAKE_INSTALL_INCLUDEDIR "include")
endif()
install(
FILES ${headers}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/xbyak
)
-47
View File
@@ -1,47 +0,0 @@
Copyright (c) 2007 MITSUNARI Shigeo
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
Neither the name of the copyright owner nor the names of its contributors may
be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
-----------------------------------------------------------------------------
ソースコード形式かバイナリ形式か、変更するかしないかを問わず、以下の条件を満た
す場合に限り、再頒布および使用が許可されます。
ソースコードを再頒布する場合、上記の著作権表示、本条件一覧、および下記免責条項
を含めること。
バイナリ形式で再頒布する場合、頒布物に付属のドキュメント等の資料に、上記の著作
権表示、本条件一覧、および下記免責条項を含めること。
書面による特別の許可なしに、本ソフトウェアから派生した製品の宣伝または販売促進
に、著作権者の名前またはコントリビューターの名前を使用してはならない。
本ソフトウェアは、著作権者およびコントリビューターによって「現状のまま」提供さ
れており、明示黙示を問わず、商業的な使用可能性、および特定の目的に対する適合性
に関する暗黙の保証も含め、またそれに限定されない、いかなる保証もありません。
著作権者もコントリビューターも、事由のいかんを問わず、 損害発生の原因いかんを
問わず、かつ責任の根拠が契約であるか厳格責任であるか(過失その他の)不法行為で
あるかを問わず、仮にそのような損害が発生する可能性を知らされていたとしても、
本ソフトウェアの使用によって発生した(代替品または代用サービスの調達、使用の
喪失、データの喪失、利益の喪失、業務の中断も含め、またそれに限定されない)直接
損害、間接損害、偶発的な損害、特別損害、懲罰的損害、または結果損害について、
一切責任を負わないものとします。
-24
View File
@@ -1,24 +0,0 @@
PREFIX?=/usr/local
INSTALL_DIR=$(PREFIX)/include/xbyak
all:
$(MAKE) -C sample
clean:
$(MAKE) -C sample clean
install:
mkdir -p $(INSTALL_DIR)
cp -pR xbyak/*.h $(INSTALL_DIR)
uninstall:
rm -i $(INSTALL_DIR)/*.h
rmdir $(INSTALL_DIR)
update:
$(MAKE) -C gen
test:
$(MAKE) -C test test
.PHONY: test update
-1
View File
@@ -1 +0,0 @@
include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
-26
View File
@@ -1,26 +0,0 @@
TARGET=../xbyak/xbyak_mnemonic.h
BIN=sortline gen_code gen_avx512
CFLAGS=-I../ -O2 -DXBYAK_NO_OP_NAMES -Wall -Wextra -Wno-missing-field-initializers
all: $(TARGET)
sortline: sortline.cpp
$(CXX) $(CFLAGS) $< -o $@
gen_code: gen_code.cpp ../xbyak/xbyak.h avx_type.hpp
$(CXX) $(CFLAGS) $< -o $@
gen_avx512: gen_avx512.cpp ../xbyak/xbyak.h avx_type.hpp
$(CXX) $(CFLAGS) $< -o $@
$(TARGET): $(BIN)
./gen_code | ./sortline > $@
echo "#ifdef XBYAK_ENABLE_OMITTED_OPERAND" >> $@
./gen_code omit | ./sortline >> $@
echo "#endif" >>$@
./gen_code fixed >> $@
echo "#ifndef XBYAK_DISABLE_AVX512" >> $@
./gen_avx512 | ./sortline >> $@
echo "#ifdef XBYAK64" >> $@
./gen_avx512 64 | ./sortline >> $@
echo "#endif" >> $@
echo "#endif" >> $@
clean:
$(RM) $(BIN) $(TARGET)
-170
View File
@@ -1,170 +0,0 @@
#include <assert.h>
// copy CodeGenerator::AVXtype
enum AVXtype {
// low 3 bit
T_N1 = 1,
T_N2 = 2,
T_N4 = 3,
T_N8 = 4,
T_N16 = 5,
T_N32 = 6,
T_NX_MASK = 7,
//
T_N_VL = 1 << 3, // N * (1, 2, 4) for VL
T_DUP = 1 << 4, // N = (8, 32, 64)
T_66 = 1 << 5,
T_F3 = 1 << 6,
T_F2 = 1 << 7,
T_0F = 1 << 8,
T_0F38 = 1 << 9,
T_0F3A = 1 << 10,
T_L0 = 1 << 11,
T_L1 = 1 << 12,
T_W0 = 1 << 13,
T_W1 = 1 << 14,
T_EW0 = 1 << 15,
T_EW1 = 1 << 16,
T_YMM = 1 << 17, // support YMM, ZMM
T_EVEX = 1 << 18,
T_ER_X = 1 << 19, // xmm{er}
T_ER_Y = 1 << 20, // ymm{er}
T_ER_Z = 1 << 21, // zmm{er}
T_SAE_X = 1 << 22, // xmm{sae}
T_SAE_Y = 1 << 23, // ymm{sae}
T_SAE_Z = 1 << 24, // zmm{sae}
T_MUST_EVEX = 1 << 25, // contains T_EVEX
T_B32 = 1 << 26, // m32bcst
T_B64 = 1 << 27, // m64bcst
T_M_K = 1 << 28, // mem{k}
T_VSIB = 1 << 29,
T_MEM_EVEX = 1 << 30, // use evex if mem
T_XXX
};
const int NONE = 256; // same as Xbyak::CodeGenerator::NONE
std::string type2String(int type)
{
std::string str;
int low = type & T_NX_MASK;
if (0 < low) {
const char *tbl[8] = {
"T_N1", "T_N2", "T_N4", "T_N8", "T_N16", "T_N32"
};
assert(low < int(sizeof(tbl) / sizeof(tbl[0])));
str = tbl[low - 1];
}
if (type & T_N_VL) {
if (!str.empty()) str += " | ";
str += "T_N_VL";
}
if (type & T_DUP) {
if (!str.empty()) str += " | ";
str += "T_DUP";
}
if (type & T_66) {
if (!str.empty()) str += " | ";
str += "T_66";
}
if (type & T_F3) {
if (!str.empty()) str += " | ";
str += "T_F3";
}
if (type & T_F2) {
if (!str.empty()) str += " | ";
str += "T_F2";
}
if (type & T_0F) {
if (!str.empty()) str += " | ";
str += "T_0F";
}
if (type & T_0F38) {
if (!str.empty()) str += " | ";
str += "T_0F38";
}
if (type & T_0F3A) {
if (!str.empty()) str += " | ";
str += "T_0F3A";
}
if (type & T_L0) {
if (!str.empty()) str += " | ";
str += "VEZ_L0";
}
if (type & T_L1) {
if (!str.empty()) str += " | ";
str += "VEZ_L1";
}
if (type & T_W0) {
if (!str.empty()) str += " | ";
str += "T_W0";
}
if (type & T_W1) {
if (!str.empty()) str += " | ";
str += "T_W1";
}
if (type & T_EW0) {
if (!str.empty()) str += " | ";
str += "T_EW0";
}
if (type & T_EW1) {
if (!str.empty()) str += " | ";
str += "T_EW1";
}
if (type & T_YMM) {
if (!str.empty()) str += " | ";
str += "T_YMM";
}
if (type & T_EVEX) {
if (!str.empty()) str += " | ";
str += "T_EVEX";
}
if (type & T_ER_X) {
if (!str.empty()) str += " | ";
str += "T_ER_X";
}
if (type & T_ER_Y) {
if (!str.empty()) str += " | ";
str += "T_ER_Y";
}
if (type & T_ER_Z) {
if (!str.empty()) str += " | ";
str += "T_ER_Z";
}
if (type & T_SAE_X) {
if (!str.empty()) str += " | ";
str += "T_SAE_X";
}
if (type & T_SAE_Y) {
if (!str.empty()) str += " | ";
str += "T_SAE_Y";
}
if (type & T_SAE_Z) {
if (!str.empty()) str += " | ";
str += "T_SAE_Z";
}
if (type & T_MUST_EVEX) {
if (!str.empty()) str += " | ";
str += "T_MUST_EVEX";
}
if (type & T_B32) {
if (!str.empty()) str += " | ";
str += "T_B32";
}
if (type & T_B64) {
if (!str.empty()) str += " | ";
str += "T_B64";
}
if (type & T_M_K) {
if (!str.empty()) str += " | ";
str += "T_M_K";
}
if (type & T_VSIB) {
if (!str.empty()) str += " | ";
str += "T_VSIB";
}
if (type & T_MEM_EVEX) {
if (!str.empty()) str += " | ";
str += "T_MEM_EVEX";
}
return str;
}
Loaded 100 of 814 files, more files were not shown because too many files have changed in this diff. Show more