Compare commits

...

123 Commits

Author SHA1 Message Date
Jay M. White 52401d6808
Merge a277d66c50 into 2c83a256ae 2025-01-17 21:26:51 +01:00
Admiral H. Curtiss 2c83a256ae
Merge pull request #13277 from jordan-woyak/iowindow-debug-assert-fix
DolphinQt: Fix ASSERT failure in IOWindow in debug build.
2025-01-16 10:04:55 +01:00
Jordan Woyak c3f66e83e6 DolphinQt: Fix ASSERT failure in IOWindow in debug build. 2025-01-15 16:39:45 -06:00
OatmealDome c344514ba2
Merge pull request #13270 from OatmealDome/vk-metal-layer
Vulkan: Only attempt to create a CAMetalLayer on macOS
2025-01-15 15:08:03 -05:00
Admiral H. Curtiss 3ea870ef8c
Merge pull request #13244 from dreamsyntax/bugfix-double-osd-texture-messages
Core/VideoCommon: Fix duplicate OSD Custom Textures messages
2025-01-12 16:16:09 +01:00
Admiral H. Curtiss ede963d4db
Merge pull request #13272 from dreamsyntax/ppc-crlf
PPCSymbolDB: Fix loading maps with CRLF endings
2025-01-12 14:26:27 +01:00
Admiral H. Curtiss b0e5ebc80d
Merge pull request #13247 from sepalani/debug-map-ranges
PPCSymbolDB: Refactor SymbolMap Save/Load
2025-01-12 14:26:04 +01:00
JosJuice da0a0c87c2
Merge pull request #13274 from dpeukert/master
Clean up CRLF line endings
2025-01-12 14:11:50 +01:00
Daniel Peukert f28e134c88
Clean up CRLF line endings 2025-01-12 11:06:04 +01:00
dreamsyntax b9a2d89035 PPCSymbolDB: Fix loading maps with CRLF endings
Symbol maps ending in CRLF were not properly loading on non-windows
systems.
2025-01-10 14:37:58 -07:00
Admiral H. Curtiss 93fc5c02ac
Merge pull request #13179 from TellowKrinkle/ForceSystemLib
CMake: Optional system library fixes
2025-01-10 04:56:08 +01:00
Admiral H. Curtiss 98a80239f1
Merge pull request #13030 from JosJuice/android-wait-for-surface-in-run
Android: Wait for surface in Run
2025-01-10 04:50:34 +01:00
Admiral H. Curtiss 75abda6a3a
Merge pull request #12864 from JosJuice/android-window-compat
Android: Replace deprecated setSystemUiVisibility
2025-01-10 04:45:11 +01:00
Admiral H. Curtiss d10cb9dfc4
Merge pull request #13238 from JosJuice/jitarm64-rlwinmx-imm-mask
JitArm64: Handle rlwinmx with zero mask
2025-01-10 04:44:22 +01:00
Tilka e24e107b3c
Merge pull request #13265 from Sintendo/warnings
Fix several minor warnings
2025-01-08 21:44:24 +00:00
Tilka ffa312f8e8
Merge pull request #13158 from jordan-woyak/wiiu-rename
DolphinQt: Rename "GameCube Adapter for Wii U".
2025-01-08 21:39:08 +00:00
Admiral H. Curtiss 0e5d7d0f2d
Merge pull request #13268 from AdmiralCurtiss/ini-delete-case-sensitive
Common/IniFile: Fix case sensitivity mismatch in IniFile::Section::Delete()
2025-01-08 21:42:32 +01:00
OatmealDome 2633b84b98 Vulkan: Only attempt to create a CAMetalLayer on macOS 2025-01-08 13:51:53 -05:00
Admiral H. Curtiss 8f9e3ffd83
Merge pull request #13228 from OatmealDome/fix-curl-2
curl: Update to 8.11.1 and use CMake to configure
2025-01-08 18:14:10 +01:00
Admiral H. Curtiss 22dc21cca4
Merge pull request #13262 from Ferdi265/feature/fix-fmt11
Fix compiling with libfmt>=11.0
2025-01-08 18:12:19 +01:00
Admiral H. Curtiss c567248b73
Common/IniFile: Fix case sensitivity mismatch in IniFile::Section::Delete()
values uses a case insensitive comparison, so erasing the equivalent key in keys_order also must do so.
2025-01-08 05:30:39 +01:00
Admiral H. Curtiss 7133bfbb0e
Merge pull request #13180 from jordan-woyak/eof-logic
Core/VideoCommon: Fix some weird (!eof) logic.
2025-01-08 05:27:42 +01:00
OatmealDome d525776ae6 GeckoCodeConfig: Remove HTTPS workaround for Android 2025-01-07 23:02:13 -05:00
OatmealDome 4dc368d8df WiiUtils: Remove HTTPS workaround for Android 2025-01-07 23:02:13 -05:00
OatmealDome 0b7f399436 curl: Set CA path on Android to the system-provided store 2025-01-07 23:02:13 -05:00
OatmealDome b5918effeb curl: Update to 8.11.1 and use CMake to configure 2025-01-07 23:02:10 -05:00
OatmealDome 696b363f47
Merge pull request #13162 from jordan-woyak/non-blocking-input-detection
DolphinQt/InputCommon: Make input mapping and output testing non-blocking.
2025-01-07 16:55:55 -05:00
Sintendo 532e25be12 Fix several minor warnings 2025-01-06 11:31:39 +01:00
JosJuice b35f7af355
Merge pull request #13257 from CasualPokePlayer/dtm_country_code
Add SYSCONF country code to DTM
2025-01-06 10:39:53 +01:00
JosJuice eec2e2f07a
Merge pull request #13251 from Sintendo/carry-opts
JitArm64_Integer: Carry flag optimizations
2025-01-06 10:39:43 +01:00
JosJuice 43d5f61a60
Merge pull request #13149 from Sintendo/dcbx-msub
JitArm64_LoadStore: Small dcbx optimization
2025-01-06 09:29:08 +01:00
OatmealDome 8d9ec2fde9
Merge pull request #13210 from OatmealDome/fix-scmrevgen
ScmRevGen: Don't generate Info.plist files directly
2025-01-06 01:18:20 -05:00
JMC47 6b686be5f1
Merge pull request #13233 from TryTwo/PR_Codec
AdvancedWidget: Replace FFV1 codec with Ut Video
2025-01-04 22:26:36 -05:00
Ferdinand Bachmann b79bdb13c0 Common: Fix compile failure with fmt>=11 2025-01-04 19:13:05 +01:00
Ferdinand Bachmann 825092ad33 BBA/HLE: Fix incorrect fmt format string 2025-01-04 18:45:32 +01:00
Sepalani bbf835b30b PPCSymbolDB: Check SplitString result 2025-01-04 17:02:13 +04:00
Sepalani 77e77863dc PPCSymbolDB: Add alignment detection heuristic
Update parse_entry_of in accordance to the sscanf change
2025-01-04 15:32:52 +04:00
Sepalani 5778cb42db PPCSymbolDB: Deduplicate parsing of the 'entry of' string 2025-01-04 15:32:52 +04:00
Sepalani 5c151c11ac PPCSymbolDB: Use ranges in SaveSymbolMap 2025-01-04 15:32:52 +04:00
JMC47 9b3b6bea9d
Merge pull request #12801 from JosJuice/jitarm64-crxxx-opt
JitArm64: Optimize crXXX
2025-01-03 16:32:49 -05:00
Jordan Woyak f12846a0e9 DolphinQt: Make input mapping and output testing non-blocking. 2025-01-01 16:48:32 -06:00
Jordan Woyak bc95c001c8 InputCommon: Move input mapping function into a class for non-blocking usage. 2025-01-01 16:48:32 -06:00
JMC47 f15a78ed38
Merge pull request #13094 from mitaclaw/ranges-modernization-5-contains
Ranges Algorithms Modernization - Contains
2025-01-01 14:42:44 -05:00
mitaclaw 527841f1df Simplify `std::search` with `Common::ContainsSubrange` 2025-01-01 09:52:03 -08:00
mitaclaw d92c68e1de Simplify `std::find_if` with `Common::Contains` 2025-01-01 09:52:03 -08:00
mitaclaw 110d32729e Simplify `std::find` with `Common::Contains`
In NandPaths.cpp, the `std::initializer_list<char>` of illegal characters has been turned into a `char[]` (similar to the one in GameList.cpp).

The reverse iteration in ResourcePack.cpp seemed to provide no benefits, and doing without it it seemed to have no ill effects.
2025-01-01 09:52:03 -08:00
mitaclaw 6f10acea3f Common: Create "Contains.h" Algorithm Header
The new `Common::Contains` and `Common::ContainsSubrange` function objects mirror C++23's `std::ranges::contains` and `std::ranges::contains_subrange`, respectively.
2025-01-01 09:52:01 -08:00
Admiral H. Curtiss b8921b1338
Merge pull request #13237 from AdmiralCurtiss/slider-mappings
DolphinQt/HacksWidget: Convert accuracy slider to ConfigSlider
2024-12-31 15:22:04 +01:00
Admiral H. Curtiss c1c80adf1a
Merge pull request #13235 from Gamer64ytb/vsync-fix
Android: Fix VSync option not working
2024-12-31 15:20:49 +01:00
Jordan Woyak 0938fca6e3 Core/VideoCommon: Fix some weird (!eof) logic. 2024-12-30 20:13:59 -06:00
TryTwo 3d248d000f Frame Dumping: Change lossless codec from FFV1 to Ut Video.
Ut Video is faster and more compatible with editing programs, but produces larger files.
2024-12-30 14:07:43 -07:00
CasualPokePlayer c1698c93e2 Add SYSCONF country code to DTM
Recently there was some issues in TASVideos trying to sync a Donkey Kong Country Returns TAS. It eventually was synced by directly using the config from the TAS author. The exact setting which caused the desync was narrowed down to being in SYSCONF, with the country code. The TAS author lives in the US, so the country code matched the US country code, while the person attempting to sync the TAS did not live in the US.

Adding SYSCONF country code to the DTM should avoid this being an issue for future Dolphin versions.
2024-12-30 07:50:22 -08:00
Sintendo d81bfe94eb JitArm64_Integer: addzex - Optimize InHostCarry case for 0
Before:
0x5280000d   mov    w13, #0x0                 ; =0
0x1a1f01ae   adc    w14, w13, wzr

After:
0x1a9f37ee   cset   w14, hs
2024-12-29 12:21:34 +01:00
Sintendo c817b4779d JitArm64_Integer: addzex - Optimize InPPCState case for 0
Before:
0x52800019   mov    w25, #0x0                 ; =0
0x394bd3b8   ldrb   w24, [x29, #0x2f4]
0x2b180339   adds   w25, w25, w24

After:
0x394bd3b9   ldrb   w25, [x29, #0x2f4]
2024-12-28 23:20:22 +01:00
Sintendo 14641b06fc JitArm64_Integer: addzex - Optimize ConstantFalse and ConstantTrue
When the input register and carry flags are known, we can always
precompute the result.

We still materialize the immediate when the condition register
needs to be updated, but this seems to be a general problem. I might
look into that one day, but for now this'll do.

- ConstantFalse
Before:
0x52800119   mov    w25, #0x8                 ; =8
0x2a1903fa   mov    w26, w25

After:
N/A

- ConstantTrue
Before:
0x52800119   mov    w25, #0x8                 ; =8
0x1100073a   add    w26, w25, #0x1

After:
N/A
2024-12-28 23:07:38 +01:00
Sintendo a4ba13b4c9 JitArm64_Integer: addex - Optimize InHostCarry for -1
Same thing we did for subfex.

Before:
0x1280001a   mov    w26, #-0x1                ; =-1
0x1a1f035a   adc    w26, w26, wzr

After:
0x5a9f23fa   csetm  w26, lo
2024-12-28 22:12:50 +01:00
Sintendo d2bfa157dc JitArm64_Integer: addex - Optimize InHostCarry for 0
Similar to what we did for subfex, but for 0.

Before:
0x5280001b   mov    w27, #0x0                 ; =0
0x1a1f037b   adc    w27, w27, wzr

After:
0x1a9f37fb   cset   w27, hs
2024-12-28 21:55:57 +01:00
Sintendo ad7dba5413 JitArm64_Integer: addex - Optimize InPPCState case for 0
Same optimization we did for subfex. Skip loading the carry flag into a
temporary register first when we're dealing with zero.

Before:
0x394bd3b8   ldrb   w24, [x29, #0x2f4]
0x2a1803f9   mov    w25, w24

After:
0x394bd3b9   ldrb   w25, [x29, #0x2f4]
2024-12-28 21:41:51 +01:00
Sintendo 7410bc2025 JitArm64_Integer: subfzex - Constant folding
When both the input register and the carry flag are constants, the
result can be precomputed.

Before:
0x52800016   mov    w22, #0x0                 ; =0
0x2a3603f6   mvn    w22, w22

After:
2024-12-28 20:58:24 +01:00
Sintendo fa13457abb JitArm64_Integer: subfex - Optimize InHostCarry case for -1
The result is either -1 or 0 depending on the state of the carry flag.
This can be done with a csetm instruction.

Before:
0x1280001a   mov    w26, #-0x1                ; =-1
0x1a1f035a   adc    w26, w26, wzr

After:
0x5a9f23fa   csetm  w26, lo
2024-12-28 20:18:14 +01:00
Sintendo 18dd3f69f1 JitArm64_Integer: subfex - Optimize InPPCState case for 0
When the immediate is zero, we can load the carry flag from memory
directly to the destination register.

Before:
0x394bd3b8   ldrb   w24, [x29, #0x2f4]
0x2a1803f9   mov    w25, w24

After:
0x394bd3b9   ldrb   w25, [x29, #0x2f4]
2024-12-28 18:15:48 +01:00
Sintendo e54bfd6605 JitArm64_Integer: Refactor subfex 2024-12-28 18:12:13 +01:00
Sintendo 5cc9bde1c1 JitBase: Improve const-correctness 2024-12-28 16:44:58 +01:00
Sintendo 50d991780f JitBase: Add HasConstantCarry helper 2024-12-28 16:44:52 +01:00
dreamsyntax 07f712f8a0 Core/VideoCommon: Fix duplicate OSD Custom Textures messages
Resolves duplicate OSD messages for Loading and Found custom textures.
VideoBackend initialization results in HiresTexture::Init being called.
We already call HiresTexture::Update when OnNewTitleLoad is called.
Thus we can remove HiresTextures::Init completely as it is redundant.
2024-12-28 01:09:50 -07:00
Tilka 05cad38abc
Merge pull request #13243 from nlebeck/stringutil-tests
Add some unit test coverage of the `SplitPath` function
2024-12-27 18:10:47 +01:00
Admiral H. Curtiss 4fc50226c6
DolphinQt/HacksWidget: Convert accuracy slider to ConfigSlider 2024-12-27 05:40:24 +01:00
Admiral H. Curtiss 98ee3836e5
DolphinQt: Add option for value mappings to ConfigSlider 2024-12-27 05:40:13 +01:00
JMC47 532a8621da
Merge pull request #13116 from mitaclaw/ranges-modernization-8-trivial-of
Ranges Algorithms Modernization - Of
2024-12-26 16:51:53 -05:00
Niel Lebeck bae4616dd1 Add some unit test coverage of the SplitPath function 2024-12-26 11:42:01 -08:00
JosJuice 6384ea97f1 JitArm64: Handle rlwinmx with zero mask
No games seem to use this, so this isn't useful as a performance
optimization, but it's required for correctness because the (sh == 0)
case of our implementation doesn't handle zero masks.
2024-12-25 15:15:24 +01:00
Gamer64 764bf314e1 Android: Fix VSync option not working
It happened due to a typo from SECTION_GFX_HARDWARE
2024-12-24 10:41:45 +01:00
JMC47 f9ce2b9d76
Merge pull request #13232 from TryTwo/PR_hotfix
Qt crash hotfix
2024-12-23 18:04:51 -05:00
TryTwo 27ac5fa777 Qt crash fix. Don't store Config::Info variable as a reference. 2024-12-23 15:40:07 -07:00
JMC47 c528a70e64
Merge pull request #13211 from Sintendo/blendvpd
Jit_FloatingPoint: fselx - Prefer BLENDVPD over VBLENDVPD
2024-12-22 18:35:11 -05:00
JMC47 a1d6aa7d3e
Merge pull request #13212 from JosJuice/jitarm64-ps-sel-same-reg
JitArm64: Optimize ps_sel with d == b || d == c
2024-12-22 18:34:32 -05:00
JosJuice 5641b83d4e
Merge pull request #13063 from TryTwo/PR_GameSettings
Add ability to edit game-specific GFX settings from game properties tab.
2024-12-22 20:42:28 +01:00
Admiral H. Curtiss bb8c0a795f
Merge pull request #13214 from JosJuice/sethardcoremode-private
AchievementManager: Make SetHardcoreMode private
2024-12-22 20:09:29 +01:00
JMC47 1ba8541da9
Merge pull request #13091 from mitaclaw/ranges-modernization-2-returns
Ranges Algorithms Modernization - Return
2024-12-20 12:50:19 -05:00
JMC47 ac0d6cbaaa
Merge pull request #13224 from Sintendo/jitarm64-subfic2
JitArm64_Integer: Optimize subfic for -1
2024-12-18 12:07:23 -05:00
OatmealDome 01f6810a9d
Merge pull request #13207 from OatmealDome/vulkan-hdr-color-space
VKSwapChain: Always use surface formats with a normal sRGB color space if not RGBA16F
2024-12-16 17:29:19 -05:00
mitaclaw 2b0cd16c8c Modernize `std::none_of` with ranges
In JitRegCache.cpp, the lambda predicate were replaced by a pointer to member function because ranges algorithms are able to invoke those.

In ConvertDialog.cpp, the `std::mem_fn` helper was removed because ranges algorithms are able to handle pointers to member functions as predicates.

In BoundingBox.cpp, the lambda predicate was returning the bool element unchanged, so `std::identity` was a better fit.
2024-12-15 19:54:17 -08:00
mitaclaw 140252ffc0 Modernize `std::any_of` with ranges
In WiimoteReal.cpp, JitRegCache.cpp, lambda predicates were replaced by pointers to member functions because ranges algorithms are able invoke those.

In ConvertDialog.cpp, the `std::mem_fn` helper was removed because ranges algorithms are able to handle pointers to member functions as predicates.
2024-12-15 19:54:16 -08:00
mitaclaw 860e6cf5cb Modernize `std::all_of` with ranges
In DITSpecification.cpp, MaterialAsset.cpp, and ShaderAsset.cpp, lambda predicates were replaced by pointers to member functions because ranges algorithms are able invoke those.

In NetPlayClient.cpp, the non-trivial `NetPlay::Player` elements were being passed by value in `NetPlayClient::DoAllPlayersHaveGame()`. This has been fixed.

In WIABlob.cpp, the second example's predicate was returning the `std::optional` by value instead of implicitly converting it to a bool. This has been fixed.
2024-12-15 19:50:34 -08:00
mitaclaw 826e2bbf98 StringUtil: More Wrappers For <cctype>
`Common::IsLower(char)` was omitted as nothing needed it.
2024-12-15 19:50:34 -08:00
Jordan Woyak b4a1967310
Merge pull request #13226 from JosJuice/achievementmanager-forward-declarations
AchievementManager: Add required forward declarations
2024-12-15 14:02:56 -06:00
JosJuice ad24ddb6bb VerifyTool: Add missing USE_RETRO_ACHIEVEMENTS ifdefs 2024-12-15 18:15:57 +01:00
JosJuice 84ab15e020 AchievementManager: Add required forward declarations
This was causing compilation errors when building without
USE_RETRO_ACHIEVEMENTS.
2024-12-15 18:00:14 +01:00
Sintendo d81213c4a5 JitArm64_Integer: Optimize subfic for -1
Another one backported from x86. Not sure why I didn't do this in #12891
already.

- Without carry
Before:
0x2a3a03fb   mvn    w27, w26
0x6b1a037b   subs   w27, w27, w26

After:
0x1280001b   mov    w27, #-0x1                ; =-1

- With carry
Before:
0x2a3b03f7   mvn    w23, w27
0x6b1b02f7   subs   w23, w23, w27
0x1a9f37f6   cset   w22, hs
0x390bd3b6   strb   w22, [x29, #0x2f4]

After:
0x12800017   mov    w23, #-0x1                ; =-1
2024-12-15 02:24:30 +01:00
TryTwo 9541bb6cf7 Add method to bold slider/spin labels when a user game ini setting is being used 2024-12-10 13:42:30 -07:00
TryTwo ac129d318b EnhancementsWidget:: Move to using ConfigControls and add new control for ComboBoxes that set two settings at once. 2024-12-10 13:42:17 -07:00
TryTwo 84a937ae65 Add GFX property tabs to game properties window, allowing them to be set to the user game ini. Additionally, refactor ConfigWidgets to reduce duplication. Refactor GameConfigWidget to use config system.
Creates a layer outside the game config layer system and passes it to the created gfx widows, so as to not interfere with the global config system.

Supports multiple game properties being open at once.
Supports editing while a game is playing, but the options only save and update the active game when the window is closed.
Right-clicking will remove a property from the game ini.
2024-12-10 13:40:30 -07:00
JosJuice 7a31c8f10b AchievementManager: Make SetHardcoreMode private
This is an implementation detail that doesn't need to be exposed.
2024-12-07 19:29:53 +01:00
TryTwo 08df9a66e0 DolphinQt: Refactor, add ConfigControl class
This reduces code duplication in the different ConfigControls. This is
helpful for the next commit, which will modify the now deduplicated
code.
2024-12-07 16:31:34 +01:00
JosJuice e3bfff5cb6 JitArm64: Optimize ps_sel with d == b || d == c 2024-12-07 12:20:24 +01:00
Sintendo 065165f749 Jit_FloatingPoint: Prefer BLENDVPD over VBLENDVPD
Prefer BLENDVPD over VBLENDVPD if the latter doesn't save any
instructions.

VBLENDVPD allows separate source and destination registers, which can
eliminate a MOVAPD/MOVSD. However, on Intel since Skylake, VBLENDVPD
takes additional uops to execute compared to BLENDVPD (according to
https://uops.info). On AMD and older Intel microarchitectures there is no
difference.
2024-12-07 11:22:28 +01:00
OatmealDome e05b033dd2 ScmRevGen: Don't generate Info.plist files directly
Some generators (like Unix Makefiles and Xcode) copy an app's Info.plist at configure time.
This causes a problem when we need to generate the Info.plist at build time, like how we
currently do it with ScmRevGen. Instead of generating the Info.plist directly in ScmRevGen,
provide an Info.plist without any version information to CMake at configure time, have
ScmRevGen generate a separate plist file with the version information at build time, and
then merge the two together to create the final Info.plist.
2024-12-05 14:56:08 -05:00
OatmealDome 3c27c38e71 DolphinQt: Use TARGET_BUNDLE_DIR generator expression instead of specifying the bundle path ourselves 2024-12-04 22:53:39 -05:00
OatmealDome e6f335bfcf VKSwapChain: Always use surface formats with a normal sRGB color space if not RGBA16F
Co-authored-by: TellowKrinkle <tellowkrinkle@gmail.com>
2024-12-03 20:49:50 -05:00
TellowKrinkle 613c959bda CMake: Pass RESOLVED_USE_SYSTEM_<lib> as a parameter
Because we were setting it with a scope, it wasn't making its way into called functions that would try to inspect it.  Now it does.
2024-11-06 21:47:10 -06:00
TellowKrinkle 432d4a2a06 CMake: Properly abort when requested system lib is missing 2024-11-06 21:30:29 -06:00
JosJuice 980a726313 JitArm64: Drop GetCRFieldBit's negate parameter
No caller is using it anymore.
2024-11-02 10:51:48 +01:00
JosJuice 71e9766519 JitArm64: Use BIC/EON/ORN in crXXX
This lets us save an instruction in certain scenarios.
2024-11-02 10:51:38 +01:00
JosJuice 9246bcad55 JitArm64: Add negate parameter to SetCRFieldBit
Unlike on x64, inverting EQ or GT in SetCRFieldBit saves us one
instruction. Also unlike on x64, inverting SO or LT in GetCRFieldBit
requires an extra instruction (just like in SetCRFieldBit). Due to this,
replacing an invert in GetCRFieldBit with an invert in SetCRFieldBit
when possible is either equally good or better - never worse.
2024-11-02 10:48:27 +01:00
JosJuice 7fddd39d97 JitArm64: Port some crXXX optimizations from Jit64 2024-11-02 10:24:02 +01:00
JosJuice bbe271eec6 JitArm64: Refactor CR bit manipulation code
This brings JitArm64 more in line with Jit64, and makes the next commit
easier to implement. No functional change.
2024-11-02 10:19:31 +01:00
Jordan Woyak bffc26da27 DolphinQt: Rename "GameCube Adapter for Wii U" to "GameCube Controller Adapter". 2024-10-31 01:25:36 -05:00
Sintendo e6fd843c0b JitArm64_LoadStore: Small dcbx optimization
MUL and SUB can be combined in one instruction.

Before:
0x1b1a7c01   mul    w1, w0, w26
0x4b010318   sub    w24, w24, w1

After:
0x1b1ae018   msub   w24, w0, w26, w24
2024-10-24 23:05:42 +02:00
mitaclaw 3d0d03b871 Modernize `std::partition` with ranges
The new return value is `std::ranges::subrange`.
2024-10-17 18:39:13 -07:00
mitaclaw 5f3a8ff0de Modernize `std::unique` with ranges
The new return value is `std::ranges::subrange`.
2024-10-17 18:39:12 -07:00
mitaclaw be0b13da97 Simplify `std::remove` with `std::erase`
`std::erase` is a replacement for the remove-erase idiom.

Changes to `OpenModeToAndroid` inadvertently revealed that the prior implementation had UB (potentially deleting the end iterator). This is now fixed.
2024-10-17 18:38:34 -07:00
mitaclaw 4fde0f2868 Modernize `std::search` with ranges
The new return value is `std::ranges::subrange`.
2024-10-17 18:38:34 -07:00
mitaclaw 0352f24a8e Modernize `std::mismatch` with ranges
The new return value is `std::ranges::mismatch_result`, an alias for the pair-like type `std::ranges::in_in_result`.
2024-10-17 18:38:34 -07:00
JosJuice dcf8ab0189 Android: Wait for surface in Run
When we boot the core, it needs to have a valid surface to draw graphics
to. Our Kotlin code does wait for a valid surface to exist before it
calls NativeLibrary.Run, but there's a chance for the surface to be
deleted before Run locks s_surface_lock. If that happens, the core boots
without a valid surface, which presumably would cause a crash. (I
haven't been able to reproduce the problem myself.)
2024-08-21 20:36:46 +02:00
Jared M. White a277d66c50 Fix executable booting not functioning, move dirty page initialization to emu thread initialization to fix MMU bug, general code cleanliness improvements 2024-07-07 20:39:50 -05:00
Jared M. White d062310de0 Only track MEM1 and MEM2 2024-07-06 21:22:55 -05:00
Jared M. White af86eaad1a Move fault handling to inside memory manager class, make key for pagemap the page base 2024-07-06 19:48:48 -05:00
Jared M. White 619cd0aa38 Lock tracking logic behind a flag, check if faulted address is in emulated memory, check for page dirtiness before fastmem fault, pass whole page size to set page dirty bit 2024-07-05 16:14:40 -05:00
Jared M. White e252c4a729 Reorganize so that we're calling any memory protectives from the arena 2024-07-03 06:29:15 -05:00
Jared M. White 4b53e9b52b Satisfy linting requirements 2024-07-03 05:58:38 -05:00
Jared M. White 32020df8c0 Cleanup some arena functionality, fix some odd boot behavior, change array to pagemap-like structure for tracking dirty bits due to possible noncontinguous pages 2024-07-03 00:42:29 -05:00
Jared M. White 6184b8c0cb Savestates: Track dirty pages for the purposes of speeding up sequential savestates 2024-07-02 23:13:39 -05:00
JosJuice 2fdeb85f0e Android: Replace deprecated setSystemUiVisibility
No functional change.
2024-06-15 15:02:50 +02:00
181 changed files with 3317 additions and 3412 deletions

View File

@ -0,0 +1,24 @@
function(dolphin_inject_version_info target)
set(INFO_PLIST_PATH "$<TARGET_BUNDLE_DIR:${target}>/Contents/Info.plist")
add_custom_command(TARGET ${target}
POST_BUILD
COMMAND /usr/libexec/PlistBuddy -c
"Delete :CFBundleShortVersionString"
"${INFO_PLIST_PATH}"
|| true
COMMAND /usr/libexec/PlistBuddy -c
"Delete :CFBundleLongVersionString"
"${INFO_PLIST_PATH}"
|| true
COMMAND /usr/libexec/PlistBuddy -c
"Delete :CFBundleVersion"
"${INFO_PLIST_PATH}"
|| true
COMMAND /usr/libexec/PlistBuddy -c
"Merge '${CMAKE_BINARY_DIR}/Source/Core/VersionInfo.plist'"
"${INFO_PLIST_PATH}")
endfunction()

View File

@ -19,28 +19,27 @@ function(dolphin_make_imported_target_if_missing target lib)
endif()
endfunction()
function(dolphin_optional_system_library library)
function(dolphin_optional_system_library out_use_system library)
string(TOUPPER ${library} upperlib)
set(USE_SYSTEM_${upperlib} "" CACHE STRING "Use system ${library} instead of bundled. ON - Always use system and fail if unavailable, OFF - Always use bundled, AUTO - Use system if available, otherwise use bundled, blank - Delegate to USE_SYSTEM_LIBS. Default is blank.")
if("${USE_SYSTEM_${upperlib}}" STREQUAL "")
if(APPROVED_VENDORED_DEPENDENCIES)
string(TOLOWER ${library} lowerlib)
if(lowerlib IN_LIST APPROVED_VENDORED_DEPENDENCIES)
set(RESOLVED_USE_SYSTEM_${upperlib} AUTO PARENT_SCOPE)
set(${out_use_system} AUTO PARENT_SCOPE)
else()
set(RESOLVED_USE_SYSTEM_${upperlib} ON PARENT_SCOPE)
set(${out_use_system} ON PARENT_SCOPE)
endif()
else()
set(RESOLVED_USE_SYSTEM_${upperlib} ${USE_SYSTEM_LIBS} PARENT_SCOPE)
set(${out_use_system} ${USE_SYSTEM_LIBS} PARENT_SCOPE)
endif()
else()
set(RESOLVED_USE_SYSTEM_${upperlib} ${USE_SYSTEM_${upperlib}} PARENT_SCOPE)
set(${out_use_system} ${USE_SYSTEM_${upperlib}} PARENT_SCOPE)
endif()
endfunction()
function(dolphin_add_bundled_library library bundled_path)
string(TOUPPER ${library} upperlib)
if (${RESOLVED_USE_SYSTEM_${upperlib}} STREQUAL "AUTO")
function(dolphin_add_bundled_library library use_system bundled_path)
if (${use_system} STREQUAL "AUTO")
message(STATUS "No system ${library} was found. Using static ${library} from Externals.")
else()
message(STATUS "Using static ${library} from Externals")
@ -52,9 +51,9 @@ function(dolphin_add_bundled_library library bundled_path)
endfunction()
function(dolphin_find_optional_system_library library bundled_path)
dolphin_optional_system_library(${library})
dolphin_optional_system_library(use_system ${library})
string(TOUPPER ${library} upperlib)
if(RESOLVED_USE_SYSTEM_${upperlib})
if(use_system)
find_package(${library} ${ARGN})
# Yay for cmake packages being inconsistent
if(DEFINED ${library}_FOUND)
@ -62,7 +61,7 @@ function(dolphin_find_optional_system_library library bundled_path)
else()
set(prefix ${upperlib})
endif()
if((NOT ${found}) AND (NOT ${RESOLVED_USE_SYSTEM_${upperlib}} STREQUAL "AUTO"))
if((NOT ${prefix}_FOUND) AND (NOT ${use_system} STREQUAL "AUTO"))
message(FATAL_ERROR "No system ${library} was found. Please install it or set USE_SYSTEM_${upperlib} to AUTO or OFF.")
endif()
endif()
@ -70,17 +69,17 @@ function(dolphin_find_optional_system_library library bundled_path)
message(STATUS "Using system ${library}")
set(${prefix}_TYPE "System" PARENT_SCOPE)
else()
dolphin_add_bundled_library(${library} ${bundled_path})
dolphin_add_bundled_library(${library} ${use_system} ${bundled_path})
set(${prefix}_TYPE "Bundled" PARENT_SCOPE)
endif()
endfunction()
function(dolphin_find_optional_system_library_pkgconfig library search alias bundled_path)
dolphin_optional_system_library(${library})
dolphin_optional_system_library(use_system ${library})
string(TOUPPER ${library} upperlib)
if(RESOLVED_USE_SYSTEM_${upperlib})
if(use_system)
pkg_search_module(${library} ${search} ${ARGN} IMPORTED_TARGET)
if((NOT ${library}_FOUND) AND (NOT ${RESOLVED_USE_SYSTEM_${upperlib}} STREQUAL "AUTO"))
if((NOT ${library}_FOUND) AND (NOT ${use_system} STREQUAL "AUTO"))
message(FATAL_ERROR "No system ${library} was found. Please install it or set USE_SYSTEM_${upperlib} to AUTO or OFF.")
endif()
endif()
@ -89,7 +88,7 @@ function(dolphin_find_optional_system_library_pkgconfig library search alias bun
dolphin_alias_library(${alias} PkgConfig::${library})
set(${library}_TYPE "System" PARENT_SCOPE)
else()
dolphin_add_bundled_library(${library} ${bundled_path})
dolphin_add_bundled_library(${library} ${use_system} ${bundled_path})
set(${library}_TYPE "Bundled" PARENT_SCOPE)
endif()
endfunction()

View File

@ -65,6 +65,5 @@ endfunction()
configure_source_file("Source/Core/Common/scmrev.h")
if(APPLE)
configure_source_file("Source/Core/DolphinQt/Info.plist")
configure_source_file("Source/Core/MacUpdater/Info.plist")
configure_source_file("Source/Core/VersionInfo.plist")
endif()

View File

@ -783,14 +783,9 @@ if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/Common/scmrev.h)
endif()
if(APPLE)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/DolphinQt)
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/DolphinQt/Info.plist)
file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/DolphinQt/Info.plist)
endif()
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/MacUpdater)
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/MacUpdater/Info.plist)
file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/MacUpdater/Info.plist)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Source/Core)
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/VersionInfo.plist)
file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/VersionInfo.plist)
endif()
endif()

View File

@ -1,41 +1,41 @@
# RZDK01 - The Legend of Zelda: Twilight Princess [Wii]
[OnFrame]
# Add memory patches to be applied every frame here.
$Hyrule Field Speed Hack
0x80047DEC:dword:0x60000000
0x80047E08:dword:0x60000000
0x80047E20:dword:0x60000000
0x80047E3C:dword:0x60000000
0x80047E40:dword:0x60000000
0x80047E44:dword:0x60000000
0x80047E48:dword:0x60000000
0x80047E4C:dword:0x60000000
0x80047E50:dword:0x60000000
0x80047E54:dword:0x60000000
0x80047E58:dword:0x60000000
0x80047E5C:dword:0x60000000
0x80047E60:dword:0x60000000
0x80047E64:dword:0x60000000
0x80047E68:dword:0x60000000
0x80047E6C:dword:0x60000000
0x80047E70:dword:0x60000000
0x80047E74:dword:0x60000000
0x80047E78:dword:0x60000000
0x80047E7C:dword:0x60000000
0x80047E80:dword:0x60000000
0x80047E84:dword:0x60000000
0x80047E88:dword:0x60000000
0x80047E8C:dword:0x60000000
0x80047E94:dword:0x60000000
0x80047EB0:dword:0x60000000
0x80047EC8:dword:0x60000000
0x80047EE4:dword:0x60000000
[Patches_RetroAchievements_Verified]
$Hyrule Field Speed Hack
[ActionReplay]
# Add action replay cheats here.
# RZDK01 - The Legend of Zelda: Twilight Princess [Wii]
[OnFrame]
# Add memory patches to be applied every frame here.
$Hyrule Field Speed Hack
0x80047DEC:dword:0x60000000
0x80047E08:dword:0x60000000
0x80047E20:dword:0x60000000
0x80047E3C:dword:0x60000000
0x80047E40:dword:0x60000000
0x80047E44:dword:0x60000000
0x80047E48:dword:0x60000000
0x80047E4C:dword:0x60000000
0x80047E50:dword:0x60000000
0x80047E54:dword:0x60000000
0x80047E58:dword:0x60000000
0x80047E5C:dword:0x60000000
0x80047E60:dword:0x60000000
0x80047E64:dword:0x60000000
0x80047E68:dword:0x60000000
0x80047E6C:dword:0x60000000
0x80047E70:dword:0x60000000
0x80047E74:dword:0x60000000
0x80047E78:dword:0x60000000
0x80047E7C:dword:0x60000000
0x80047E80:dword:0x60000000
0x80047E84:dword:0x60000000
0x80047E88:dword:0x60000000
0x80047E8C:dword:0x60000000
0x80047E94:dword:0x60000000
0x80047EB0:dword:0x60000000
0x80047EC8:dword:0x60000000
0x80047EE4:dword:0x60000000
[Patches_RetroAchievements_Verified]
$Hyrule Field Speed Hack
[ActionReplay]
# Add action replay cheats here.

View File

@ -1,62 +1,53 @@
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/curl/include)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/curl/lib)
set(BUILD_CURL_EXE OFF)
set(BUILD_EXAMPLES OFF)
set(BUILD_LIBCURL_DOCS OFF)
set(BUILD_MISC_DOCS OFF)
set(BUILD_SHARED_LIBS OFF)
set(BUILD_STATIC_LIBS ON)
set(BUILD_TESTING OFF)
set(CURL_ENABLE_EXPORT_TARGET OFF)
set(CURL_LIBS MbedTLS::mbedtls zlibstatic)
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(use_core_foundation ON)
set(HTTP_ONLY ON)
find_library(SYSTEMCONFIGURATION_FRAMEWORK "SystemConfiguration")
if(NOT SYSTEMCONFIGURATION_FRAMEWORK)
message(FATAL_ERROR "SystemConfiguration framework not found")
endif()
set(CURL_USE_LIBPSL OFF)
set(CURL_USE_LIBSSH2 OFF)
set(CURL_ZLIB OFF CACHE BOOL "" FORCE)
set(CURL_ZSTD OFF)
set(USE_LIBIDN2 OFF)
set(USE_NGHTTP2 OFF)
list(APPEND CURL_LIBS "-framework SystemConfiguration")
if(UNIX)
# We use mbedtls on Unix(-like) systems and Android.
set(CURL_USE_OPENSSL OFF)
# This is set if the dolphin_find_optional_system_library call from the main CMakeLists.txt
# is able to find mbedtls on the system.
if(MBEDTLS_FOUND)
# We can just enable CURL_USE_MBEDTLS.
set(CURL_USE_MBEDTLS ON)
else()
# HACK: Set some internal variables to pretend like mbedtls was found on the system.
# We can't use CURL_USE_MBEDTLS with our copy from Externals, as that flag expects
# mbedtls to be installed (the CMakeLists attempts to search for it with find_package).
set(_ssl_enabled ON)
set(USE_MBEDTLS ON)
set(_curl_ca_bundle_supported TRUE)
endif()
endif()
file(GLOB SRCS curl/lib/*.c curl/lib/vauth/*.c curl/lib/vquic/*.c curl/lib/vssh/*.c curl/lib/vtls/*.c)
add_library(
curl
STATIC
curl/include/curl/curl.h
${SRCS}
)
set(SEARCH_CA_BUNDLE_PATHS
/etc/ssl/certs/ca-certificates.crt
/etc/pki/tls/certs/ca-bundle.crt
/usr/share/ssl/certs/ca-bundle.crt
/usr/local/share/certs/ca-root-nss.crt
/etc/ssl/cert.pem)
foreach(SEARCH_CA_BUNDLE_PATH ${SEARCH_CA_BUNDLE_PATHS})
if(EXISTS "${SEARCH_CA_BUNDLE_PATH}")
message(STATUS "Found CA bundle: ${SEARCH_CA_BUNDLE_PATH}")
set(CURL_CA_BUNDLE "${SEARCH_CA_BUNDLE_PATH}")
set(CURL_CA_BUNDLE_SET TRUE)
break()
endif()
endforeach()
if(NOT CURL_CA_PATH_SET)
if(EXISTS "/etc/ssl/certs")
set(CURL_CA_PATH "/etc/ssl/certs")
set(CURL_CA_PATH_SET TRUE)
endif()
if(WIN32)
set(CURL_USE_SCHANNEL ON)
endif()
dolphin_disable_warnings(curl)
target_link_libraries(curl ${CURL_LIBS})
target_include_directories(curl PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/curl/include")
target_compile_definitions(curl PRIVATE "BUILDING_LIBCURL=1")
if (WIN32)
target_compile_definitions(curl PUBLIC "CURL_STATICLIB=1" "CURL_DISABLE_LDAP" "USE_WINDOWS_SSPI" "USE_SCHANNEL")
target_link_libraries(curl Crypt32.lib)
else()
target_compile_definitions(curl PUBLIC "CURL_STATICLIB=1" "USE_MBEDTLS=1" "HAVE_CONFIG_H=1" "CURL_DISABLE_LDAP")
if (CURL_CA_PATH_SET)
target_compile_definitions(curl PUBLIC CURL_CA_PATH="${CURL_CA_PATH}")
endif()
if(ANDROID)
set(CURL_CA_PATH "/system/etc/security/cacerts" CACHE STRING "")
endif()
add_library(CURL::libcurl ALIAS curl)
add_subdirectory(curl)
if(UNIX AND NOT MBEDTLS_FOUND)
# HACK: Manually link with the mbedtls libraries.
target_link_libraries(libcurl_static PRIVATE
MbedTLS::mbedtls
MbedTLS::mbedx509)
endif()

View File

@ -1,22 +0,0 @@
COPYRIGHT AND PERMISSION NOTICE
Copyright (c) 1996 - 2016, Daniel Stenberg, <daniel@haxx.se>, and many
contributors, see the THANKS file.
All rights reserved.
Permission to use, copy, modify, and distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright
notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of a copyright holder shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization of the copyright holder.

2
Externals/curl/curl vendored

@ -1 +1 @@
Subproject commit d755a5f7c009dd63a61b2c745180d8ba937cbfeb
Subproject commit 75a2079d5c28debb2eaa848ca9430f1fe0d7844c

View File

@ -50,14 +50,14 @@
<ClCompile Include="curl\lib\curl_memrchr.c" />
<ClCompile Include="curl\lib\curl_multibyte.c" />
<ClCompile Include="curl\lib\curl_ntlm_core.c" />
<ClCompile Include="curl\lib\curl_ntlm_wb.c" />
<ClCompile Include="curl\lib\curl_path.c" />
<ClCompile Include="curl\lib\curl_range.c" />
<ClCompile Include="curl\lib\curl_rtmp.c" />
<ClCompile Include="curl\lib\curl_sasl.c" />
<ClCompile Include="curl\lib\curl_sha512_256.c" />
<ClCompile Include="curl\lib\curl_sspi.c" />
<ClCompile Include="curl\lib\curl_threads.c" />
<ClCompile Include="curl\lib\curl_trc.c" />
<ClCompile Include="curl\lib\cw-out.c" />
<ClCompile Include="curl\lib\dict.c" />
<ClCompile Include="curl\lib\doh.c" />
<ClCompile Include="curl\lib\dynbuf.c" />
@ -120,6 +120,7 @@
<ClCompile Include="curl\lib\psl.c" />
<ClCompile Include="curl\lib\rand.c" />
<ClCompile Include="curl\lib\rename.c" />
<ClCompile Include="curl\lib\request.c" />
<ClCompile Include="curl\lib\rtsp.c" />
<ClCompile Include="curl\lib\select.c" />
<ClCompile Include="curl\lib\sendf.c" />
@ -167,12 +168,16 @@
<ClCompile Include="curl\lib\vauth\vauth.c" />
<ClCompile Include="curl\lib\vquic\curl_msh3.c" />
<ClCompile Include="curl\lib\vquic\curl_ngtcp2.c" />
<ClCompile Include="curl\lib\vquic\curl_osslq.c" />
<ClCompile Include="curl\lib\vquic\curl_quiche.c" />
<ClCompile Include="curl\lib\vquic\vquic.c" />
<ClCompile Include="curl\lib\vquic\vquic-tls.c" />
<ClCompile Include="curl\lib\vssh\curl_path.c" />
<ClCompile Include="curl\lib\vssh\libssh.c" />
<ClCompile Include="curl\lib\vssh\libssh2.c" />
<ClCompile Include="curl\lib\vssh\wolfssh.c" />
<ClCompile Include="curl\lib\vtls\bearssl.c" />
<ClCompile Include="curl\lib\vtls\cipher_suite.c" />
<ClCompile Include="curl\lib\vtls\gtls.c" />
<ClCompile Include="curl\lib\vtls\hostcheck.c" />
<ClCompile Include="curl\lib\vtls\keylog.c" />
@ -244,8 +249,6 @@
<ClInclude Include="curl\lib\curl_memrchr.h" />
<ClInclude Include="curl\lib\curl_multibyte.h" />
<ClInclude Include="curl\lib\curl_ntlm_core.h" />
<ClInclude Include="curl\lib\curl_ntlm_wb.h" />
<ClInclude Include="curl\lib\curl_path.h" />
<ClInclude Include="curl\lib\curl_printf.h" />
<ClInclude Include="curl\lib\curl_range.h" />
<ClInclude Include="curl\lib\curl_rtmp.h" />
@ -253,9 +256,11 @@
<ClInclude Include="curl\lib\curl_setup.h" />
<ClInclude Include="curl\lib\curl_setup_once.h" />
<ClInclude Include="curl\lib\curl_sha256.h" />
<ClInclude Include="curl\lib\curl_sha512_256.h" />
<ClInclude Include="curl\lib\curl_sspi.h" />
<ClInclude Include="curl\lib\curl_threads.h" />
<ClInclude Include="curl\lib\curl_trc.h" />
<ClInclude Include="curl\lib\cw_out.h" />
<ClInclude Include="curl\lib\dict.h" />
<ClInclude Include="curl\lib\doh.h" />
<ClInclude Include="curl\lib\dynbuf.h" />
@ -308,6 +313,7 @@
<ClInclude Include="curl\lib\psl.h" />
<ClInclude Include="curl\lib\rand.h" />
<ClInclude Include="curl\lib\rename.h" />
<ClInclude Include="curl\lib\request.h" />
<ClInclude Include="curl\lib\rtsp.h" />
<ClInclude Include="curl\lib\select.h" />
<ClInclude Include="curl\lib\sendf.h" />
@ -347,11 +353,15 @@
<ClInclude Include="curl\lib\vauth\vauth.h" />
<ClInclude Include="curl\lib\vquic\curl_msh3.h" />
<ClInclude Include="curl\lib\vquic\curl_ngtcp2.h" />
<ClInclude Include="curl\lib\vquic\curl_osslq.h" />
<ClInclude Include="curl\lib\vquic\curl_quiche.h" />
<ClInclude Include="curl\lib\vquic\vquic.h" />
<ClInclude Include="curl\lib\vquic\vquic-tls.h" />
<ClInclude Include="curl\lib\vquic\vquic_int.h" />
<ClInclude Include="curl\lib\vssh\curl_path.h" />
<ClInclude Include="curl\lib\vssh\ssh.h" />
<ClInclude Include="curl\lib\vtls\bearssl.h" />
<ClInclude Include="curl\lib\vtls\cipher_suites.h" />
<ClInclude Include="curl\lib\vtls\gtls.h" />
<ClInclude Include="curl\lib\vtls\hostcheck.h" />
<ClInclude Include="curl\lib\vtls\keylog.h" />

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,9 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
@ -326,12 +328,11 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider {
}
private fun enableFullscreenImmersive() {
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
WindowCompat.setDecorFitsSystemWindows(window, false)
WindowInsetsControllerCompat(window, window.decorView).let { controller ->
controller.hide(WindowInsetsCompat.Type.systemBars())
controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
}
private fun updateDisplaySettings() {

View File

@ -124,7 +124,7 @@ class Settings : Closeable {
const val SECTION_INI_DSP = "DSP"
const val SECTION_LOGGER_LOGS = "Logs"
const val SECTION_LOGGER_OPTIONS = "Options"
const val SECTION_GFX_HARDWARE = "Settings"
const val SECTION_GFX_HARDWARE = "Hardware"
const val SECTION_GFX_SETTINGS = "Settings"
const val SECTION_GFX_ENHANCEMENTS = "Enhancements"
const val SECTION_GFX_COLOR_CORRECTION = "ColorCorrection"

View File

@ -1,132 +1,132 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.skylanders.ui
import android.app.AlertDialog
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.AdapterView.OnItemClickListener
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.dolphinemu.dolphinemu.R
import org.dolphinemu.dolphinemu.activities.EmulationActivity
import org.dolphinemu.dolphinemu.databinding.DialogCreateSkylanderBinding
import org.dolphinemu.dolphinemu.databinding.ListItemNfcFigureSlotBinding
import org.dolphinemu.dolphinemu.features.skylanders.SkylanderConfig
import org.dolphinemu.dolphinemu.features.skylanders.SkylanderConfig.removeSkylander
import org.dolphinemu.dolphinemu.features.skylanders.model.SkylanderPair
class SkylanderSlotAdapter(
private val slots: List<SkylanderSlot>,
private val activity: EmulationActivity
) : RecyclerView.Adapter<SkylanderSlotAdapter.ViewHolder>(), OnItemClickListener {
class ViewHolder(var binding: ListItemNfcFigureSlotBinding) :
RecyclerView.ViewHolder(binding.getRoot())
private lateinit var binding: DialogCreateSkylanderBinding
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ListItemNfcFigureSlotBinding.inflate(inflater, parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val slot = slots[position]
holder.binding.textFigureName.text = slot.label
holder.binding.buttonClearFigure.setOnClickListener {
removeSkylander(slot.portalSlot)
activity.clearSkylander(slot.slotNum)
}
holder.binding.buttonLoadFigure.setOnClickListener {
val loadSkylander = Intent(Intent.ACTION_OPEN_DOCUMENT)
loadSkylander.addCategory(Intent.CATEGORY_OPENABLE)
loadSkylander.type = "*/*"
activity.setSkylanderData(0, 0, "", position)
activity.startActivityForResult(
loadSkylander,
EmulationActivity.REQUEST_SKYLANDER_FILE
)
}
val inflater = LayoutInflater.from(activity)
binding = DialogCreateSkylanderBinding.inflate(inflater)
val nameList = SkylanderConfig.REVERSE_LIST_SKYLANDERS.keys.toMutableList()
nameList.sort()
val skylanderNames: ArrayList<String> = ArrayList(nameList)
binding.skylanderDropdown.setAdapter(
ArrayAdapter(
activity, R.layout.support_simple_spinner_dropdown_item,
skylanderNames
)
)
binding.skylanderDropdown.onItemClickListener = this
holder.binding.buttonCreateFigure.setOnClickListener {
if (binding.getRoot().parent != null) {
(binding.getRoot().parent as ViewGroup).removeAllViews()
}
val createDialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.create_skylander_title)
.setView(binding.getRoot())
.setPositiveButton(R.string.create_figure, null)
.setNegativeButton(R.string.cancel, null)
.show()
createDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
if (binding.skylanderId.text.toString().isNotBlank() &&
binding.skylanderVar.text.toString().isNotBlank()
) {
val createSkylander = Intent(Intent.ACTION_CREATE_DOCUMENT)
createSkylander.addCategory(Intent.CATEGORY_OPENABLE)
createSkylander.type = "*/*"
val id = binding.skylanderId.text.toString().toInt()
val variant = binding.skylanderVar.text.toString().toInt()
val name = SkylanderConfig.LIST_SKYLANDERS[SkylanderPair(id, variant)]
if (name != null) {
createSkylander.putExtra(
Intent.EXTRA_TITLE,
"$name.sky"
)
activity.setSkylanderData(id, variant, name, position)
} else {
createSkylander.putExtra(
Intent.EXTRA_TITLE,
"Unknown(ID: " + id + "Variant: " + variant + ").sky"
)
activity.setSkylanderData(id, variant, "Unknown", position)
}
activity.startActivityForResult(
createSkylander,
EmulationActivity.REQUEST_CREATE_SKYLANDER
)
createDialog.dismiss()
} else {
Toast.makeText(
activity, R.string.invalid_skylander,
Toast.LENGTH_SHORT
).show()
}
}
}
}
override fun getItemCount(): Int {
return slots.size
}
override fun onItemClick(parent: AdapterView<*>, view: View, position: Int, id: Long) {
val skylanderIdVar =
SkylanderConfig.REVERSE_LIST_SKYLANDERS[parent.getItemAtPosition(position)]
binding.skylanderId.setText(skylanderIdVar!!.id.toString())
binding.skylanderVar.setText(skylanderIdVar.variant.toString())
}
}
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.skylanders.ui
import android.app.AlertDialog
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.AdapterView.OnItemClickListener
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.dolphinemu.dolphinemu.R
import org.dolphinemu.dolphinemu.activities.EmulationActivity
import org.dolphinemu.dolphinemu.databinding.DialogCreateSkylanderBinding
import org.dolphinemu.dolphinemu.databinding.ListItemNfcFigureSlotBinding
import org.dolphinemu.dolphinemu.features.skylanders.SkylanderConfig
import org.dolphinemu.dolphinemu.features.skylanders.SkylanderConfig.removeSkylander
import org.dolphinemu.dolphinemu.features.skylanders.model.SkylanderPair
class SkylanderSlotAdapter(
private val slots: List<SkylanderSlot>,
private val activity: EmulationActivity
) : RecyclerView.Adapter<SkylanderSlotAdapter.ViewHolder>(), OnItemClickListener {
class ViewHolder(var binding: ListItemNfcFigureSlotBinding) :
RecyclerView.ViewHolder(binding.getRoot())
private lateinit var binding: DialogCreateSkylanderBinding
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ListItemNfcFigureSlotBinding.inflate(inflater, parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val slot = slots[position]
holder.binding.textFigureName.text = slot.label
holder.binding.buttonClearFigure.setOnClickListener {
removeSkylander(slot.portalSlot)
activity.clearSkylander(slot.slotNum)
}
holder.binding.buttonLoadFigure.setOnClickListener {
val loadSkylander = Intent(Intent.ACTION_OPEN_DOCUMENT)
loadSkylander.addCategory(Intent.CATEGORY_OPENABLE)
loadSkylander.type = "*/*"
activity.setSkylanderData(0, 0, "", position)
activity.startActivityForResult(
loadSkylander,
EmulationActivity.REQUEST_SKYLANDER_FILE
)
}
val inflater = LayoutInflater.from(activity)
binding = DialogCreateSkylanderBinding.inflate(inflater)
val nameList = SkylanderConfig.REVERSE_LIST_SKYLANDERS.keys.toMutableList()
nameList.sort()
val skylanderNames: ArrayList<String> = ArrayList(nameList)
binding.skylanderDropdown.setAdapter(
ArrayAdapter(
activity, R.layout.support_simple_spinner_dropdown_item,
skylanderNames
)
)
binding.skylanderDropdown.onItemClickListener = this
holder.binding.buttonCreateFigure.setOnClickListener {
if (binding.getRoot().parent != null) {
(binding.getRoot().parent as ViewGroup).removeAllViews()
}
val createDialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.create_skylander_title)
.setView(binding.getRoot())
.setPositiveButton(R.string.create_figure, null)
.setNegativeButton(R.string.cancel, null)
.show()
createDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
if (binding.skylanderId.text.toString().isNotBlank() &&
binding.skylanderVar.text.toString().isNotBlank()
) {
val createSkylander = Intent(Intent.ACTION_CREATE_DOCUMENT)
createSkylander.addCategory(Intent.CATEGORY_OPENABLE)
createSkylander.type = "*/*"
val id = binding.skylanderId.text.toString().toInt()
val variant = binding.skylanderVar.text.toString().toInt()
val name = SkylanderConfig.LIST_SKYLANDERS[SkylanderPair(id, variant)]
if (name != null) {
createSkylander.putExtra(
Intent.EXTRA_TITLE,
"$name.sky"
)
activity.setSkylanderData(id, variant, name, position)
} else {
createSkylander.putExtra(
Intent.EXTRA_TITLE,
"Unknown(ID: " + id + "Variant: " + variant + ").sky"
)
activity.setSkylanderData(id, variant, "Unknown", position)
}
activity.startActivityForResult(
createSkylander,
EmulationActivity.REQUEST_CREATE_SKYLANDER
)
createDialog.dismiss()
} else {
Toast.makeText(
activity, R.string.invalid_skylander,
Toast.LENGTH_SHORT
).show()
}
}
}
}
override fun getItemCount(): Int {
return slots.size
}
override fun onItemClick(parent: AdapterView<*>, view: View, position: Int, id: Long) {
val skylanderIdVar =
SkylanderConfig.REVERSE_LIST_SKYLANDERS[parent.getItemAtPosition(position)]
binding.skylanderId.setText(skylanderIdVar!!.id.toString())
binding.skylanderVar.setText(skylanderIdVar.variant.toString())
}
}

View File

@ -1,72 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/root"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/spacing_medlarge">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/layout_skylander_dropdown"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/spacing_medlarge"
android:paddingHorizontal="@dimen/spacing_medlarge"
android:hint="@string/skylander_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.MaterialAutoCompleteTextView
android:id="@+id/skylander_dropdown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:spinnerMode="dialog"
android:imeOptions="actionDone" />
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="@+id/layout_skylander_dropdown"
android:gravity="center_vertical"
android:baselineAligned="false">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/layout_skylander_id"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="@string/skylander_id"
android:paddingHorizontal="@dimen/spacing_medlarge">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/skylander_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/layout_skylander_var"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="@string/skylander_variant"
android:paddingHorizontal="@dimen/spacing_medlarge">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/skylander_var"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/root"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/spacing_medlarge">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/layout_skylander_dropdown"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/spacing_medlarge"
android:paddingHorizontal="@dimen/spacing_medlarge"
android:hint="@string/skylander_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.MaterialAutoCompleteTextView
android:id="@+id/skylander_dropdown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:spinnerMode="dialog"
android:imeOptions="actionDone" />
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="@+id/layout_skylander_dropdown"
android:gravity="center_vertical"
android:baselineAligned="false">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/layout_skylander_id"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="@string/skylander_id"
android:paddingHorizontal="@dimen/spacing_medlarge">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/skylander_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/layout_skylander_var"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="@string/skylander_variant"
android:paddingHorizontal="@dimen/spacing_medlarge">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/skylander_var"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -62,7 +62,7 @@ bool IsPathAndroidContent(std::string_view uri)
std::string OpenModeToAndroid(std::string mode)
{
// The 'b' specifier is not supported by Android. Since we're on POSIX, it's fine to just skip it.
mode.erase(std::remove(mode.begin(), mode.end(), 'b'));
std::erase(mode, 'b');
if (mode == "r")
return "r";

View File

@ -191,7 +191,7 @@ Java_org_dolphinemu_dolphinemu_features_cheats_model_GeckoCheat_downloadCodes(JN
const std::string gametdb_id = GetJString(env, jGameTdbId);
bool success = true;
const std::vector<Gecko::GeckoCode> codes = Gecko::DownloadCodes(gametdb_id, &success, false);
const std::vector<Gecko::GeckoCode> codes = Gecko::DownloadCodes(gametdb_id, &success);
if (!success)
return nullptr;

View File

@ -51,10 +51,9 @@ Java_org_dolphinemu_dolphinemu_features_cheats_model_GraphicsModGroup_getMods(JN
for (GraphicsModConfig& mod : mod_group->GetMods())
{
// If no group matches the mod's features, or if the mod has no features, skip it
if (std::none_of(mod.m_features.begin(), mod.m_features.end(),
[&groups](const GraphicsModFeatureConfig& feature) {
return groups.contains(feature.m_group);
}))
if (std::ranges::none_of(mod.m_features, [&groups](const GraphicsModFeatureConfig& feature) {
return groups.contains(feature.m_group);
}))
{
continue;
}

View File

@ -1,13 +1,9 @@
// Copyright 2003 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <EGL/egl.h>
#include <android/log.h>
#include <android/native_window_jni.h>
#include <condition_variable>
#include <cstdio>
#include <cstdlib>
#include <fmt/format.h>
#include <jni.h>
#include <memory>
#include <mutex>
#include <optional>
@ -15,6 +11,12 @@
#include <thread>
#include <utility>
#include <EGL/egl.h>
#include <android/log.h>
#include <android/native_window_jni.h>
#include <fmt/format.h>
#include <jni.h>
#include "Common/AndroidAnalytics.h"
#include "Common/Assert.h"
#include "Common/CPUDetect.h"
@ -77,6 +79,8 @@ Common::Event s_update_main_frame_event;
// This exists to prevent surfaces from being destroyed during the boot process,
// as that can lead to the boot process dereferencing nullptr.
std::mutex s_surface_lock;
std::condition_variable s_surface_cv;
bool s_need_nonblocking_alert_msg;
Common::Flag s_is_booting;
@ -482,6 +486,8 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceChang
if (g_presenter)
g_presenter->ChangeSurface(s_surf);
s_surface_cv.notify_all();
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestroyed(JNIEnv*,
@ -515,6 +521,8 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestr
ANativeWindow_release(s_surf);
s_surf = nullptr;
}
s_surface_cv.notify_all();
}
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_HasSurface(JNIEnv*, jclass)
@ -606,12 +614,14 @@ static void Run(JNIEnv* env, std::unique_ptr<BootParameters>&& boot, bool riivol
volume.GetDiscNumber()));
}
WindowSystemInfo wsi(WindowSystemType::Android, nullptr, s_surf, s_surf);
wsi.render_surface_scale = GetRenderSurfaceScale(env);
s_need_nonblocking_alert_msg = true;
std::unique_lock<std::mutex> surface_guard(s_surface_lock);
s_surface_cv.wait(surface_guard, []() { return s_surf != nullptr; });
WindowSystemInfo wsi(WindowSystemType::Android, nullptr, s_surf, s_surf);
wsi.render_surface_scale = GetRenderSurfaceScale(env);
if (BootManager::BootCore(Core::System::GetInstance(), std::move(boot), wsi))
{
static constexpr int WAIT_STEP = 25;

View File

@ -34,6 +34,7 @@ add_library(common
Config/Enums.h
Config/Layer.cpp
Config/Layer.h
Contains.h
CPUDetect.h
Crypto/AES.cpp
Crypto/AES.h

View File

@ -0,0 +1,61 @@
// Copyright 2025 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <algorithm>
#include <iterator>
namespace Common
{
struct ContainsFn
{
template <std::input_iterator I, std::sentinel_for<I> S, class T, class Proj = std::identity>
requires std::indirect_binary_predicate < std::ranges::equal_to, std::projected<I, Proj>,
const T* > constexpr bool operator()(I first, S last, const T& value, Proj proj = {}) const
{
return std::ranges::find(std::move(first), last, value, std::move(proj)) != last;
}
template <std::ranges::input_range R, class T, class Proj = std::identity>
requires std::indirect_binary_predicate < std::ranges::equal_to,
std::projected<std::ranges::iterator_t<R>, Proj>,
const T* > constexpr bool operator()(R&& r, const T& value, Proj proj = {}) const
{
return (*this)(std::ranges::begin(r), std::ranges::end(r), value, std::move(proj));
}
};
struct ContainsSubrangeFn
{
template <std::forward_iterator I1, std::sentinel_for<I1> S1, std::forward_iterator I2,
std::sentinel_for<I2> S2, class Pred = std::ranges::equal_to,
class Proj1 = std::identity, class Proj2 = std::identity>
requires std::indirectly_comparable<I1, I2, Pred, Proj1, Proj2>
constexpr bool operator()(I1 first1, S1 last1, I2 first2, S2 last2, Pred pred = {},
Proj1 proj1 = {}, Proj2 proj2 = {}) const
{
return !std::ranges::search(std::move(first1), std::move(last1), std::move(first2),
std::move(last2), std::move(pred), std::move(proj1),
std::move(proj2))
.empty();
}
template <std::ranges::forward_range R1, std::ranges::forward_range R2,
class Pred = std::ranges::equal_to, class Proj1 = std::identity,
class Proj2 = std::identity>
requires std::indirectly_comparable<std::ranges::iterator_t<R1>, std::ranges::iterator_t<R2>,
Pred, Proj1, Proj2>
constexpr bool operator()(R1&& r1, R2&& r2, Pred pred = {}, Proj1 proj1 = {},
Proj2 proj2 = {}) const
{
return (*this)(std::ranges::begin(r1), std::ranges::end(r1), std::ranges::begin(r2),
std::ranges::end(r2), std::move(pred), std::move(proj1), std::move(proj2));
}
};
// TODO C++23: Replace with std::ranges::contains.
inline constexpr ContainsFn Contains{};
// TODO C++23: Replace with std::ranges::contains_subrange.
inline constexpr ContainsSubrangeFn ContainsSubrange{};
} // namespace Common

View File

@ -24,7 +24,7 @@ struct Elt
{
bool IsZero() const
{
return std::all_of(data.begin(), data.end(), [](u8 b) { return b == 0; });
return std::ranges::all_of(data, [](u8 b) { return b == 0; });
}
void MulX()

View File

@ -91,7 +91,7 @@ void MemoryPatches::DisablePatch(const Core::CPUThreadGuard& guard, std::size_t
bool MemoryPatches::HasEnabledPatch(u32 address) const
{
return std::any_of(m_patches.begin(), m_patches.end(), [address](const MemoryPatch& patch) {
return std::ranges::any_of(m_patches, [address](const MemoryPatch& patch) {
return patch.address == address && patch.is_enabled == MemoryPatch::State::Enabled;
});
}

View File

@ -79,7 +79,7 @@ void Watches::DisableWatch(std::size_t index)
bool Watches::HasEnabledWatch(u32 address) const
{
return std::any_of(m_watches.begin(), m_watches.end(), [address](const auto& watch) {
return std::ranges::any_of(m_watches, [address](const auto& watch) {
return watch.address == address && watch.is_enabled == Watch::State::Enabled;
});
}

View File

@ -729,7 +729,7 @@ static bool Unpack(const std::function<bool()>& cancelled, const std::string pat
const bool is_path_traversal_attack =
(childname.find("\\") != std::string_view::npos) ||
(childname.find('/') != std::string_view::npos) ||
std::all_of(childname.begin(), childname.end(), [](char c) { return c == '.'; });
std::ranges::all_of(childname, [](char c) { return c == '.'; });
if (is_path_traversal_attack)
{
ERROR_LOG_FMT(

View File

@ -41,7 +41,7 @@ std::vector<std::string> DoFileSearch(const std::vector<std::string>& directorie
// N.B. This avoids doing any copies
auto ext_matches = [&native_exts](const fs::path& path) {
const std::basic_string_view<fs::path::value_type> native_path = path.native();
return std::any_of(native_exts.cbegin(), native_exts.cend(), [&native_path](const auto& ext) {
return std::ranges::any_of(native_exts, [&native_path](const auto& ext) {
const auto compare_len = ext.native().length();
if (native_path.length() < compare_len)
return false;
@ -98,7 +98,8 @@ std::vector<std::string> DoFileSearch(const std::vector<std::string>& directorie
// not because std::filesystem returns duplicates). Also note that this pathname-based uniqueness
// isn't as thorough as std::filesystem::equivalent.
std::ranges::sort(result);
result.erase(std::unique(result.begin(), result.end()), result.end());
const auto unique_result = std::ranges::unique(result);
result.erase(unique_result.begin(), unique_result.end());
// Dolphin expects to be able to use "/" (DIR_SEP) everywhere.
// std::filesystem uses the OS separator.

View File

@ -85,7 +85,8 @@ bool IniFile::Section::Delete(std::string_view key)
return false;
values.erase(it);
keys_order.erase(std::ranges::find(keys_order, key));
keys_order.erase(std::ranges::find_if(
keys_order, [&](std::string_view v) { return CaseInsensitiveEquals(key, v); }));
return true;
}

View File

@ -99,7 +99,13 @@ void GenericLogFmt(LogLevel level, LogType type, const char* file, int line, con
static_assert(NumFields == sizeof...(args),
"Unexpected number of replacement fields in format string; did you pass too few or "
"too many arguments?");
GenericLogFmtImpl(level, type, file, line, format, fmt::make_format_args(args...));
#if FMT_VERSION >= 110000
auto&& format_str = fmt::format_string<Args...>(format);
#else
auto&& format_str = format;
#endif
GenericLogFmtImpl(level, type, file, line, format_str, fmt::make_format_args(args...));
}
} // namespace Common::Log

View File

@ -22,6 +22,7 @@ struct WindowsMemoryFunctions
void* m_address_UnmapViewOfFileEx = nullptr;
void* m_address_VirtualAlloc2 = nullptr;
void* m_address_MapViewOfFile3 = nullptr;
void* m_address_VirtualProtect = nullptr;
};
#endif
@ -111,6 +112,15 @@ public:
///
void UnmapFromMemoryRegion(void* view, size_t size);
///
/// Virtual protect a section from the memory region previously mapped by CreateView.
///
/// @param data Pointer to data to protect.
/// @param size Size of the protection.
/// @param flag What new permission to protect with.
///
bool VirtualProtectMemoryRegion(u8* data, size_t size, u64 flag);
private:
#ifdef _WIN32
WindowsMemoryRegion* EnsureSplitRegionForMapping(void* address, size_t size);

View File

@ -34,6 +34,9 @@ using PMapViewOfFile3 = PVOID(WINAPI*)(HANDLE FileMapping, HANDLE Process, PVOID
using PUnmapViewOfFileEx = BOOL(WINAPI*)(PVOID BaseAddress, ULONG UnmapFlags);
using PVirtualProtect = BOOL(WINAPI*)(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect,
PDWORD lpflOldProtect);
using PIsApiSetImplemented = BOOL(APIENTRY*)(PCSTR Contract);
namespace Common
@ -78,11 +81,14 @@ static bool InitWindowsMemoryFunctions(WindowsMemoryFunctions* functions)
functions->m_api_ms_win_core_memory_l1_1_6_handle.GetSymbolAddress("MapViewOfFile3FromApp");
void* const address_UnmapViewOfFileEx =
functions->m_kernel32_handle.GetSymbolAddress("UnmapViewOfFileEx");
void* const address_VirtualProtect =
functions->m_kernel32_handle.GetSymbolAddress("VirtualProtect");
if (address_VirtualAlloc2 && address_MapViewOfFile3 && address_UnmapViewOfFileEx)
{
functions->m_address_VirtualAlloc2 = address_VirtualAlloc2;
functions->m_address_MapViewOfFile3 = address_MapViewOfFile3;
functions->m_address_UnmapViewOfFileEx = address_UnmapViewOfFileEx;
functions->m_address_VirtualProtect = address_VirtualProtect;
return true;
}
@ -209,6 +215,13 @@ void MemArena::ReleaseMemoryRegion()
}
}
bool MemArena::VirtualProtectMemoryRegion(u8* data, size_t size, u64 flag)
{
DWORD lpflOldProtect = 0;
return static_cast<PVirtualProtect>(m_memory_functions.m_address_VirtualProtect)(
data, size, flag, &lpflOldProtect);
}
WindowsMemoryRegion* MemArena::EnsureSplitRegionForMapping(void* start_address, size_t size)
{
u8* const address = static_cast<u8*>(start_address);

View File

@ -289,4 +289,15 @@ size_t MemPhysical()
#endif
}
size_t PageSize()
{
#ifdef _WIN32
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
return sysInfo.dwPageSize;
#else
return sysconf(_SC_PAGESIZE);
#endif
}
} // namespace Common

View File

@ -34,5 +34,6 @@ bool ReadProtectMemory(void* ptr, size_t size);
bool WriteProtectMemory(void* ptr, size_t size, bool executable = false);
bool UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false);
size_t MemPhysical();
size_t PageSize();
} // namespace Common

View File

@ -1,48 +1,48 @@
// Copyright 2019 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <algorithm>
#include <mz_compat.h>
#include "Common/CommonTypes.h"
#include "Common/ScopeGuard.h"
namespace Common
{
// Reads all of the current file. destination must be big enough to fit the whole file.
inline bool ReadFileFromZip(unzFile file, u8* destination, u64 len)
{
const u64 MAX_BUFFER_SIZE = 65535;
if (unzOpenCurrentFile(file) != UNZ_OK)
return false;
Common::ScopeGuard guard{[&] { unzCloseCurrentFile(file); }};
u64 bytes_to_go = len;
while (bytes_to_go > 0)
{
// NOTE: multiples of 4G can't cause read_len == 0 && bytes_to_go > 0, as MAX_BUFFER_SIZE is
// small.
const u32 read_len = static_cast<u32>(std::min(bytes_to_go, MAX_BUFFER_SIZE));
const int rv = unzReadCurrentFile(file, destination, read_len);
if (rv < 0)
return false;
const u32 bytes_read = static_cast<u32>(rv);
bytes_to_go -= bytes_read;
destination += bytes_read;
}
return unzEndOfFile(file) == 1;
}
template <typename ContiguousContainer>
bool ReadFileFromZip(unzFile file, ContiguousContainer* destination)
{
return ReadFileFromZip(file, reinterpret_cast<u8*>(destination->data()), destination->size());
}
} // namespace Common
// Copyright 2019 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <algorithm>
#include <mz_compat.h>
#include "Common/CommonTypes.h"
#include "Common/ScopeGuard.h"
namespace Common
{
// Reads all of the current file. destination must be big enough to fit the whole file.
inline bool ReadFileFromZip(unzFile file, u8* destination, u64 len)
{
const u64 MAX_BUFFER_SIZE = 65535;
if (unzOpenCurrentFile(file) != UNZ_OK)
return false;
Common::ScopeGuard guard{[&] { unzCloseCurrentFile(file); }};
u64 bytes_to_go = len;
while (bytes_to_go > 0)
{
// NOTE: multiples of 4G can't cause read_len == 0 && bytes_to_go > 0, as MAX_BUFFER_SIZE is
// small.
const u32 read_len = static_cast<u32>(std::min(bytes_to_go, MAX_BUFFER_SIZE));
const int rv = unzReadCurrentFile(file, destination, read_len);
if (rv < 0)
return false;
const u32 bytes_read = static_cast<u32>(rv);
bytes_to_go -= bytes_read;
destination += bytes_read;
}
return unzEndOfFile(file) == 1;
}
template <typename ContiguousContainer>
bool ReadFileFromZip(unzFile file, ContiguousContainer* destination)
{
return ReadFileFromZip(file, reinterpret_cast<u8*>(destination->data()), destination->size());
}
} // namespace Common

View File

@ -41,12 +41,17 @@ bool MsgAlertFmt(bool yes_no, MsgType style, Common::Log::LogType log_type, cons
static_assert(NumFields == sizeof...(args),
"Unexpected number of replacement fields in format string; did you pass too few or "
"too many arguments?");
#if FMT_VERSION >= 90000
#if FMT_VERSION >= 110000
static_assert(std::is_base_of_v<fmt::detail::compile_string, S>);
auto&& format_str = fmt::format_string<Args...>(format);
#elif FMT_VERSION >= 90000
static_assert(fmt::detail::is_compile_string<S>::value);
auto&& format_str = format;
#else
static_assert(fmt::is_compile_string<S>::value);
auto&& format_str = format;
#endif
return MsgAlertFmtImpl(yes_no, style, log_type, file, line, format,
return MsgAlertFmtImpl(yes_no, style, log_type, file, line, format_str,
fmt::make_format_args(args...));
}
@ -60,7 +65,9 @@ bool MsgAlertFmtT(bool yes_no, MsgType style, Common::Log::LogType log_type, con
static_assert(NumFields == sizeof...(args),
"Unexpected number of replacement fields in format string; did you pass too few or "
"too many arguments?");
#if FMT_VERSION >= 90000
#if FMT_VERSION >= 110000
static_assert(std::is_base_of_v<fmt::detail::compile_string, S>);
#elif FMT_VERSION >= 90000
static_assert(fmt::detail::is_compile_string<S>::value);
#else
static_assert(fmt::is_compile_string<S>::value);

View File

@ -11,6 +11,7 @@
#include <fmt/ranges.h>
#include "Common/CommonTypes.h"
#include "Common/Contains.h"
#include "Common/FileUtil.h"
#include "Common/StringUtil.h"
@ -105,15 +106,14 @@ bool IsTitlePath(const std::string& path, std::optional<FromWhichRoot> from, u64
static bool IsIllegalCharacter(char c)
{
static constexpr auto illegal_chars = {'\"', '*', '/', ':', '<', '>', '?', '\\', '|', '\x7f'};
return static_cast<unsigned char>(c) <= 0x1F ||
std::find(illegal_chars.begin(), illegal_chars.end(), c) != illegal_chars.end();
static constexpr char illegal_chars[] = {'\"', '*', '/', ':', '<', '>', '?', '\\', '|', '\x7f'};
return static_cast<unsigned char>(c) <= 0x1F || Common::Contains(illegal_chars, c);
}
std::string EscapeFileName(const std::string& filename)
{
// Prevent paths from containing special names like ., .., ..., ...., and so on
if (std::all_of(filename.begin(), filename.end(), [](char c) { return c == '.'; }))
if (std::ranges::all_of(filename, [](char c) { return c == '.'; }))
return ReplaceAll(filename, ".", "__2e__");
// Escape all double underscores since we will use double underscores for our escape sequences
@ -170,8 +170,7 @@ std::string UnescapeFileName(const std::string& filename)
bool IsFileNameSafe(const std::string_view filename)
{
return !filename.empty() &&
!std::all_of(filename.begin(), filename.end(), [](char c) { return c == '.'; }) &&
std::none_of(filename.begin(), filename.end(), IsIllegalCharacter);
return !filename.empty() && !std::ranges::all_of(filename, [](char c) { return c == '.'; }) &&
std::ranges::none_of(filename, IsIllegalCharacter);
}
} // namespace Common

View File

@ -275,6 +275,21 @@ inline bool IsAlpha(char c)
return std::isalpha(c, std::locale::classic());
}
inline bool IsAlnum(char c)
{
return std::isalnum(c, std::locale::classic());
}
inline bool IsUpper(char c)
{
return std::isupper(c, std::locale::classic());
}
inline bool IsXDigit(char c)
{
return std::isxdigit(c /* no locale needed */) != 0;
}
inline char ToLower(char ch)
{
return std::tolower(ch, std::locale::classic());

View File

@ -132,7 +132,6 @@ public:
void DoIdle();
std::recursive_mutex& GetLock();
void SetHardcoreMode();
bool IsHardcoreModeActive() const;
void SetGameIniId(const std::string& game_ini_id) { m_game_ini_id = game_ini_id; }
@ -199,6 +198,8 @@ private:
void* userdata);
void DisplayWelcomeMessage();
void SetHardcoreMode();
template <typename T>
void FilterApprovedIni(std::vector<T>& codes, const std::string& game_ini_id) const;
template <typename T>
@ -275,11 +276,21 @@ private:
#include <string>
namespace ActionReplay
{
struct ARCode;
}
namespace DiscIO
{
class Volume;
}
namespace Gecko
{
class GeckoCode;
}
class AchievementManager
{
public:

View File

@ -17,6 +17,7 @@
#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "Common/Contains.h"
#include "Common/StringUtil.h"
#include "Core/Host.h"
#include "DiscIO/Enums.h"
@ -38,17 +39,13 @@ static std::optional<DiscIO::Language> TryParseLanguage(const std::string& local
// Special handling of Chinese due to its two writing systems
if (split_locale[0] == "zh")
{
const auto locale_contains = [&split_locale](std::string_view str) {
return std::find(split_locale.cbegin(), split_locale.cend(), str) != split_locale.cend();
};
if (locale_contains("Hans"))
if (Common::Contains(split_locale, "Hans"))
return DiscIO::Language::SimplifiedChinese;
if (locale_contains("Hant"))
if (Common::Contains(split_locale, "Hant"))
return DiscIO::Language::TraditionalChinese;
// Mainland China and Singapore use simplified characters
if (locale_contains("CN") || locale_contains("SG"))
if (Common::Contains(split_locale, "CN") || Common::Contains(split_locale, "SG"))
return DiscIO::Language::SimplifiedChinese;
else
return DiscIO::Language::TraditionalChinese;
@ -79,11 +76,9 @@ static DiscIO::Language ComputeDefaultLanguage()
static std::optional<std::string> TryParseCountryCode(const std::string& locale)
{
const auto is_upper = [](char c) { return std::isupper(c, std::locale::classic()); };
for (const std::string& part : SplitString(locale, '-'))
{
if (part.size() == 2 && is_upper(part[0]) && is_upper(part[1]))
if (part.size() == 2 && Common::IsUpper(part[0]) && Common::IsUpper(part[1]))
return part;
}

View File

@ -63,7 +63,7 @@ const Info<bool> GFX_CACHE_HIRES_TEXTURES{{System::GFX, "Settings", "CacheHiresT
const Info<bool> GFX_DUMP_EFB_TARGET{{System::GFX, "Settings", "DumpEFBTarget"}, false};
const Info<bool> GFX_DUMP_XFB_TARGET{{System::GFX, "Settings", "DumpXFBTarget"}, false};
const Info<bool> GFX_DUMP_FRAMES_AS_IMAGES{{System::GFX, "Settings", "DumpFramesAsImages"}, false};
const Info<bool> GFX_USE_FFV1{{System::GFX, "Settings", "UseFFV1"}, false};
const Info<bool> GFX_USE_LOSSLESS{{System::GFX, "Settings", "UseLossless"}, false};
const Info<std::string> GFX_DUMP_FORMAT{{System::GFX, "Settings", "DumpFormat"}, "avi"};
const Info<std::string> GFX_DUMP_CODEC{{System::GFX, "Settings", "DumpCodec"}, ""};
const Info<std::string> GFX_DUMP_PIXEL_FORMAT{{System::GFX, "Settings", "DumpPixelFormat"}, ""};

View File

@ -62,7 +62,7 @@ extern const Info<bool> GFX_CACHE_HIRES_TEXTURES;
extern const Info<bool> GFX_DUMP_EFB_TARGET;
extern const Info<bool> GFX_DUMP_XFB_TARGET;
extern const Info<bool> GFX_DUMP_FRAMES_AS_IMAGES;
extern const Info<bool> GFX_USE_FFV1;
extern const Info<bool> GFX_USE_LOSSLESS;
extern const Info<std::string> GFX_DUMP_FORMAT;
extern const Info<std::string> GFX_DUMP_CODEC;
extern const Info<std::string> GFX_DUMP_PIXEL_FORMAT;

View File

@ -114,9 +114,8 @@ public:
for (const auto& value : section_map)
{
const Config::Location location{system.first, section_name, value.first};
const bool load_disallowed =
std::any_of(begin(s_setting_disallowed), end(s_setting_disallowed),
[&location](const Config::Location* l) { return *l == location; });
const bool load_disallowed = std::ranges::any_of(
s_setting_disallowed, [&location](const auto* l) { return *l == location; });
if (load_disallowed)
continue;

View File

@ -6,6 +6,7 @@
#include <algorithm>
#include <array>
#include "Common/Contains.h"
#include "Core/Config/WiimoteSettings.h"
namespace ConfigLoaders
@ -15,8 +16,7 @@ bool IsSettingSaveable(const Config::Location& config_location)
static constexpr std::array systems_not_saveable = {Config::System::GCPad, Config::System::WiiPad,
Config::System::GCKeyboard};
if (std::find(begin(systems_not_saveable), end(systems_not_saveable), config_location.system) ==
end(systems_not_saveable))
if (!Common::Contains(systems_not_saveable, config_location.system))
{
return true;
}
@ -27,9 +27,8 @@ bool IsSettingSaveable(const Config::Location& config_location)
&Config::WIIMOTE_BB_SOURCE.GetLocation(),
};
return std::any_of(begin(s_setting_saveable), end(s_setting_saveable),
[&config_location](const Config::Location* location) {
return *location == config_location;
});
return std::ranges::any_of(s_setting_saveable, [&config_location](const auto* location) {
return *location == config_location;
});
}
} // namespace ConfigLoaders

View File

@ -41,6 +41,7 @@ static void LoadFromDTM(Config::Layer* config_layer, Movie::DTMHeader* dtm)
else
config_layer->Set(Config::MAIN_GC_LANGUAGE, static_cast<int>(dtm->language));
config_layer->Set(Config::SYSCONF_WIDESCREEN, dtm->bWidescreen);
config_layer->Set(Config::SYSCONF_COUNTRY, dtm->countryCode);
config_layer->Set(Config::GFX_HACK_EFB_ACCESS_ENABLE, dtm->bEFBAccessEnable);
config_layer->Set(Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM, dtm->bSkipEFBCopyToRam);
@ -69,6 +70,7 @@ void SaveToDTM(Movie::DTMHeader* dtm)
else
dtm->language = Config::Get(Config::MAIN_GC_LANGUAGE);
dtm->bWidescreen = Config::Get(Config::SYSCONF_WIDESCREEN);
dtm->countryCode = Config::Get(Config::SYSCONF_COUNTRY);
dtm->bEFBAccessEnable = Config::Get(Config::GFX_HACK_EFB_ACCESS_ENABLE);
dtm->bSkipEFBCopyToRam = Config::Get(Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM);

View File

@ -75,6 +75,7 @@
#include "Core/State.h"
#include "Core/System.h"
#include "Core/WiiRoot.h"
#include "Core/HW/Memmap.h"
#ifdef USE_MEMORYWATCHER
#include "Core/MemoryWatcher.h"
@ -194,7 +195,7 @@ void DisplayMessage(std::string message, int time_in_ms)
return;
// Actually displaying non-ASCII could cause things to go pear-shaped
if (!std::all_of(message.begin(), message.end(), Common::IsPrintableCharacter))
if (!std::ranges::all_of(message, Common::IsPrintableCharacter))
return;
OSD::AddMessage(std::move(message), time_in_ms);
@ -385,8 +386,9 @@ static void CpuThread(Core::System& system, const std::optional<std::string>& sa
// The JIT need to be able to intercept faults, both for fastmem and for the BLR optimization.
const bool exception_handler = EMM::IsExceptionHandlerSupported();
if (exception_handler)
{
EMM::InstallExceptionHandler();
}
#ifdef USE_MEMORYWATCHER
s_memory_watcher = std::make_unique<MemoryWatcher>();
#endif
@ -689,6 +691,9 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
cpuThreadFunc(system, savestate_path, delete_savestate);
}
if (cpuThreadFunc == CpuThread)
system.GetMemory().InitDirtyPages();
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Stopping GDB ..."));
GDBStub::Deinit();
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "GDB stopped."));

View File

@ -776,14 +776,11 @@ bool DSPAssembler::AssemblePass(const std::string& text, int pass)
m_location.line_num = 0;
m_cur_pass = pass;
#define LINEBUF_SIZE 1024
char line[LINEBUF_SIZE] = {0};
while (!m_failed && !fsrc.fail() && !fsrc.eof())
constexpr int LINEBUF_SIZE = 1024;
char line[LINEBUF_SIZE] = {};
while (!m_failed && fsrc.getline(line, LINEBUF_SIZE))
{
int opcode_size = 0;
fsrc.getline(line, LINEBUF_SIZE);
if (fsrc.fail())
break;
m_location.line_text = line;
m_location.line_num++;

View File

@ -7,6 +7,7 @@
#include <chrono>
#include <regex>
#include "Common/Contains.h"
#include "Common/Event.h"
#include "Core/Core.h"
#include "Core/Debugger/PPCDebugInterface.h"
@ -254,12 +255,9 @@ HitType CodeTrace::TraceLogic(const TraceOutput& current_instr, bool first_hit)
// The reg_itr will be used later for erasing.
auto reg_itr = std::ranges::find(m_reg_autotrack, instr.reg0);
const bool match_reg123 =
(!instr.reg1.empty() && std::find(m_reg_autotrack.begin(), m_reg_autotrack.end(),
instr.reg1) != m_reg_autotrack.end()) ||
(!instr.reg2.empty() && std::find(m_reg_autotrack.begin(), m_reg_autotrack.end(),
instr.reg2) != m_reg_autotrack.end()) ||
(!instr.reg3.empty() && std::find(m_reg_autotrack.begin(), m_reg_autotrack.end(),
instr.reg3) != m_reg_autotrack.end());
(!instr.reg1.empty() && Common::Contains(m_reg_autotrack, instr.reg1)) ||
(!instr.reg2.empty() && Common::Contains(m_reg_autotrack, instr.reg2)) ||
(!instr.reg3.empty() && Common::Contains(m_reg_autotrack, instr.reg3));
const bool match_reg0 = reg_itr != m_reg_autotrack.end();
if (!match_reg0 && !match_reg123 && !mem_hit)
@ -267,8 +265,8 @@ HitType CodeTrace::TraceLogic(const TraceOutput& current_instr, bool first_hit)
// Checks if the intstruction is a type that needs special handling.
const auto CompareInstruction = [](std::string_view instruction, const auto& type_compare) {
return std::any_of(type_compare.begin(), type_compare.end(),
[&instruction](std::string_view s) { return instruction.starts_with(s); });
return std::ranges::any_of(
type_compare, [&instruction](std::string_view s) { return instruction.starts_with(s); });
};
// Exclusions from updating tracking logic. mt operations are too complex and specialized.

View File

@ -13,6 +13,7 @@
#include <fmt/format.h>
#include "Common/Align.h"
#include "Common/Contains.h"
#include "Common/GekkoDisassembler.h"
#include "Common/StringUtil.h"
@ -253,7 +254,7 @@ Common::Debug::Threads PPCDebugInterface::GetThreads(const Core::CPUThreadGuard&
const auto insert_threads = [&guard, &threads, &visited_addrs](u32 addr, auto get_next_addr) {
while (addr != 0 && PowerPC::MMU::HostIsRAMAddress(guard, addr))
{
if (std::find(visited_addrs.begin(), visited_addrs.end(), addr) != visited_addrs.end())
if (Common::Contains(visited_addrs, addr))
break;
visited_addrs.push_back(addr);
auto thread = std::make_unique<Core::Debug::OSThreadView>(guard, addr);

View File

@ -17,13 +17,10 @@
namespace Gecko
{
std::vector<GeckoCode> DownloadCodes(std::string gametdb_id, bool* succeeded, bool use_https)
std::vector<GeckoCode> DownloadCodes(std::string gametdb_id, bool* succeeded)
{
// TODO: Fix https://bugs.dolphin-emu.org/issues/11772 so we don't need this workaround
const std::string protocol = use_https ? "https://" : "http://";
// codes.rc24.xyz is a mirror of the now defunct geckocodes.org.
std::string endpoint{protocol + "codes.rc24.xyz/txt.php?txt=" + gametdb_id};
std::string endpoint{"https://codes.rc24.xyz/txt.php?txt=" + gametdb_id};
Common::HttpRequest http;
// The server always redirects once to the same location.

View File

@ -17,8 +17,7 @@ class IniFile;
namespace Gecko
{
std::vector<GeckoCode> LoadCodes(const Common::IniFile& globalIni, const Common::IniFile& localIni);
std::vector<GeckoCode> DownloadCodes(std::string gametdb_id, bool* succeeded,
bool use_https = true);
std::vector<GeckoCode> DownloadCodes(std::string gametdb_id, bool* succeeded);
void SaveCodes(Common::IniFile& inifile, const std::vector<GeckoCode>& gcodes);
std::optional<GeckoCode::Code> DeserializeLine(const std::string& line);

View File

@ -686,7 +686,7 @@ bool CEXIETHERNET::BuiltInBBAInterface::SendFrame(const u8* frame, u32 size)
}
default:
ERROR_LOG_FMT(SP1, "Unsupported EtherType {#06x}", *ethertype);
ERROR_LOG_FMT(SP1, "Unsupported EtherType {:#06x}", *ethertype);
return false;
}

View File

@ -101,8 +101,8 @@ std::pair<GCMemcardErrorCode, std::optional<GCMemcard>> GCMemcard::Open(std::str
MBIT_SIZE_MEMORY_CARD_2043,
}};
if (!std::any_of(valid_megabits.begin(), valid_megabits.end(),
[filesize_megabits](u64 mbits) { return mbits == filesize_megabits; }))
if (!std::ranges::any_of(valid_megabits,
[filesize_megabits](u64 mbits) { return mbits == filesize_megabits; }))
{
error_code.Set(GCMemcardValidityIssues::INVALID_CARD_SIZE);
return std::make_pair(error_code, std::nullopt);
@ -1296,8 +1296,8 @@ GCMemcardErrorCode Header::CheckForErrors(u16 card_size_mbits) const
error_code.Set(GCMemcardValidityIssues::MISMATCHED_CARD_SIZE);
// unused areas, should always be filled with 0xFF
if (std::any_of(m_unused_1.begin(), m_unused_1.end(), [](u8 val) { return val != 0xFF; }) ||
std::any_of(m_unused_2.begin(), m_unused_2.end(), [](u8 val) { return val != 0xFF; }))
if (std::ranges::any_of(m_unused_1, [](u8 val) { return val != 0xFF; }) ||
std::ranges::any_of(m_unused_2, [](u8 val) { return val != 0xFF; }))
{
error_code.Set(GCMemcardValidityIssues::DATA_IN_UNUSED_AREA);
}
@ -1361,7 +1361,7 @@ GCMemcardErrorCode Directory::CheckForErrors() const
error_code.Set(GCMemcardValidityIssues::INVALID_CHECKSUM);
// unused area, should always be filled with 0xFF
if (std::any_of(m_padding.begin(), m_padding.end(), [](u8 val) { return val != 0xFF; }))
if (std::ranges::any_of(m_padding, [](u8 val) { return val != 0xFF; }))
error_code.Set(GCMemcardValidityIssues::DATA_IN_UNUSED_AREA);
return error_code;

View File

@ -82,9 +82,9 @@ void Shutdown(Core::System& system)
system.GetCoreTiming().Shutdown();
}
void DoState(Core::System& system, PointerWrap& p)
void DoState(Core::System& system, PointerWrap& p, bool delta)
{
system.GetMemory().DoState(p);
system.GetMemory().DoState(p, delta);
p.DoMarker("Memory");
system.GetMemoryInterface().DoState(p);
p.DoMarker("MemoryInterface");

View File

@ -14,5 +14,5 @@ namespace HW
{
void Init(Core::System& system, const Sram* override_sram);
void Shutdown(Core::System& system);
void DoState(Core::System& system, PointerWrap& p);
void DoState(Core::System& system, PointerWrap& p, bool delta);
} // namespace HW

View File

@ -47,6 +47,57 @@ MemoryManager::MemoryManager(Core::System& system) : m_system(system)
MemoryManager::~MemoryManager() = default;
u64 MemoryManager::GetDirtyPageIndexFromAddress(u64 address)
{
const size_t page_size = Common::PageSize();
const size_t page_mask = page_size - 1;
return address & ~page_mask;
}
bool MemoryManager::HandleFault(uintptr_t fault_address)
{
u8* fault_address_bytes = reinterpret_cast<u8*>(fault_address);
if (!IsAddressInEmulatedMemory(fault_address_bytes) || IsPageDirty(fault_address))
{
return false;
}
SetPageDirtyBit(fault_address, 0x1, true);
bool change_protection =
m_arena.VirtualProtectMemoryRegion(fault_address_bytes, 0x1, PAGE_READWRITE);
if (!change_protection)
{
return false;
}
return true;
}
void MemoryManager::WriteProtectPhysicalMemoryRegions()
{
for (const PhysicalMemoryRegion& region : m_physical_regions)
{
if (!region.active || !region.track)
continue;
bool change_protection =
m_arena.VirtualProtectMemoryRegion((*region.out_pointer), region.size, PAGE_READONLY);
if (!change_protection)
{
PanicAlertFmt("Memory::WriteProtectPhysicalMemoryRegions(): Failed to write protect for "
"this block of memory at 0x{:08X}.",
reinterpret_cast<u64>(*region.out_pointer));
}
const size_t page_size = Common::PageSize();
const intptr_t out_pointer = reinterpret_cast<intptr_t>(*region.out_pointer);
for (size_t i = out_pointer; i < region.size; i += page_size)
{
m_dirty_pages[i] = false;
}
}
}
void MemoryManager::InitMMIO(bool is_wii)
{
m_mmio_mapping = std::make_unique<MMIO::Mapping>();
@ -71,6 +122,11 @@ void MemoryManager::InitMMIO(bool is_wii)
}
}
void MemoryManager::InitDirtyPages()
{
WriteProtectPhysicalMemoryRegions();
}
void MemoryManager::Init()
{
const auto get_mem1_size = [] {
@ -95,13 +151,14 @@ void MemoryManager::Init()
m_exram_mask = GetExRamSize() - 1;
m_physical_regions[0] = PhysicalMemoryRegion{
&m_ram, 0x00000000, GetRamSize(), PhysicalMemoryRegion::ALWAYS, 0, false};
&m_ram, 0x00000000, GetRamSize(), PhysicalMemoryRegion::ALWAYS, 0, false, true};
m_physical_regions[1] = PhysicalMemoryRegion{
&m_l1_cache, 0xE0000000, GetL1CacheSize(), PhysicalMemoryRegion::ALWAYS, 0, false};
&m_l1_cache, 0xE0000000, GetL1CacheSize(), PhysicalMemoryRegion::ALWAYS, 0, false, false};
m_physical_regions[2] = PhysicalMemoryRegion{
&m_fake_vmem, 0x7E000000, GetFakeVMemSize(), PhysicalMemoryRegion::FAKE_VMEM, 0, false};
&m_fake_vmem, 0x7E000000, GetFakeVMemSize(), PhysicalMemoryRegion::FAKE_VMEM, 0,
false, false};
m_physical_regions[3] = PhysicalMemoryRegion{
&m_exram, 0x10000000, GetExRamSize(), PhysicalMemoryRegion::WII_ONLY, 0, false};
&m_exram, 0x10000000, GetExRamSize(), PhysicalMemoryRegion::WII_ONLY, 0, false, true};
const bool wii = m_system.IsWii();
const bool mmu = m_system.IsMMUMode();
@ -164,6 +221,21 @@ bool MemoryManager::IsAddressInFastmemArea(const u8* address) const
return address >= m_fastmem_arena && address < m_fastmem_arena + m_fastmem_arena_size;
}
bool MemoryManager::IsAddressInEmulatedMemory(const u8* address) const
{
for (const PhysicalMemoryRegion& region : m_physical_regions)
{
if (!region.active || !region.track)
continue;
if (address >= *region.out_pointer && address < *region.out_pointer + region.size)
{
return true;
}
}
return false;
}
bool MemoryManager::InitFastmemArena()
{
// Here we set up memory mappings for fastmem. The basic idea of fastmem is that we reserve 4 GiB
@ -290,7 +362,7 @@ void MemoryManager::UpdateLogicalMemory(const PowerPC::BatTable& dbat_table)
}
}
void MemoryManager::DoState(PointerWrap& p)
void MemoryManager::DoState(PointerWrap& p, bool delta)
{
const u32 current_ram_size = GetRamSize();
const u32 current_l1_cache_size = GetL1CacheSize();
@ -327,28 +399,60 @@ void MemoryManager::DoState(PointerWrap& p)
p.SetVerifyMode();
return;
}
p.DoArray(m_ram, current_ram_size);
p.DoArray(m_l1_cache, current_l1_cache_size);
p.DoMarker("Memory RAM");
if (current_have_fake_vmem)
p.DoArray(m_fake_vmem, current_fake_vmem_size);
p.DoMarker("Memory FakeVMEM");
if (current_have_exram)
p.DoArray(m_exram, current_exram_size);
p.DoMarker("Memory EXRAM");
if (delta)
{
const u32 page_size = static_cast<u32>(Common::PageSize());
p.Do(m_dirty_pages);
for (size_t i = 0; i < current_ram_size; i += page_size)
{
if (IsPageDirty(reinterpret_cast<uintptr_t>(&m_ram[i])))
{
p.DoArray(m_ram + i, page_size);
}
}
p.DoArray(m_l1_cache, current_l1_cache_size);
p.DoMarker("Memory RAM");
if (current_have_fake_vmem)
{
p.DoArray(m_fake_vmem, current_fake_vmem_size);
}
p.DoMarker("Memory FakeVMEM");
if (current_have_exram)
{
for (size_t i = 0; i < current_exram_size; i += page_size)
{
if (IsPageDirty(reinterpret_cast<uintptr_t>(&m_exram[i])))
{
p.DoArray(m_exram + i, page_size);
}
}
}
p.DoMarker("Memory EXRAM");
}
else
{
p.DoArray(m_ram, current_ram_size);
p.DoArray(m_l1_cache, current_l1_cache_size);
p.DoMarker("Memory RAM");
if (current_have_fake_vmem)
p.DoArray(m_fake_vmem, current_fake_vmem_size);
p.DoMarker("Memory FakeVMEM");
if (current_have_exram)
p.DoArray(m_exram, current_exram_size);
p.DoMarker("Memory EXRAM");
}
}
void MemoryManager::Shutdown()
{
ShutdownFastmemArena();
m_dirty_pages.clear();
m_is_initialized = false;
for (const PhysicalMemoryRegion& region : m_physical_regions)
{
if (!region.active)
continue;
m_arena.ReleaseView(*region.out_pointer, region.size);
*region.out_pointer = nullptr;
}
@ -573,4 +677,22 @@ void MemoryManager::Write_U64_Swap(u64 value, u32 address)
CopyToEmu(address, &value, sizeof(value));
}
bool MemoryManager::IsPageDirty(uintptr_t address)
{
return m_dirty_pages[GetDirtyPageIndexFromAddress(address)];
}
void MemoryManager::SetPageDirtyBit(uintptr_t address, size_t size, bool dirty)
{
for (size_t i = 0; i < size; i++)
{
m_dirty_pages[GetDirtyPageIndexFromAddress(address + i)] = dirty;
}
}
void MemoryManager::ResetDirtyPages()
{
WriteProtectPhysicalMemoryRegions();
}
} // namespace Memory

View File

@ -8,6 +8,7 @@
#include <span>
#include <string>
#include <vector>
#include <map>
#include "Common/CommonTypes.h"
#include "Common/MathUtil.h"
@ -48,6 +49,7 @@ struct PhysicalMemoryRegion
} flags;
u32 shm_position;
bool active;
bool track;
};
struct LogicalMemoryView
@ -78,6 +80,7 @@ public:
u32 GetExRamMask() const { return m_exram_mask; }
bool IsAddressInFastmemArea(const u8* address) const;
bool IsAddressInEmulatedMemory(const u8* address) const;
u8* GetPhysicalBase() const { return m_physical_base; }
u8* GetLogicalBase() const { return m_logical_base; }
u8* GetPhysicalPageMappingsBase() const { return m_physical_page_mappings_base; }
@ -94,10 +97,11 @@ public:
// Init and Shutdown
bool IsInitialized() const { return m_is_initialized; }
void Init();
void InitDirtyPages();
void Shutdown();
bool InitFastmemArena();
void ShutdownFastmemArena();
void DoState(PointerWrap& p);
void DoState(PointerWrap& p, bool delta);
void UpdateLogicalMemory(const PowerPC::BatTable& dbat_table);
@ -130,6 +134,13 @@ public:
void Write_U32_Swap(u32 var, u32 address);
void Write_U64_Swap(u64 var, u32 address);
bool IsPageDirty(uintptr_t address);
void SetPageDirtyBit(uintptr_t address, size_t size, bool dirty);
void ResetDirtyPages();
bool HandleFault(uintptr_t fault_address);
std::map<u64, u8>& GetDirtyPages() { return m_dirty_pages; }
// Templated functions for byteswapped copies.
template <typename T>
void CopyFromEmuSwapped(T* data, u32 address, size_t size) const
@ -254,6 +265,11 @@ private:
Core::System& m_system;
std::map<u64, u8> m_dirty_pages;
u64 GetDirtyPageIndexFromAddress(u64 address);
void WriteProtectPhysicalMemoryRegions();
void InitMMIO(bool is_wii);
};
} // namespace Memory

View File

@ -613,8 +613,7 @@ void WiimoteScanner::SetScanMode(WiimoteScanMode scan_mode)
bool WiimoteScanner::IsReady() const
{
std::lock_guard lg(m_backends_mutex);
return std::any_of(m_backends.begin(), m_backends.end(),
[](const auto& backend) { return backend->IsReady(); });
return std::ranges::any_of(m_backends, &WiimoteScannerBackend::IsReady);
}
static void CheckForDisconnectedWiimotes()

View File

@ -98,8 +98,8 @@ bool IOCtlVRequest::HasNumberOfValidVectors(const size_t in_count, const size_t
return false;
auto IsValidVector = [](const auto& vector) { return vector.size == 0 || vector.address != 0; };
return std::all_of(in_vectors.begin(), in_vectors.end(), IsValidVector) &&
std::all_of(io_vectors.begin(), io_vectors.end(), IsValidVector);
return std::ranges::all_of(in_vectors, IsValidVector) &&
std::ranges::all_of(io_vectors, IsValidVector);
}
void IOCtlRequest::Log(std::string_view device_name, Common::Log::LogType type,

View File

@ -298,7 +298,7 @@ std::string TMDReader::GetGameID() const
std::memcpy(game_id, m_bytes.data() + offsetof(TMDHeader, title_id) + 4, 4);
std::memcpy(game_id + 4, m_bytes.data() + offsetof(TMDHeader, group_id), 2);
if (std::all_of(std::begin(game_id), std::end(game_id), Common::IsPrintableCharacter))
if (std::ranges::all_of(game_id, Common::IsPrintableCharacter))
return std::string(game_id, sizeof(game_id));
return fmt::format("{:016x}", GetTitleId());

View File

@ -79,8 +79,7 @@ static bool IsValidPartOfTitleID(const std::string& string)
{
if (string.length() != 8)
return false;
return std::all_of(string.begin(), string.end(),
[](const auto character) { return std::isxdigit(character) != 0; });
return std::ranges::all_of(string, Common::IsXDigit);
}
static std::vector<u64> GetTitlesInTitleOrImport(FS::FileSystem* fs, const std::string& titles_dir)

View File

@ -464,7 +464,7 @@ static bool HasAllRequiredContents(Kernel& ios, const ES::TMDReader& tmd)
const u64 title_id = tmd.GetTitleId();
const std::vector<ES::Content> contents = tmd.GetContents();
const ES::SharedContentMap shared_content_map{ios.GetFSCore()};
return std::all_of(contents.cbegin(), contents.cend(), [&](const ES::Content& content) {
return std::ranges::all_of(contents, [&](const ES::Content& content) {
if (content.IsOptional())
return true;
@ -878,7 +878,7 @@ ReturnCode ESCore::DeleteSharedContent(const std::array<u8, 20>& sha1) const
// Check whether the shared content is used by a system title.
const std::vector<u64> titles = GetInstalledTitles();
const bool is_used_by_system_title = std::any_of(titles.begin(), titles.end(), [&](u64 id) {
const bool is_used_by_system_title = std::ranges::any_of(titles, [&](u64 id) {
if (!ES::IsTitleType(id, ES::TitleType::System))
return false;
@ -887,8 +887,8 @@ ReturnCode ESCore::DeleteSharedContent(const std::array<u8, 20>& sha1) const
return true;
const auto contents = tmd.GetContents();
return std::any_of(contents.begin(), contents.end(),
[&sha1](const auto& content) { return content.sha1 == sha1; });
return std::ranges::any_of(contents,
[&sha1](const auto& content) { return content.sha1 == sha1; });
});
// Any shared content used by a system title cannot be deleted.

View File

@ -26,7 +26,7 @@ bool IsValidNonRootPath(std::string_view path)
bool IsValidFilename(std::string_view filename)
{
return filename.length() <= MaxFilenameLength &&
!std::any_of(filename.begin(), filename.end(), [](char c) { return c == '/'; });
!std::ranges::any_of(filename, [](char c) { return c == '/'; });
}
SplitPathResult SplitPathAndBasename(std::string_view path)

View File

@ -461,8 +461,7 @@ ResultCode HostFileSystem::Format(Uid uid)
ResultCode HostFileSystem::CreateFileOrDirectory(Uid uid, Gid gid, const std::string& path,
FileAttribute attr, Modes modes, bool is_file)
{
if (!IsValidNonRootPath(path) ||
!std::all_of(path.begin(), path.end(), Common::IsPrintableCharacter))
if (!IsValidNonRootPath(path) || !std::ranges::all_of(path, Common::IsPrintableCharacter))
{
return ResultCode::Invalid;
}
@ -516,14 +515,14 @@ ResultCode HostFileSystem::CreateDirectory(Uid uid, Gid gid, const std::string&
bool HostFileSystem::IsFileOpened(const std::string& path) const
{
return std::any_of(m_handles.begin(), m_handles.end(), [&path](const Handle& handle) {
return std::ranges::any_of(m_handles, [&path](const Handle& handle) {
return handle.opened && handle.wii_path == path;
});
}
bool HostFileSystem::IsDirectoryInUse(const std::string& path) const
{
return std::any_of(m_handles.begin(), m_handles.end(), [&path](const Handle& handle) {
return std::ranges::any_of(m_handles, [&path](const Handle& handle) {
return handle.opened && handle.wii_path.starts_with(path);
});
}

View File

@ -58,8 +58,8 @@ bool WC24FriendList::CheckFriendList() const
bool WC24FriendList::DoesFriendExist(u64 friend_id) const
{
return std::any_of(m_data.friend_codes.cbegin(), m_data.friend_codes.cend(),
[&friend_id](const u64 v) { return v == friend_id; });
return std::ranges::any_of(m_data.friend_codes,
[&friend_id](const u64 v) { return v == friend_id; });
}
std::vector<u64> WC24FriendList::GetUnconfirmedFriends() const

View File

@ -1085,10 +1085,10 @@ void WiiSockMan::UpdatePollCommands()
std::vector<int> original_order(pfds.size());
std::iota(original_order.begin(), original_order.end(), 0);
// Select indices with valid fds
auto mid = std::partition(original_order.begin(), original_order.end(), [&](auto i) {
const auto partition_result = std::ranges::partition(original_order, [&](auto i) {
return GetHostSocket(memory.Read_U32(pcmd.buffer_out + 0xc * i)) >= 0;
});
const auto n_valid = std::distance(original_order.begin(), mid);
const auto n_valid = std::distance(original_order.begin(), partition_result.begin());
// Move all the valid pollfds to the front of the vector
for (auto i = 0; i < n_valid; ++i)

View File

@ -74,7 +74,7 @@ bool Device::HasClass(const u8 device_class) const
if (GetDeviceDescriptor().bDeviceClass == device_class)
return true;
const auto interfaces = GetInterfaces(0);
return std::any_of(interfaces.begin(), interfaces.end(), [device_class](const auto& interface) {
return std::ranges::any_of(interfaces, [device_class](const auto& interface) {
return interface.bInterfaceClass == device_class;
});
}

View File

@ -234,7 +234,7 @@ std::optional<IPCReply> OH0::RegisterClassChangeHook(const IOCtlVRequest& reques
bool OH0::HasDeviceWithVidPid(const u16 vid, const u16 pid) const
{
return std::any_of(m_devices.begin(), m_devices.end(), [=](const auto& device) {
return std::ranges::any_of(m_devices, [=](const auto& device) {
return device.second->GetVid() == vid && device.second->GetPid() == pid;
});
}

View File

@ -380,9 +380,9 @@ bool IsEmulated(u32 major_version)
if (major_version == static_cast<u32>(Titles::BC & 0xffffffff))
return true;
return std::any_of(
ios_memory_values.begin(), ios_memory_values.end(),
[major_version](const MemoryValues& values) { return values.ios_number == major_version; });
return std::ranges::any_of(ios_memory_values, [major_version](const MemoryValues& values) {
return values.ios_number == major_version;
});
}
bool IsEmulated(u64 title_id)

View File

@ -375,8 +375,8 @@ s32 WFSSRVDevice::Rename(std::string source, std::string dest) const
INFO_LOG_FMT(IOS_WFS, "IOCTL_WFS_RENAME: {} to {}", source, dest);
const bool opened = std::any_of(m_fds.begin(), m_fds.end(),
[&](const auto& fd) { return fd.in_use && fd.path == source; });
const bool opened =
std::ranges::any_of(m_fds, [&](const auto& fd) { return fd.in_use && fd.path == source; });
if (opened)
return WFS_FILE_IS_OPENED;

View File

@ -14,6 +14,7 @@
#include "Common/MsgHandler.h"
#include "Common/Thread.h"
#include "Core/HW/Memmap.h"
#include "Core/MachineContext.h"
#include "Core/PowerPC/JitInterface.h"
#include "Core/System.h"
@ -24,6 +25,7 @@
#ifndef _WIN32
#include <unistd.h> // Needed for _POSIX_VERSION
#endif
#include <Common/MemoryUtil.h>
#if defined(__APPLE__)
#ifdef _M_X86_64
@ -60,8 +62,12 @@ static LONG NTAPI Handler(PEXCEPTION_POINTERS pPtrs)
// virtual address of the inaccessible data
uintptr_t fault_address = (uintptr_t)pPtrs->ExceptionRecord->ExceptionInformation[1];
SContext* ctx = pPtrs->ContextRecord;
if (Core::System::GetInstance().GetJitInterface().HandleFault(fault_address, ctx))
Core::System& system = Core::System::GetInstance();
if (system.GetMemory().HandleFault(fault_address))
{
return EXCEPTION_CONTINUE_EXECUTION;
}
else if (system.GetJitInterface().HandleFault(fault_address, ctx))
{
return EXCEPTION_CONTINUE_EXECUTION;
}

View File

@ -93,7 +93,7 @@ static std::array<u8, 20> ConvertGitRevisionToBytes(const std::string& revision)
{
std::array<u8, 20> revision_bytes{};
if (revision.size() % 2 == 0 && std::all_of(revision.begin(), revision.end(), ::isxdigit))
if (revision.size() % 2 == 0 && std::ranges::all_of(revision, Common::IsXDigit))
{
// The revision string normally contains a git commit hash,
// which is 40 hexadecimal digits long. In DTM files, each pair of
@ -1081,11 +1081,11 @@ void MovieManager::LoadInput(const std::string& movie_path)
std::vector<u8> movInput(m_current_byte);
t_record.ReadArray(movInput.data(), movInput.size());
const auto result = std::mismatch(movInput.begin(), movInput.end(), m_temp_input.begin());
const auto mismatch_result = std::ranges::mismatch(movInput, m_temp_input);
if (result.first != movInput.end())
if (mismatch_result.in1 != movInput.end())
{
const ptrdiff_t mismatch_index = std::distance(movInput.begin(), result.first);
const ptrdiff_t mismatch_index = std::distance(movInput.begin(), mismatch_result.in1);
// this is a "you did something wrong" alert for the user's benefit.
// we'll try to say what's going on in excruciating detail, otherwise the user might not

View File

@ -132,7 +132,8 @@ struct DTMHeader
bool bUseFMA;
u8 GBAControllers; // GBA Controllers plugged in (the bits are ports 1-4)
bool bWidescreen; // true indicates SYSCONF aspect ratio is 16:9, false for 4:3
std::array<u8, 6> reserved; // Padding for any new config options
u8 countryCode; // SYSCONF country code
std::array<u8, 5> reserved; // Padding for any new config options
std::array<char, 40> discChange; // Name of iso file to switch to, for two disc games.
std::array<u8, 20> revision; // Git hash
u32 DSPiromHash;

View File

@ -2486,8 +2486,8 @@ bool NetPlayClient::PlayerHasControllerMapped(const PlayerId pid) const
{
const auto mapping_matches_player_id = [pid](const PlayerId& mapping) { return mapping == pid; };
return std::any_of(m_pad_map.begin(), m_pad_map.end(), mapping_matches_player_id) ||
std::any_of(m_wiimote_map.begin(), m_wiimote_map.end(), mapping_matches_player_id);
return std::ranges::any_of(m_pad_map, mapping_matches_player_id) ||
std::ranges::any_of(m_wiimote_map, mapping_matches_player_id);
}
bool NetPlayClient::IsLocalPlayer(const PlayerId pid) const
@ -2543,7 +2543,7 @@ bool NetPlayClient::DoAllPlayersHaveGame()
{
std::lock_guard lkp(m_crit.players);
return std::all_of(std::begin(m_players), std::end(m_players), [](auto entry) {
return std::ranges::all_of(m_players, [](const auto& entry) {
return entry.second.game_status == SyncIdentifierComparison::SameGame;
});
}

View File

@ -237,7 +237,7 @@ static bool DecompressPacketIntoFolderInternal(sf::Packet& packet, const std::st
if (name.find('\\') != std::string::npos)
return false;
#endif
if (std::all_of(name.begin(), name.end(), [](char c) { return c == '.'; }))
if (std::ranges::all_of(name, [](char c) { return c == '.'; }))
return false;
bool is_folder;

View File

@ -1060,14 +1060,14 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player)
{
// we have all records for this frame
if (!std::all_of(timebases.begin(), timebases.end(), [&](std::pair<PlayerId, u64> pair) {
if (!std::ranges::all_of(timebases, [&](std::pair<PlayerId, u64> pair) {
return pair.second == timebases[0].second;
}))
{
int pid_to_blame = 0;
for (auto pair : timebases)
{
if (std::all_of(timebases.begin(), timebases.end(), [&](std::pair<PlayerId, u64> other) {
if (std::ranges::all_of(timebases, [&](std::pair<PlayerId, u64> other) {
return other.first == pair.first || other.second != pair.second;
}))
{
@ -1467,14 +1467,12 @@ bool NetPlayServer::SetupNetSettings()
bool NetPlayServer::DoAllPlayersHaveIPLDump() const
{
return std::all_of(m_players.begin(), m_players.end(),
[](const auto& p) { return p.second.has_ipl_dump; });
return std::ranges::all_of(m_players, [](const auto& p) { return p.second.has_ipl_dump; });
}
bool NetPlayServer::DoAllPlayersHaveHardwareFMA() const
{
return std::all_of(m_players.begin(), m_players.end(),
[](const auto& p) { return p.second.has_hardware_fma; });
return std::ranges::all_of(m_players, [](const auto& p) { return p.second.has_hardware_fma; });
}
struct SaveSyncInfo
@ -2234,8 +2232,8 @@ bool NetPlayServer::PlayerHasControllerMapped(const PlayerId pid) const
{
const auto mapping_matches_player_id = [pid](const PlayerId& mapping) { return mapping == pid; };
return std::any_of(m_pad_map.begin(), m_pad_map.end(), mapping_matches_player_id) ||
std::any_of(m_wiimote_map.begin(), m_wiimote_map.end(), mapping_matches_player_id);
return std::ranges::any_of(m_pad_map, mapping_matches_player_id) ||
std::ranges::any_of(m_wiimote_map, mapping_matches_player_id);
}
void NetPlayServer::AssignNewUserAPad(const Client& player)

View File

@ -371,7 +371,7 @@ bool MemChecks::OverlapsMemcheck(u32 address, u32 length) const
const u32 page_end_suffix = length - 1;
const u32 page_end_address = address | page_end_suffix;
return std::any_of(m_mem_checks.cbegin(), m_mem_checks.cend(), [&](const auto& mc) {
return std::ranges::any_of(m_mem_checks, [&](const auto& mc) {
return ((mc.start_address | page_end_suffix) == page_end_address ||
(mc.end_address | page_end_suffix) == page_end_address) ||
((mc.start_address | page_end_suffix) < page_end_address &&

View File

@ -136,15 +136,14 @@ static double CallstackFunc(expr_func* f, vec_expr_t* args, void* c)
if (!std::isnan(num))
{
u32 address = static_cast<u32>(num);
return std::any_of(stack.begin(), stack.end(),
[address](const auto& s) { return s.vAddress == address; });
return std::ranges::any_of(stack, [address](const auto& s) { return s.vAddress == address; });
}
const char* cstr = expr_get_str(&vec_nth(args, 0));
if (cstr != nullptr)
{
return std::any_of(stack.begin(), stack.end(),
[cstr](const auto& s) { return s.Name.find(cstr) != std::string::npos; });
return std::ranges::any_of(
stack, [cstr](const auto& s) { return s.Name.find(cstr) != std::string::npos; });
}
return 0;

View File

@ -644,6 +644,20 @@ void Jit64::fselx(UGeckoInstruction inst)
if (cpu_info.bAVX)
{
// Prefer BLENDVPD over VBLENDVPD if the latter doesn't save any
// instructions.
//
// VBLENDVPD allows separate source and destination registers, which can
// eliminate a MOVAPD/MOVSD. However, on Intel since Skylake, VBLENDVPD
// takes additional uops to execute compared to BLENDVPD (according to
// https://uops.info). On AMD and older Intel microarchitectures there is no
// difference.
if (d == c)
{
BLENDVPD(Rd, Rb);
return;
}
X64Reg src1 = XMM1;
if (Rc.IsSimpleReg())
{
@ -654,7 +668,7 @@ void Jit64::fselx(UGeckoInstruction inst)
MOVAPD(XMM1, Rc);
}
if (d == c || packed)
if (packed)
{
VBLENDVPD(Rd, src1, Rb, XMM0);
return;

View File

@ -369,10 +369,8 @@ RCForkGuard RegCache::Fork()
void RegCache::Discard(BitSet32 pregs)
{
ASSERT_MSG(
DYNA_REC,
std::none_of(m_xregs.begin(), m_xregs.end(), [](const auto& x) { return x.IsLocked(); }),
"Someone forgot to unlock a X64 reg");
ASSERT_MSG(DYNA_REC, std::ranges::none_of(m_xregs, &X64CachedReg::IsLocked),
"Someone forgot to unlock a X64 reg");
for (preg_t i : pregs)
{
@ -393,10 +391,8 @@ void RegCache::Discard(BitSet32 pregs)
void RegCache::Flush(BitSet32 pregs, IgnoreDiscardedRegisters ignore_discarded_registers)
{
ASSERT_MSG(
DYNA_REC,
std::none_of(m_xregs.begin(), m_xregs.end(), [](const auto& x) { return x.IsLocked(); }),
"Someone forgot to unlock a X64 reg");
ASSERT_MSG(DYNA_REC, std::ranges::none_of(m_xregs, &X64CachedReg::IsLocked),
"Someone forgot to unlock a X64 reg");
for (preg_t i : pregs)
{
@ -459,9 +455,8 @@ void RegCache::Commit()
bool RegCache::IsAllUnlocked() const
{
return std::none_of(m_regs.begin(), m_regs.end(), [](const auto& r) { return r.IsLocked(); }) &&
std::none_of(m_xregs.begin(), m_xregs.end(), [](const auto& x) { return x.IsLocked(); }) &&
!IsAnyConstraintActive();
return std::ranges::none_of(m_regs, &PPCCachedReg::IsLocked) &&
std::ranges::none_of(m_xregs, &X64CachedReg::IsLocked) && !IsAnyConstraintActive();
}
void RegCache::PreloadRegisters(BitSet32 to_preload)
@ -754,6 +749,5 @@ void RegCache::Realize(preg_t preg)
bool RegCache::IsAnyConstraintActive() const
{
return std::any_of(m_constraints.begin(), m_constraints.end(),
[](const auto& c) { return c.IsActive(); });
return std::ranges::any_of(m_constraints, &RCConstraint::IsActive);
}

View File

@ -355,8 +355,13 @@ protected:
Arm64Gen::ARM64Reg exit_address_after_return_reg = Arm64Gen::ARM64Reg::INVALID_REG);
void WriteBLRExit(Arm64Gen::ARM64Reg dest);
Arm64Gen::FixupBranch JumpIfCRFieldBit(int field, int bit, bool jump_if_set);
void GetCRFieldBit(int field, int bit, Arm64Gen::ARM64Reg out);
void SetCRFieldBit(int field, int bit, Arm64Gen::ARM64Reg in, bool negate = false);
void ClearCRFieldBit(int field, int bit);
void SetCRFieldBit(int field, int bit);
void FixGTBeforeSettingCRFieldBit(Arm64Gen::ARM64Reg reg);
Arm64Gen::FixupBranch JumpIfCRFieldBit(int field, int bit, bool jump_if_set);
void UpdateFPExceptionSummary(Arm64Gen::ARM64Reg fpscr);
void UpdateRoundingMode();

View File

@ -765,6 +765,14 @@ void JitArm64::rlwinmx_internal(UGeckoInstruction inst, u32 sh)
return;
}
if (mask == 0)
{
gpr.SetImmediate(a, 0);
if (inst.Rc)
ComputeRC0(0);
return;
}
gpr.BindToRegister(a, a == s);
if (sh == 0 && mask == 0xFFFFFFFF)
@ -1128,47 +1136,85 @@ void JitArm64::addzex(UGeckoInstruction inst)
int a = inst.RA, d = inst.RD;
switch (js.carryFlag)
if (gpr.IsImm(a) && (gpr.GetImm(a) == 0 || HasConstantCarry()))
{
case CarryFlag::InPPCState:
{
const bool allocate_reg = d == a;
gpr.BindToRegister(d, allocate_reg);
const u32 imm = gpr.GetImm(a);
const bool is_all_ones = imm == 0xFFFFFFFF;
switch (js.carryFlag)
{
auto WA = allocate_reg ? gpr.GetScopedReg() : Arm64GPRCache::ScopedARM64Reg(gpr.R(d));
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
CARRY_IF_NEEDED(ADD, ADDS, gpr.R(d), gpr.R(a), WA);
}
ComputeCarry();
break;
}
case CarryFlag::InHostCarry:
{
gpr.BindToRegister(d, d == a);
CARRY_IF_NEEDED(ADC, ADCS, gpr.R(d), gpr.R(a), ARM64Reg::WZR);
ComputeCarry();
break;
}
case CarryFlag::ConstantTrue:
{
gpr.BindToRegister(d, d == a);
CARRY_IF_NEEDED(ADD, ADDS, gpr.R(d), gpr.R(a), 1);
ComputeCarry();
break;
}
case CarryFlag::ConstantFalse:
{
if (d != a)
case CarryFlag::InPPCState:
{
gpr.BindToRegister(d, false);
MOV(gpr.R(d), gpr.R(a));
LDRB(IndexType::Unsigned, gpr.R(d), PPC_REG, PPCSTATE_OFF(xer_ca));
ComputeCarry(false);
break;
}
case CarryFlag::InHostCarry:
{
gpr.BindToRegister(d, false);
CSET(gpr.R(d), CCFlags::CC_CS);
ComputeCarry(false);
break;
}
case CarryFlag::ConstantTrue:
{
gpr.SetImmediate(d, imm + 1);
ComputeCarry(is_all_ones);
break;
}
case CarryFlag::ConstantFalse:
{
gpr.SetImmediate(d, imm);
ComputeCarry(false);
break;
}
}
ComputeCarry(false);
break;
}
else
{
switch (js.carryFlag)
{
case CarryFlag::InPPCState:
{
const bool allocate_reg = d == a;
gpr.BindToRegister(d, allocate_reg);
{
auto WA = allocate_reg ? gpr.GetScopedReg() : Arm64GPRCache::ScopedARM64Reg(gpr.R(d));
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
CARRY_IF_NEEDED(ADD, ADDS, gpr.R(d), gpr.R(a), WA);
}
ComputeCarry();
break;
}
case CarryFlag::InHostCarry:
{
gpr.BindToRegister(d, d == a);
CARRY_IF_NEEDED(ADC, ADCS, gpr.R(d), gpr.R(a), ARM64Reg::WZR);
ComputeCarry();
break;
}
case CarryFlag::ConstantTrue:
{
gpr.BindToRegister(d, d == a);
CARRY_IF_NEEDED(ADD, ADDS, gpr.R(d), gpr.R(a), 1);
ComputeCarry();
break;
}
case CarryFlag::ConstantFalse:
{
if (d != a)
{
gpr.BindToRegister(d, false);
MOV(gpr.R(d), gpr.R(a));
}
ComputeCarry(false);
break;
}
}
}
if (inst.Rc)
@ -1216,40 +1262,62 @@ void JitArm64::subfex(UGeckoInstruction inst)
if (gpr.IsImm(a) && (mex || gpr.IsImm(b)))
{
u32 i = gpr.GetImm(a), j = mex ? -1 : gpr.GetImm(b);
gpr.BindToRegister(d, false);
const u32 i = gpr.GetImm(a);
const u32 j = mex ? -1 : gpr.GetImm(b);
const u32 imm = ~i + j;
const bool is_zero = imm == 0;
const bool is_all_ones = imm == 0xFFFFFFFF;
switch (js.carryFlag)
{
case CarryFlag::InPPCState:
{
auto WA = gpr.GetScopedReg();
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
ADDI2R(gpr.R(d), WA, ~i + j, gpr.R(d));
gpr.BindToRegister(d, false);
ARM64Reg RD = gpr.R(d);
if (is_zero)
{
LDRB(IndexType::Unsigned, RD, PPC_REG, PPCSTATE_OFF(xer_ca));
}
else
{
auto WA = gpr.GetScopedReg();
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
ADDI2R(RD, WA, imm, RD);
}
break;
}
case CarryFlag::InHostCarry:
{
auto WA = gpr.GetScopedReg();
MOVI2R(WA, ~i + j);
ADC(gpr.R(d), WA, ARM64Reg::WZR);
gpr.BindToRegister(d, false);
ARM64Reg RD = gpr.R(d);
if (is_all_ones)
{
// RD = -1 + carry = carry ? 0 : -1
// CSETM sets the destination to -1 if the condition is true, 0
// otherwise. Hence, the condition must be carry clear.
CSETM(RD, CC_CC);
}
else
{
MOVI2R(RD, imm);
ADC(RD, RD, ARM64Reg::WZR);
}
break;
}
case CarryFlag::ConstantTrue:
{
gpr.SetImmediate(d, ~i + j + 1);
gpr.SetImmediate(d, imm + 1);
break;
}
case CarryFlag::ConstantFalse:
{
gpr.SetImmediate(d, ~i + j);
gpr.SetImmediate(d, imm);
break;
}
}
const bool must_have_carry = Interpreter::Helper_Carry(~i, j);
const bool might_have_carry = (~i + j) == 0xFFFFFFFF;
const bool might_have_carry = is_all_ones;
if (must_have_carry)
{
@ -1337,39 +1405,49 @@ void JitArm64::subfzex(UGeckoInstruction inst)
int a = inst.RA, d = inst.RD;
gpr.BindToRegister(d, d == a);
if (gpr.IsImm(a) && HasConstantCarry())
{
const u32 imm = ~gpr.GetImm(a);
const u32 carry = js.carryFlag == CarryFlag::ConstantTrue;
gpr.SetImmediate(d, imm + carry);
ComputeCarry(Interpreter::Helper_Carry(imm, carry));
}
else
{
gpr.BindToRegister(d, d == a);
switch (js.carryFlag)
{
case CarryFlag::InPPCState:
{
switch (js.carryFlag)
{
auto WA = gpr.GetScopedReg();
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
MVN(gpr.R(d), gpr.R(a));
CARRY_IF_NEEDED(ADD, ADDS, gpr.R(d), gpr.R(d), WA);
case CarryFlag::InPPCState:
{
{
auto WA = gpr.GetScopedReg();
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
MVN(gpr.R(d), gpr.R(a));
CARRY_IF_NEEDED(ADD, ADDS, gpr.R(d), gpr.R(d), WA);
}
ComputeCarry();
break;
}
case CarryFlag::InHostCarry:
{
CARRY_IF_NEEDED(SBC, SBCS, gpr.R(d), ARM64Reg::WZR, gpr.R(a));
ComputeCarry();
break;
}
case CarryFlag::ConstantTrue:
{
CARRY_IF_NEEDED(NEG, NEGS, gpr.R(d), gpr.R(a));
ComputeCarry();
break;
}
case CarryFlag::ConstantFalse:
{
MVN(gpr.R(d), gpr.R(a));
ComputeCarry(false);
break;
}
}
ComputeCarry();
break;
}
case CarryFlag::InHostCarry:
{
CARRY_IF_NEEDED(SBC, SBCS, gpr.R(d), ARM64Reg::WZR, gpr.R(a));
ComputeCarry();
break;
}
case CarryFlag::ConstantTrue:
{
CARRY_IF_NEEDED(NEG, NEGS, gpr.R(d), gpr.R(a));
ComputeCarry();
break;
}
case CarryFlag::ConstantFalse:
{
MVN(gpr.R(d), gpr.R(a));
ComputeCarry(false);
break;
}
}
if (inst.Rc)
@ -1394,23 +1472,34 @@ void JitArm64::subfic(UGeckoInstruction inst)
else
{
const bool will_read = d == a;
const bool is_zero = imm == 0;
gpr.BindToRegister(d, will_read);
// d = imm - a
ARM64Reg RD = gpr.R(d);
if (imm == -1)
{
Arm64GPRCache::ScopedARM64Reg WA(ARM64Reg::WZR);
if (!is_zero)
// d = -1 - a = ~a
MVN(RD, gpr.R(a));
// CA is always set in this case
ComputeCarry(true);
}
else
{
const bool is_zero = imm == 0;
// d = imm - a
{
WA = will_read ? gpr.GetScopedReg() : Arm64GPRCache::ScopedARM64Reg(RD);
MOVI2R(WA, imm);
Arm64GPRCache::ScopedARM64Reg WA(ARM64Reg::WZR);
if (!is_zero)
{
WA = will_read ? gpr.GetScopedReg() : Arm64GPRCache::ScopedARM64Reg(RD);
MOVI2R(WA, imm);
}
CARRY_IF_NEEDED(SUB, SUBS, RD, WA, gpr.R(a));
}
CARRY_IF_NEEDED(SUB, SUBS, RD, WA, gpr.R(a));
ComputeCarry();
}
ComputeCarry();
}
}
@ -1425,40 +1514,66 @@ void JitArm64::addex(UGeckoInstruction inst)
if (gpr.IsImm(a) && (mex || gpr.IsImm(b)))
{
u32 i = gpr.GetImm(a), j = mex ? -1 : gpr.GetImm(b);
gpr.BindToRegister(d, false);
const u32 i = gpr.GetImm(a), j = mex ? -1 : gpr.GetImm(b);
const u32 imm = i + j;
const bool is_zero = imm == 0;
const bool is_all_ones = imm == 0xFFFFFFFF;
switch (js.carryFlag)
{
case CarryFlag::InPPCState:
{
auto WA = gpr.GetScopedReg();
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
ADDI2R(gpr.R(d), WA, i + j, gpr.R(d));
gpr.BindToRegister(d, false);
ARM64Reg RD = gpr.R(d);
if (is_zero)
{
LDRB(IndexType::Unsigned, RD, PPC_REG, PPCSTATE_OFF(xer_ca));
}
else
{
auto WA = gpr.GetScopedReg();
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
ADDI2R(RD, WA, imm, RD);
}
break;
}
case CarryFlag::InHostCarry:
{
gpr.BindToRegister(d, false);
ARM64Reg RD = gpr.R(d);
MOVI2R(RD, i + j);
ADC(RD, RD, ARM64Reg::WZR);
if (is_zero)
{
// RD = 0 + carry = carry ? 1 : 0
CSET(RD, CC_CS);
}
else if (is_all_ones)
{
// RD = -1 + carry = carry ? 0 : -1
// Note that CSETM sets the destination to -1 if the condition is true,
// and 0 otherwise. Hence, the condition must be carry clear.
CSETM(RD, CC_CC);
}
else
{
MOVI2R(RD, imm);
ADC(RD, RD, ARM64Reg::WZR);
}
break;
}
case CarryFlag::ConstantTrue:
{
gpr.SetImmediate(d, i + j + 1);
gpr.SetImmediate(d, imm + 1);
break;
}
case CarryFlag::ConstantFalse:
{
gpr.SetImmediate(d, i + j);
gpr.SetImmediate(d, imm);
break;
}
}
const bool must_have_carry = Interpreter::Helper_Carry(i, j);
const bool might_have_carry = (i + j) == 0xFFFFFFFF;
const bool might_have_carry = is_all_ones;
if (must_have_carry)
{

View File

@ -817,9 +817,8 @@ void JitArm64::dcbx(UGeckoInstruction inst)
STR(IndexType::Unsigned, loop_counter, PPC_REG, PPCSTATE_OFF_SPR(SPR_CTR));
// downcount -= (WA * reg_cycle_count)
MUL(WB, WA, reg_cycle_count);
MSUB(reg_downcount, WA, reg_cycle_count, reg_downcount);
// ^ Note that this cannot overflow because it's limited by (downcount/cycle_count).
SUB(reg_downcount, reg_downcount, WB);
STR(IndexType::Unsigned, reg_downcount, PPC_REG, PPCSTATE_OFF(downcount));
SetJumpTarget(downcount_is_zero_or_negative);

View File

@ -7,6 +7,7 @@
#include "Common/CommonTypes.h"
#include "Common/Config/Config.h"
#include "Common/StringUtil.h"
#include "Common/Unreachable.h"
#include "Core/Config/SessionSettings.h"
#include "Core/ConfigManager.h"
@ -337,8 +338,12 @@ void JitArm64::ps_sel(UGeckoInstruction inst)
const auto V0Q = fpr.GetScopedReg();
const ARM64Reg V0 = reg_encoder(V0Q);
m_float_emit.FCMGE(size, V0, VA);
m_float_emit.BSL(V0, VC, VB);
m_float_emit.MOV(VD, V0);
if (d == b)
m_float_emit.BIT(VD, VC, V0);
else if (d == c)
m_float_emit.BIF(VD, VB, V0);
else
Common::Unreachable();
}
ASSERT_MSG(DYNA_REC, singles == (fpr.IsSingle(a) && fpr.IsSingle(b) && fpr.IsSingle(c)),

View File

@ -20,6 +20,144 @@
using namespace Arm64Gen;
void JitArm64::GetCRFieldBit(int field, int bit, ARM64Reg out)
{
ARM64Reg CR = gpr.CR(field);
ARM64Reg WCR = EncodeRegTo32(CR);
switch (bit)
{
case PowerPC::CR_SO_BIT: // check bit 59 set
UBFX(out, CR, PowerPC::CR_EMU_SO_BIT, 1);
break;
case PowerPC::CR_EQ_BIT: // check bits 31-0 == 0
CMP(WCR, ARM64Reg::WZR);
CSET(out, CC_EQ);
break;
case PowerPC::CR_GT_BIT: // check val > 0
CMP(CR, ARM64Reg::ZR);
CSET(out, CC_GT);
break;
case PowerPC::CR_LT_BIT: // check bit 62 set
UBFX(out, CR, PowerPC::CR_EMU_LT_BIT, 1);
break;
default:
ASSERT_MSG(DYNA_REC, false, "Invalid CR bit");
}
}
void JitArm64::SetCRFieldBit(int field, int bit, ARM64Reg in, bool negate)
{
gpr.BindCRToRegister(field, true);
ARM64Reg CR = gpr.CR(field);
if (bit != PowerPC::CR_GT_BIT)
FixGTBeforeSettingCRFieldBit(CR);
switch (bit)
{
case PowerPC::CR_SO_BIT: // set bit 59 to input
BFI(CR, in, PowerPC::CR_EMU_SO_BIT, 1);
if (negate)
EOR(CR, CR, LogicalImm(1ULL << PowerPC::CR_EMU_SO_BIT, GPRSize::B64));
break;
case PowerPC::CR_EQ_BIT: // clear low 32 bits, set bit 0 to !input
AND(CR, CR, LogicalImm(0xFFFF'FFFF'0000'0000, GPRSize::B64));
ORR(CR, CR, in);
if (!negate)
EOR(CR, CR, LogicalImm(1ULL << 0, GPRSize::B64));
break;
case PowerPC::CR_GT_BIT: // set bit 63 to !input
BFI(CR, in, 63, 1);
if (!negate)
EOR(CR, CR, LogicalImm(1ULL << 63, GPRSize::B64));
break;
case PowerPC::CR_LT_BIT: // set bit 62 to input
BFI(CR, in, PowerPC::CR_EMU_LT_BIT, 1);
if (negate)
EOR(CR, CR, LogicalImm(1ULL << PowerPC::CR_EMU_LT_BIT, GPRSize::B64));
break;
}
ORR(CR, CR, LogicalImm(1ULL << 32, GPRSize::B64));
}
void JitArm64::ClearCRFieldBit(int field, int bit)
{
gpr.BindCRToRegister(field, true);
ARM64Reg XA = gpr.CR(field);
switch (bit)
{
case PowerPC::CR_SO_BIT:
AND(XA, XA, LogicalImm(~(u64(1) << PowerPC::CR_EMU_SO_BIT), GPRSize::B64));
break;
case PowerPC::CR_EQ_BIT:
FixGTBeforeSettingCRFieldBit(XA);
ORR(XA, XA, LogicalImm(1, GPRSize::B64));
break;
case PowerPC::CR_GT_BIT:
ORR(XA, XA, LogicalImm(u64(1) << 63, GPRSize::B64));
break;
case PowerPC::CR_LT_BIT:
AND(XA, XA, LogicalImm(~(u64(1) << PowerPC::CR_EMU_LT_BIT), GPRSize::B64));
break;
}
}
void JitArm64::SetCRFieldBit(int field, int bit)
{
gpr.BindCRToRegister(field, true);
ARM64Reg XA = gpr.CR(field);
if (bit != PowerPC::CR_GT_BIT)
FixGTBeforeSettingCRFieldBit(XA);
switch (bit)
{
case PowerPC::CR_SO_BIT:
ORR(XA, XA, LogicalImm(u64(1) << PowerPC::CR_EMU_SO_BIT, GPRSize::B64));
break;
case PowerPC::CR_EQ_BIT:
AND(XA, XA, LogicalImm(0xFFFF'FFFF'0000'0000, GPRSize::B64));
break;
case PowerPC::CR_GT_BIT:
AND(XA, XA, LogicalImm(~(u64(1) << 63), GPRSize::B64));
break;
case PowerPC::CR_LT_BIT:
ORR(XA, XA, LogicalImm(u64(1) << PowerPC::CR_EMU_LT_BIT, GPRSize::B64));
break;
}
ORR(XA, XA, LogicalImm(u64(1) << 32, GPRSize::B64));
}
void JitArm64::FixGTBeforeSettingCRFieldBit(ARM64Reg reg)
{
// GT is considered unset if the internal representation is <= 0, or in other words,
// if the internal representation either has bit 63 set or has all bits set to zero.
// If all bits are zero and we set some bit that's unrelated to GT, we need to set bit 63 so GT
// doesn't accidentally become considered set. Gross but necessary; this can break actual games.
auto WA = gpr.GetScopedReg();
ARM64Reg XA = EncodeRegTo64(WA);
ORR(XA, reg, LogicalImm(1ULL << 63, GPRSize::B64));
CMP(reg, ARM64Reg::ZR);
CSEL(reg, reg, XA, CC_NEQ);
}
FixupBranch JitArm64::JumpIfCRFieldBit(int field, int bit, bool jump_if_set)
{
ARM64Reg XA = gpr.CR(field);
@ -42,19 +180,6 @@ FixupBranch JitArm64::JumpIfCRFieldBit(int field, int bit, bool jump_if_set)
}
}
void JitArm64::FixGTBeforeSettingCRFieldBit(Arm64Gen::ARM64Reg reg)
{
// GT is considered unset if the internal representation is <= 0, or in other words,
// if the internal representation either has bit 63 set or has all bits set to zero.
// If all bits are zero and we set some bit that's unrelated to GT, we need to set bit 63 so GT
// doesn't accidentally become considered set. Gross but necessary; this can break actual games.
auto WA = gpr.GetScopedReg();
ARM64Reg XA = EncodeRegTo64(WA);
ORR(XA, reg, LogicalImm(1ULL << 63, GPRSize::B64));
CMP(reg, ARM64Reg::ZR);
CSEL(reg, reg, XA, CC_NEQ);
}
void JitArm64::UpdateFPExceptionSummary(ARM64Reg fpscr)
{
auto WA = gpr.GetScopedReg();
@ -468,72 +593,47 @@ void JitArm64::crXXX(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITSystemRegistersOff);
// Special case: crclr
if (inst.CRBA == inst.CRBB && inst.CRBA == inst.CRBD && inst.SUBOP10 == 193)
if (inst.CRBA == inst.CRBB)
{
// Clear CR field bit
int field = inst.CRBD >> 2;
int bit = 3 - (inst.CRBD & 3);
gpr.BindCRToRegister(field, true);
ARM64Reg XA = gpr.CR(field);
switch (bit)
switch (inst.SUBOP10)
{
case PowerPC::CR_SO_BIT:
AND(XA, XA, LogicalImm(~(u64(1) << PowerPC::CR_EMU_SO_BIT), GPRSize::B64));
break;
case PowerPC::CR_EQ_BIT:
FixGTBeforeSettingCRFieldBit(XA);
ORR(XA, XA, LogicalImm(1, GPRSize::B64));
break;
case PowerPC::CR_GT_BIT:
ORR(XA, XA, LogicalImm(u64(1) << 63, GPRSize::B64));
break;
case PowerPC::CR_LT_BIT:
AND(XA, XA, LogicalImm(~(u64(1) << PowerPC::CR_EMU_LT_BIT), GPRSize::B64));
break;
// crclr
case 129: // crandc: A && ~B => 0
case 193: // crxor: A ^ B => 0
{
ClearCRFieldBit(inst.CRBD >> 2, 3 - (inst.CRBD & 3));
return;
}
// crset
case 289: // creqv: ~(A ^ B) => 1
case 417: // crorc: A || ~B => 1
{
SetCRFieldBit(inst.CRBD >> 2, 3 - (inst.CRBD & 3));
return;
}
case 257: // crand: A && B => A
case 449: // cror: A || B => A
{
auto WA = gpr.GetScopedReg();
ARM64Reg XA = EncodeRegTo64(WA);
GetCRFieldBit(inst.CRBA >> 2, 3 - (inst.CRBA & 3), XA);
SetCRFieldBit(inst.CRBD >> 2, 3 - (inst.CRBD & 3), XA, false);
return;
}
case 33: // crnor: ~(A || B) => ~A
case 225: // crnand: ~(A && B) => ~A
{
auto WA = gpr.GetScopedReg();
ARM64Reg XA = EncodeRegTo64(WA);
GetCRFieldBit(inst.CRBA >> 2, 3 - (inst.CRBA & 3), XA);
SetCRFieldBit(inst.CRBD >> 2, 3 - (inst.CRBD & 3), XA, true);
return;
}
}
return;
}
// Special case: crset
if (inst.CRBA == inst.CRBB && inst.CRBA == inst.CRBD && inst.SUBOP10 == 289)
{
// SetCRFieldBit
int field = inst.CRBD >> 2;
int bit = 3 - (inst.CRBD & 3);
gpr.BindCRToRegister(field, true);
ARM64Reg XA = gpr.CR(field);
if (bit != PowerPC::CR_GT_BIT)
FixGTBeforeSettingCRFieldBit(XA);
switch (bit)
{
case PowerPC::CR_SO_BIT:
ORR(XA, XA, LogicalImm(u64(1) << PowerPC::CR_EMU_SO_BIT, GPRSize::B64));
break;
case PowerPC::CR_EQ_BIT:
AND(XA, XA, LogicalImm(0xFFFF'FFFF'0000'0000, GPRSize::B64));
break;
case PowerPC::CR_GT_BIT:
AND(XA, XA, LogicalImm(~(u64(1) << 63), GPRSize::B64));
break;
case PowerPC::CR_LT_BIT:
ORR(XA, XA, LogicalImm(u64(1) << PowerPC::CR_EMU_LT_BIT, GPRSize::B64));
break;
}
ORR(XA, XA, LogicalImm(u64(1) << 32, GPRSize::B64));
return;
}
// crnor or crnand
const bool negate_result = inst.SUBOP10 == 33 || inst.SUBOP10 == 225;
auto WA = gpr.GetScopedReg();
ARM64Reg XA = EncodeRegTo64(WA);
@ -541,106 +641,42 @@ void JitArm64::crXXX(UGeckoInstruction inst)
auto WB = gpr.GetScopedReg();
ARM64Reg XB = EncodeRegTo64(WB);
// creqv or crnand or crnor
bool negateA = inst.SUBOP10 == 289 || inst.SUBOP10 == 225 || inst.SUBOP10 == 33;
// crandc or crorc or crnand or crnor
bool negateB =
inst.SUBOP10 == 129 || inst.SUBOP10 == 417 || inst.SUBOP10 == 225 || inst.SUBOP10 == 33;
// GetCRFieldBit
for (int i = 0; i < 2; i++)
{
int field = i ? inst.CRBB >> 2 : inst.CRBA >> 2;
int bit = i ? 3 - (inst.CRBB & 3) : 3 - (inst.CRBA & 3);
ARM64Reg out = i ? XB : XA;
bool negate = i ? negateB : negateA;
ARM64Reg XC = gpr.CR(field);
ARM64Reg WC = EncodeRegTo32(XC);
switch (bit)
{
case PowerPC::CR_SO_BIT: // check bit 59 set
UBFX(out, XC, PowerPC::CR_EMU_SO_BIT, 1);
if (negate)
EOR(out, out, LogicalImm(1, GPRSize::B64));
break;
case PowerPC::CR_EQ_BIT: // check bits 31-0 == 0
CMP(WC, ARM64Reg::WZR);
CSET(out, negate ? CC_NEQ : CC_EQ);
break;
case PowerPC::CR_GT_BIT: // check val > 0
CMP(XC, ARM64Reg::ZR);
CSET(out, negate ? CC_LE : CC_GT);
break;
case PowerPC::CR_LT_BIT: // check bit 62 set
UBFX(out, XC, PowerPC::CR_EMU_LT_BIT, 1);
if (negate)
EOR(out, out, LogicalImm(1, GPRSize::B64));
break;
default:
ASSERT_MSG(DYNA_REC, false, "Invalid CR bit");
}
}
GetCRFieldBit(inst.CRBA >> 2, 3 - (inst.CRBA & 3), XA);
GetCRFieldBit(inst.CRBB >> 2, 3 - (inst.CRBB & 3), XB);
// Compute combined bit
switch (inst.SUBOP10)
{
case 33: // crnor: ~(A || B) == (~A && ~B)
case 129: // crandc: A && ~B
case 225: // crnand: ~(A && B)
case 257: // crand: A && B
AND(XA, XA, XB);
break;
case 129: // crandc: A && ~B
BIC(XA, XA, XB);
break;
case 193: // crxor: A ^ B
case 289: // creqv: ~(A ^ B) = ~A ^ B
EOR(XA, XA, XB);
break;
case 225: // crnand: ~(A && B) == (~A || ~B)
case 417: // crorc: A || ~B
case 289: // creqv: ~(A ^ B) = A ^ ~B
EON(XA, XA, XB);
break;
case 33: // crnor: ~(A || B)
case 449: // cror: A || B
ORR(XA, XA, XB);
break;
case 417: // crorc: A || ~B
ORN(XA, XA, XB);
break;
}
}
// Store result bit in CRBD
int field = inst.CRBD >> 2;
int bit = 3 - (inst.CRBD & 3);
gpr.BindCRToRegister(field, true);
ARM64Reg CR = gpr.CR(field);
if (bit != PowerPC::CR_GT_BIT)
FixGTBeforeSettingCRFieldBit(CR);
switch (bit)
{
case PowerPC::CR_SO_BIT: // set bit 59 to input
BFI(CR, XA, PowerPC::CR_EMU_SO_BIT, 1);
break;
case PowerPC::CR_EQ_BIT: // clear low 32 bits, set bit 0 to !input
AND(CR, CR, LogicalImm(0xFFFF'FFFF'0000'0000, GPRSize::B64));
EOR(XA, XA, LogicalImm(1, GPRSize::B64));
ORR(CR, CR, XA);
break;
case PowerPC::CR_GT_BIT: // set bit 63 to !input
EOR(XA, XA, LogicalImm(1, GPRSize::B64));
BFI(CR, XA, 63, 1);
break;
case PowerPC::CR_LT_BIT: // set bit 62 to input
BFI(CR, XA, PowerPC::CR_EMU_LT_BIT, 1);
break;
}
ORR(CR, CR, LogicalImm(1ULL << 32, GPRSize::B64));
SetCRFieldBit(inst.CRBD >> 2, 3 - (inst.CRBD & 3), XA, negate_result);
}
void JitArm64::mfcr(UGeckoInstruction inst)

View File

@ -110,9 +110,9 @@ JitBase::~JitBase()
CPUThreadConfigCallback::RemoveConfigChangedCallback(m_registered_config_callback_id);
}
bool JitBase::DoesConfigNeedRefresh()
bool JitBase::DoesConfigNeedRefresh() const
{
return std::any_of(JIT_SETTINGS.begin(), JIT_SETTINGS.end(), [this](const auto& pair) {
return std::ranges::any_of(JIT_SETTINGS, [this](const auto& pair) {
return this->*pair.first != Config::Get(*pair.second);
});
}
@ -276,7 +276,7 @@ bool JitBase::CanMergeNextInstructions(int count) const
return true;
}
bool JitBase::ShouldHandleFPExceptionForInstruction(const PPCAnalyst::CodeOp* op)
bool JitBase::ShouldHandleFPExceptionForInstruction(const PPCAnalyst::CodeOp* op) const
{
if (jo.fp_exceptions)
return (op->opinfo->flags & FL_FLOAT_EXCEPTION) != 0;

View File

@ -167,7 +167,7 @@ protected:
static const std::array<std::pair<bool JitBase::*, const Config::Info<bool>*>, 23> JIT_SETTINGS;
bool DoesConfigNeedRefresh();
bool DoesConfigNeedRefresh() const;
void RefreshConfig();
void InitFastmemArena();
@ -178,8 +178,16 @@ protected:
void CleanUpAfterStackFault();
bool CanMergeNextInstructions(int count) const;
bool HasConstantCarry() const
{
#ifdef _M_ARM_64
return js.carryFlag == CarryFlag::ConstantTrue || js.carryFlag == CarryFlag::ConstantFalse;
#else
return false;
#endif
}
bool ShouldHandleFPExceptionForInstruction(const PPCAnalyst::CodeOp* op);
bool ShouldHandleFPExceptionForInstruction(const PPCAnalyst::CodeOp* op) const;
public:
explicit JitBase(Core::System& system);

View File

@ -191,7 +191,7 @@ static void AnalyzeFunction2(PPCSymbolDB* func_db, Common::Symbol* func)
{
u32 flags = func->flags;
bool nonleafcall = std::any_of(func->calls.begin(), func->calls.end(), [&](const auto& call) {
bool nonleafcall = std::ranges::any_of(func->calls, [&](const auto& call) {
const Common::Symbol* const called_func = func_db->GetSymbolFromAddr(call.function);
return called_func && (called_func->flags & Common::FFLAG_LEAF) == 0;
});

View File

@ -6,11 +6,11 @@
#include <algorithm>
#include <cstring>
#include <map>
#include <ranges>
#include <sstream>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include <fmt/format.h>
@ -18,6 +18,7 @@
#include "Common/IOFile.h"
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Common/Unreachable.h"
#include "Core/Core.h"
#include "Core/Debugger/DebugInterface.h"
#include "Core/PowerPC/MMU.h"
@ -251,7 +252,7 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
continue;
// Support CodeWarrior and Dolphin map
if (std::string_view{line}.ends_with(" section layout\n") || strcmp(temp, ".text") == 0 ||
if (StripWhitespace(line).ends_with(" section layout") || strcmp(temp, ".text") == 0 ||
strcmp(temp, ".init") == 0)
{
section_name = temp;
@ -310,7 +311,7 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
continue;
column_count = 2;
// Three columns format:
// Three columns format (with optional alignment):
// Starting Virtual
// address Size address
// -----------------------
@ -319,7 +320,7 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
else
iss.str("");
// Four columns format:
// Four columns format (with optional alignment):
// Starting Virtual File
// address Size address offset
// ---------------------------------
@ -327,85 +328,77 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
column_count = 4;
}
u32 address, vaddress, size, offset, alignment;
char name[512], container[512];
if (column_count == 4)
{
// sometimes there is no alignment value, and sometimes it is because it is an entry of
// something else
if (length > 37 && line[37] == ' ')
u32 address;
u32 vaddress;
u32 size = 0;
u32 offset = 0;
u32 alignment = 0;
char name[512]{};
static constexpr char ENTRY_OF_STRING[] = " (entry of ";
static constexpr std::string_view ENTRY_OF_VIEW(ENTRY_OF_STRING);
auto parse_entry_of = [](char* name) {
if (char* s1 = strstr(name, ENTRY_OF_STRING); s1 != nullptr)
{
alignment = 0;
sscanf(line, "%08x %08x %08x %08x %511s", &address, &size, &vaddress, &offset, name);
char* s = strstr(line, "(entry of ");
if (s)
char container[512];
char* ptr = s1 + ENTRY_OF_VIEW.size();
sscanf(ptr, "%511s", container);
// Skip sections, those start with a dot, e.g. (entry of .text)
if (char* s2 = strchr(container, ')'); s2 != nullptr && *container != '.')
{
sscanf(s + 10, "%511s", container);
char* s2 = (strchr(container, ')'));
if (s2 && container[0] != '.')
{
s2[0] = '\0';
strcat(container, "::");
strcat(container, name);
strcpy(name, container);
}
ptr += strlen(container);
// Preserve data after the entry part, usually it contains object names
strcpy(s1, ptr);
*s2 = '\0';
strcat(container, "::");
strcat(container, name);
strcpy(name, container);
}
}
else
{
sscanf(line, "%08x %08x %08x %08x %i %511s", &address, &size, &vaddress, &offset,
&alignment, name);
}
}
else if (column_count == 3)
};
auto was_alignment = [](const char* name) {
return *name == ' ' || (*name >= '0' && *name <= '9');
};
auto parse_alignment = [](char* name, u32* alignment) {
const std::string buffer(StripWhitespace(name));
return sscanf(buffer.c_str(), "%i %511[^\r\n]", alignment, name);
};
switch (column_count)
{
case 4:
// sometimes there is no alignment value, and sometimes it is because it is an entry of
// something else
sscanf(line, "%08x %08x %08x %08x %511[^\r\n]", &address, &size, &vaddress, &offset, name);
if (was_alignment(name))
parse_alignment(name, &alignment);
// The `else` statement was omitted to handle symbol already saved in Dolphin symbol map
// since it doesn't omit the alignment on save for such case.
parse_entry_of(name);
break;
case 3:
// some entries in the table have a function name followed by " (entry of " followed by a
// container name, followed by ")"
// instead of a space followed by a number followed by a space followed by a name
if (length > 27 && line[27] != ' ' && strstr(line, "(entry of "))
{
alignment = 0;
sscanf(line, "%08x %08x %08x %511s", &address, &size, &vaddress, name);
char* s = strstr(line, "(entry of ");
if (s)
{
sscanf(s + 10, "%511s", container);
char* s2 = (strchr(container, ')'));
if (s2 && container[0] != '.')
{
s2[0] = '\0';
strcat(container, "::");
strcat(container, name);
strcpy(name, container);
}
}
}
else
{
sscanf(line, "%08x %08x %08x %i %511s", &address, &size, &vaddress, &alignment, name);
}
}
else if (column_count == 2)
{
sscanf(line, "%08x %511s", &address, name);
sscanf(line, "%08x %08x %08x %511[^\r\n]", &address, &size, &vaddress, name);
if (was_alignment(name))
parse_alignment(name, &alignment);
// The `else` statement was omitted to handle symbol already saved in Dolphin symbol map
// since it doesn't omit the alignment on save for such case.
parse_entry_of(name);
break;
case 2:
sscanf(line, "%08x %511[^\r\n]", &address, name);
vaddress = address;
size = 0;
}
else
{
break;
default:
// Should never happen
Common::Unreachable();
break;
}
const char* namepos = strstr(line, name);
if (namepos != nullptr) // would be odd if not :P
strcpy(name, namepos);
name[strlen(name) - 1] = 0;
if (name[strlen(name) - 1] == '\r')
name[strlen(name) - 1] = 0;
// Split the current name string into separate parts, and get the object name
// if it exists.
const std::vector<std::string> parts = SplitString(name, '\t');
const std::string name_string(StripWhitespace(parts[0]));
const std::string name_string(StripWhitespace(parts.size() > 0 ? parts[0] : name));
const std::string object_filename_string =
parts.size() > 1 ? std::string(StripWhitespace(parts[1])) : "";
@ -466,42 +459,38 @@ bool PPCSymbolDB::SaveSymbolMap(const std::string& filename) const
if (!f)
return false;
std::vector<const Common::Symbol*> function_symbols;
std::vector<const Common::Symbol*> data_symbols;
for (const auto& function : m_functions)
{
const Common::Symbol& symbol = function.second;
if (symbol.type == Common::Symbol::Type::Function)
function_symbols.push_back(&symbol);
else
data_symbols.push_back(&symbol);
}
// Write .text section
auto function_symbols =
m_functions |
std::views::filter([](auto f) { return f.second.type == Common::Symbol::Type::Function; }) |
std::views::transform([](auto f) { return f.second; });
f.WriteString(".text section layout\n");
for (const auto& symbol : function_symbols)
{
// Write symbol address, size, virtual address, alignment, name
std::string line = fmt::format("{0:08x} {1:06x} {2:08x} {3} {4}", symbol->address, symbol->size,
symbol->address, 0, symbol->name);
std::string line = fmt::format("{:08x} {:06x} {:08x} {} {}", symbol.address, symbol.size,
symbol.address, 0, symbol.name);
// Also write the object name if it exists
if (!symbol->object_name.empty())
line += fmt::format(" \t{0}", symbol->object_name);
if (!symbol.object_name.empty())
line += fmt::format(" \t{0}", symbol.object_name);
line += "\n";
f.WriteString(line);
}
// Write .data section
auto data_symbols =
m_functions |
std::views::filter([](auto f) { return f.second.type == Common::Symbol::Type::Data; }) |
std::views::transform([](auto f) { return f.second; });
f.WriteString("\n.data section layout\n");
for (const auto& symbol : data_symbols)
{
// Write symbol address, size, virtual address, alignment, name
std::string line = fmt::format("{0:08x} {1:06x} {2:08x} {3} {4}", symbol->address, symbol->size,
symbol->address, 0, symbol->name);
std::string line = fmt::format("{:08x} {:06x} {:08x} {} {}", symbol.address, symbol.size,
symbol.address, 0, symbol.name);
// Also write the object name if it exists
if (!symbol->object_name.empty())
line += fmt::format(" \t{0}", symbol->object_name);
if (!symbol.object_name.empty())
line += fmt::format(" \t{0}", symbol.object_name);
line += "\n";
f.WriteString(line);
}

View File

@ -24,6 +24,7 @@
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
#include "Common/Contains.h"
#include "Common/Event.h"
#include "Common/FileUtil.h"
#include "Common/IOFile.h"
@ -138,7 +139,7 @@ void EnableCompression(bool compression)
s_use_compression = compression;
}
static void DoState(Core::System& system, PointerWrap& p)
static void DoState(Core::System& system, PointerWrap& p, bool delta)
{
bool is_wii = system.IsWii() || system.IsMIOS();
const bool is_wii_currently = is_wii;
@ -187,7 +188,7 @@ static void DoState(Core::System& system, PointerWrap& p)
p.DoMarker("CoreTiming");
// HW needs to be restored before PowerPC because the data cache might need to be flushed.
HW::DoState(system, p);
HW::DoState(system, p, delta);
p.DoMarker("HW");
system.GetPowerPC().DoState(p);
@ -223,7 +224,7 @@ void LoadFromBuffer(Core::System& system, std::vector<u8>& buffer)
[&] {
u8* ptr = buffer.data();
PointerWrap p(&ptr, buffer.size(), PointerWrap::Mode::Read);
DoState(system, p);
DoState(system, p, false);
},
true);
}
@ -236,13 +237,13 @@ void SaveToBuffer(Core::System& system, std::vector<u8>& buffer)
u8* ptr = nullptr;
PointerWrap p_measure(&ptr, 0, PointerWrap::Mode::Measure);
DoState(system, p_measure);
DoState(system, p_measure, false);
const size_t buffer_size = reinterpret_cast<size_t>(ptr);
buffer.resize(buffer_size);
ptr = buffer.data();
PointerWrap p(&ptr, buffer_size, PointerWrap::Mode::Write);
DoState(system, p);
DoState(system, p, false);
},
true);
}
@ -261,9 +262,7 @@ static int GetEmptySlot(const std::vector<SlotWithTimestamp>& used_slots)
{
for (int i = 1; i <= (int)NUM_STATES; i++)
{
const auto it = std::find_if(used_slots.begin(), used_slots.end(),
[i](const SlotWithTimestamp& slot) { return slot.slot == i; });
if (it == used_slots.end())
if (!Common::Contains(used_slots, i, &SlotWithTimestamp::slot))
return i;
}
return -1;
@ -487,7 +486,7 @@ void SaveAs(Core::System& system, const std::string& filename, bool wait)
// Measure the size of the buffer.
u8* ptr = nullptr;
PointerWrap p_measure(&ptr, 0, PointerWrap::Mode::Measure);
DoState(system, p_measure);
DoState(system, p_measure, false);
const size_t buffer_size = reinterpret_cast<size_t>(ptr);
// Then actually do the write.
@ -495,7 +494,7 @@ void SaveAs(Core::System& system, const std::string& filename, bool wait)
current_buffer.resize(buffer_size);
ptr = current_buffer.data();
PointerWrap p(&ptr, buffer_size, PointerWrap::Mode::Write);
DoState(system, p);
DoState(system, p, true);
if (p.IsWriteMode())
{
@ -901,7 +900,7 @@ void LoadAs(Core::System& system, const std::string& filename)
{
u8* ptr = buffer.data();
PointerWrap p(&ptr, buffer.size(), PointerWrap::Mode::Read);
DoState(system, p);
DoState(system, p, true);
loaded = true;
loadedSuccessfully = p.IsReadMode();
}

View File

@ -10,6 +10,7 @@
#include <array>
#include <fmt/format.h>
#include "Common/Contains.h"
#include "Common/IniFile.h"
#include "Common/StringUtil.h"
@ -86,10 +87,7 @@ static void LoadPatchSection(const Common::IniFile& ini)
static bool IsWC24Channel()
{
const auto& sconfig = SConfig::GetInstance();
const auto found =
std::find(s_wc24_channels.begin(), s_wc24_channels.end(), sconfig.GetTitleID());
return found != s_wc24_channels.end();
return Common::Contains(s_wc24_channels, sconfig.GetTitleID());
}
static void LoadPatches()

View File

@ -21,6 +21,7 @@
#include "Common/Align.h"
#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "Common/Contains.h"
#include "Common/EnumUtils.h"
#include "Common/FileUtil.h"
#include "Common/HttpRequest.h"
@ -223,15 +224,14 @@ bool IsTitleInstalled(u64 title_id)
// Since this isn't IOS and we only need a simple way to figure out if a title is installed,
// we make the (reasonable) assumption that having more than just the TMD in the content
// directory means that the title is installed.
return std::any_of(entries->begin(), entries->end(),
[](const std::string& file) { return file != "title.tmd"; });
return std::ranges::any_of(*entries, [](const std::string& file) { return file != "title.tmd"; });
}
bool IsTMDImported(IOS::HLE::FS::FileSystem& fs, u64 title_id)
{
const auto entries = fs.ReadDirectory(0, 0, Common::GetTitleContentPath(title_id));
return entries && std::any_of(entries->begin(), entries->end(),
[](const std::string& file) { return file == "title.tmd"; });
return entries &&
std::ranges::any_of(*entries, [](const std::string& file) { return file == "title.tmd"; });
}
IOS::ES::TMDReader FindBackupTMD(IOS::HLE::FS::FileSystem& fs, u64 title_id)
@ -467,12 +467,7 @@ OnlineSystemUpdater::Response OnlineSystemUpdater::GetSystemTitles()
// but the backing data CDN is still active and accessible from other URLs. We take advantage
// of this by hosting our own NetUpdateSOAP endpoint which serves the correct list of titles to
// install along with URLs for the Wii U CDN.
#ifdef ANDROID
// HTTPS is unsupported on Android (https://bugs.dolphin-emu.org/issues/11772).
base_url = "http://fakenus.dolphin-emu.org";
#else
base_url = "https://fakenus.dolphin-emu.org";
#endif
}
const std::string url = fmt::format("{}/nus/services/NetUpdateSOAP", base_url);
@ -591,10 +586,8 @@ UpdateResult OnlineSystemUpdater::InstallTitleFromNUS(const std::string& prefix_
const UpdateResult import_result = [&]() {
for (const IOS::ES::Content& content : tmd.first.GetContents())
{
const bool is_already_installed = std::find_if(stored_contents.begin(), stored_contents.end(),
[&content](const auto& stored_content) {
return stored_content.id == content.id;
}) != stored_contents.end();
const bool is_already_installed =
Common::Contains(stored_contents, content.id, &IOS::ES::Content::id);
// Do skip what is already installed on the NAND.
if (is_already_installed)
@ -947,8 +940,8 @@ static NANDCheckResult CheckNAND(IOS::HLE::Kernel& ios, bool repair)
}
const auto installed_contents = es.GetStoredContentsFromTMD(tmd);
const bool is_installed = std::any_of(installed_contents.begin(), installed_contents.end(),
[](const auto& content) { return !content.IsShared(); });
const bool is_installed = std::ranges::any_of(
installed_contents, [](const auto& content) { return !content.IsShared(); });
if (is_installed && installed_contents != tmd.GetContents() &&
(tmd.GetTitleFlags() & IOS::ES::TitleFlags::TITLE_TYPE_DATA) == 0)

View File

@ -13,6 +13,7 @@
#include "Common/CommonTypes.h"
#include "Common/MathUtil.h"
#include "Common/StringUtil.h"
#include "DiscIO/Blob.h"
#include "DiscIO/Filesystem.h"
#include "DiscIO/Volume.h"
@ -39,8 +40,7 @@ std::string NameForPartitionType(u32 partition_type, bool include_prefix)
static_cast<char>((partition_type >> 16) & 0xFF),
static_cast<char>((partition_type >> 8) & 0xFF),
static_cast<char>(partition_type & 0xFF)};
if (std::all_of(type_as_game_id.cbegin(), type_as_game_id.cend(),
[](char c) { return std::isalnum(c, std::locale::classic()); }))
if (std::ranges::all_of(type_as_game_id, Common::IsAlnum))
{
return include_prefix ? "P-" + type_as_game_id : type_as_game_id;
}

View File

@ -240,11 +240,9 @@ bool NANDImporter::ExtractCertificates()
for (const PEMCertificate& certificate : certificates)
{
const auto search_result =
std::search(content_bytes.begin(), content_bytes.end(), certificate.search_bytes.begin(),
certificate.search_bytes.end());
const auto search_result = std::ranges::search(content_bytes, certificate.search_bytes);
if (search_result == content_bytes.end())
if (search_result.empty())
{
ERROR_LOG_FMT(DISCIO, "ExtractCertificates: Could not find offset for certficate '{}'",
certificate.filename);
@ -252,7 +250,8 @@ bool NANDImporter::ExtractCertificates()
}
const std::string pem_file_path = m_nand_root + std::string(certificate.filename);
const ptrdiff_t certificate_offset = std::distance(content_bytes.begin(), search_result);
const ptrdiff_t certificate_offset =
std::distance(content_bytes.begin(), search_result.begin());
constexpr int min_offset = 2;
if (certificate_offset < min_offset)
{

View File

@ -104,7 +104,7 @@ FileDataLoaderHostFS::MakeAbsoluteFromRelative(std::string_view external_relativ
result.pop_back();
result.pop_back();
}
else if (std::all_of(element.begin(), element.end(), [](char c) { return c == '.'; }))
else if (std::ranges::all_of(element, [](char c) { return c == '.'; }))
{
// This is a triple, quadruple, etc. dot.
// Some file systems treat this as several 'up' path traversals, but Riivolution does not.

View File

@ -21,6 +21,7 @@
#include "Common/CPUDetect.h"
#include "Common/CommonPaths.h"
#include "Common/CommonTypes.h"
#include "Common/Contains.h"
#include "Common/Crypto/SHA1.h"
#include "Common/FileUtil.h"
#include "Common/Hash.h"
@ -143,11 +144,8 @@ RedumpVerifier::DownloadStatus RedumpVerifier::DownloadDatfile(const std::string
if (File::Exists(output_path))
return DownloadStatus::FailButOldCacheAvailable;
const std::string system_not_available_message = "System \"" + system + "\" doesn't exist.";
const bool system_not_available_match =
result->end() != std::search(result->begin(), result->end(),
system_not_available_message.begin(),
system_not_available_message.end());
Common::ContainsSubrange(*result, "System \"" + system + "\" doesn't exist.");
return system_not_available_match ? DownloadStatus::SystemNotAvailable : DownloadStatus::Fail;
}
@ -453,21 +451,18 @@ std::vector<Partition> VolumeVerifier::CheckPartitions()
types.emplace_back(*type);
}
if (std::find(types.cbegin(), types.cend(), PARTITION_UPDATE) == types.cend())
if (!Common::Contains(types, PARTITION_UPDATE))
AddProblem(Severity::Low, Common::GetStringT("The update partition is missing."));
const bool has_data_partition =
std::find(types.cbegin(), types.cend(), PARTITION_DATA) != types.cend();
const bool has_data_partition = Common::Contains(types, PARTITION_DATA);
if (!m_is_datel && !has_data_partition)
AddProblem(Severity::High, Common::GetStringT("The data partition is missing."));
const bool has_channel_partition =
std::find(types.cbegin(), types.cend(), PARTITION_CHANNEL) != types.cend();
const bool has_channel_partition = Common::Contains(types, PARTITION_CHANNEL);
if (ShouldHaveChannelPartition() && !has_channel_partition)
AddProblem(Severity::Medium, Common::GetStringT("The channel partition is missing."));
const bool has_install_partition =
std::find(types.cbegin(), types.cend(), PARTITION_INSTALL) != types.cend();
const bool has_install_partition = Common::Contains(types, PARTITION_INSTALL);
if (ShouldHaveInstallPartition() && !has_install_partition)
AddProblem(Severity::High, Common::GetStringT("The install partition is missing."));
@ -729,16 +724,15 @@ bool VolumeVerifier::ShouldHaveInstallPartition() const
static constexpr std::array<std::string_view, 4> dragon_quest_x = {"S4MJGD", "S4SJGD", "S6TJGD",
"SDQJGD"};
const std::string& game_id = m_volume.GetGameID();
return std::any_of(dragon_quest_x.cbegin(), dragon_quest_x.cend(),
[&game_id](std::string_view x) { return x == game_id; });
return std::ranges::any_of(dragon_quest_x,
[&game_id](std::string_view x) { return x == game_id; });
}
bool VolumeVerifier::ShouldHaveMasterpiecePartitions() const
{
static constexpr std::array<std::string_view, 4> ssbb = {"RSBE01", "RSBJ01", "RSBK01", "RSBP01"};
const std::string& game_id = m_volume.GetGameID();
return std::any_of(ssbb.cbegin(), ssbb.cend(),
[&game_id](std::string_view x) { return x == game_id; });
return std::ranges::any_of(ssbb, [&game_id](std::string_view x) { return x == game_id; });
}
bool VolumeVerifier::ShouldBeDualLayer() const
@ -1039,7 +1033,7 @@ void VolumeVerifier::CheckSuperPaperMario()
if (!m_volume.Read(offset, length, data.data(), partition))
return;
if (std::any_of(data.cbegin(), data.cend(), [](u8 x) { return x != 0; }))
if (std::ranges::any_of(data, [](u8 x) { return x != 0; }))
{
AddProblem(Severity::High,
Common::GetStringT("Some padding data that should be zero is not zero. "

View File

@ -176,7 +176,7 @@ IOS::ES::TicketReader VolumeWAD::GetTicketWithFixedCommonKey() const
return m_ticket;
const std::vector<u8> sig = m_ticket.GetSignatureData();
if (!std::all_of(sig.cbegin(), sig.cend(), [](u8 a) { return a == 0; }))
if (!std::ranges::all_of(sig, [](u8 a) { return a == 0; }))
{
// This does not look like a typical "invalid common key index" ticket, so let's assume
// the index is correct. This saves some time when reading properly signed titles.

View File

@ -1126,7 +1126,7 @@ bool WIARVZFileReader<RVZ>::TryReuse(std::map<ReuseID, GroupEntry>* reusable_gro
static bool AllAre(const std::vector<u8>& data, u8 x)
{
return std::all_of(data.begin(), data.end(), [x](u8 y) { return x == y; });
return std::ranges::all_of(data, [x](u8 y) { return x == y; });
}
static bool AllAre(const u8* begin, const u8* end, u8 x)
@ -1379,8 +1379,8 @@ WIARVZFileReader<RVZ>::ProcessAndCompress(CompressThreadState* state, CompressPa
}
}
if (!std::all_of(output_entries.begin(), output_entries.end(),
[](const OutputParametersEntry& entry) { return entry.reused_group; }))
if (!std::ranges::all_of(output_entries,
[](const auto& entry) { return entry.reused_group.has_value(); }))
{
const u64 number_of_exception_lists =
chunks_per_wii_group == 1 ? exception_lists_per_chunk : chunks;

View File

@ -38,6 +38,7 @@
<ClInclude Include="Common\Config\ConfigInfo.h" />
<ClInclude Include="Common\Config\Enums.h" />
<ClInclude Include="Common\Config\Layer.h" />
<ClInclude Include="Common\Contains.h" />
<ClInclude Include="Common\CPUDetect.h" />
<ClInclude Include="Common\Crypto\AES.h" />
<ClInclude Include="Common\Crypto\bn.h" />

View File

@ -52,6 +52,7 @@ add_executable(dolphin-emu
Config/ConfigControls/ConfigBool.h
Config/ConfigControls/ConfigChoice.cpp
Config/ConfigControls/ConfigChoice.h
Config/ConfigControls/ConfigControl.h
Config/ConfigControls/ConfigInteger.cpp
Config/ConfigControls/ConfigInteger.h
Config/ConfigControls/ConfigRadio.cpp
@ -572,12 +573,11 @@ endif()
if(APPLE)
include(BundleUtilities)
set(BUNDLE_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/DolphinQt.app)
# Ask for an application bundle.
set_target_properties(dolphin-emu PROPERTIES
MACOSX_BUNDLE true
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/Info.plist"
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in"
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ""
OUTPUT_NAME DolphinQt
)
@ -614,6 +614,9 @@ if(APPLE)
source_group("Resources" FILES "${CMAKE_SOURCE_DIR}/Data/${res}")
endforeach()
include(DolphinInjectVersionInfo)
dolphin_inject_version_info(dolphin-emu)
# Copy MoltenVK into the bundle
if(ENABLE_VULKAN)
if(USE_BUNDLED_MOLTENVK)
@ -648,7 +651,7 @@ if(APPLE)
COMMAND "${CMAKE_SOURCE_DIR}/Tools/mac-codesign.sh"
"-e" "${CMAKE_CURRENT_SOURCE_DIR}/DolphinEmu$<$<CONFIG:Debug>:Debug>.entitlements"
"${MACOS_CODE_SIGNING_IDENTITY}"
"${BUNDLE_PATH}"
"$<TARGET_BUNDLE_DIR:dolphin-emu>"
)
endif()
else()

View File

@ -3,31 +3,28 @@
#include "DolphinQt/Config/ConfigControls/ConfigBool.h"
#include <QEvent>
#include <QFont>
#include <QSignalBlocker>
#include "Common/Config/Config.h"
#include "DolphinQt/Settings.h"
ConfigBool::ConfigBool(const QString& label, const Config::Info<bool>& setting, bool reverse)
: ToolTipCheckBox(label), m_setting(setting), m_reverse(reverse)
: ConfigBool(label, setting, nullptr, reverse)
{
}
ConfigBool::ConfigBool(const QString& label, const Config::Info<bool>& setting,
Config::Layer* layer, bool reverse)
: ConfigControl(label, setting.GetLocation(), layer), m_setting(setting), m_reverse(reverse)
{
setChecked(ReadValue(setting) ^ reverse);
connect(this, &QCheckBox::toggled, this, &ConfigBool::Update);
setChecked(Config::Get(m_setting) ^ reverse);
connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] {
QFont bf = font();
bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base);
setFont(bf);
const QSignalBlocker blocker(this);
setChecked(Config::Get(m_setting) ^ m_reverse);
});
}
void ConfigBool::Update()
{
Config::SetBaseOrCurrent(m_setting, static_cast<bool>(isChecked() ^ m_reverse));
const bool value = static_cast<bool>(isChecked() ^ m_reverse);
SaveValue(m_setting, value);
}
void ConfigBool::OnConfigChanged()
{
setChecked(ReadValue(m_setting) ^ m_reverse);
}

View File

@ -3,23 +3,25 @@
#pragma once
#include "DolphinQt/Config/ConfigControls/ConfigControl.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipCheckBox.h"
namespace Config
{
template <typename T>
class Info;
}
#include "Common/Config/ConfigInfo.h"
class ConfigBool : public ToolTipCheckBox
class ConfigBool final : public ConfigControl<ToolTipCheckBox>
{
Q_OBJECT
public:
ConfigBool(const QString& label, const Config::Info<bool>& setting, bool reverse = false);
ConfigBool(const QString& label, const Config::Info<bool>& setting, Config::Layer* layer,
bool reverse = false);
protected:
void OnConfigChanged() override;
private:
void Update();
const Config::Info<bool>& m_setting;
const Config::Info<bool> m_setting;
bool m_reverse;
};

View File

@ -5,85 +5,168 @@
#include <QSignalBlocker>
#include "Common/Config/Config.h"
#include "DolphinQt/Settings.h"
ConfigChoice::ConfigChoice(const QStringList& options, const Config::Info<int>& setting)
: m_setting(setting)
ConfigChoice::ConfigChoice(const QStringList& options, const Config::Info<int>& setting,
Config::Layer* layer)
: ConfigControl(setting.GetLocation(), layer), m_setting(setting)
{
addItems(options);
setCurrentIndex(ReadValue(setting));
connect(this, &QComboBox::currentIndexChanged, this, &ConfigChoice::Update);
setCurrentIndex(Config::Get(m_setting));
connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] {
QFont bf = font();
bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base);
setFont(bf);
const QSignalBlocker blocker(this);
setCurrentIndex(Config::Get(m_setting));
});
}
void ConfigChoice::Update(int choice)
{
Config::SetBaseOrCurrent(m_setting, choice);
SaveValue(m_setting, choice);
}
void ConfigChoice::OnConfigChanged()
{
setCurrentIndex(ReadValue(m_setting));
}
ConfigStringChoice::ConfigStringChoice(const std::vector<std::string>& options,
const Config::Info<std::string>& setting)
: m_setting(setting), m_text_is_data(true)
const Config::Info<std::string>& setting,
Config::Layer* layer)
: ConfigControl(setting.GetLocation(), layer), m_setting(setting), m_text_is_data(true)
{
for (const auto& op : options)
addItem(QString::fromStdString(op));
Connect();
Load();
connect(this, &QComboBox::currentIndexChanged, this, &ConfigStringChoice::Update);
}
ConfigStringChoice::ConfigStringChoice(const std::vector<std::pair<QString, QString>>& options,
const Config::Info<std::string>& setting)
: m_setting(setting), m_text_is_data(false)
const Config::Info<std::string>& setting,
Config::Layer* layer)
: ConfigControl(setting.GetLocation(), layer), m_setting(setting), m_text_is_data(false)
{
for (const auto& [option_text, option_data] : options)
addItem(option_text, option_data);
Connect();
Load();
}
void ConfigStringChoice::Connect()
{
const auto on_config_changed = [this]() {
QFont bf = font();
bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base);
setFont(bf);
Load();
};
connect(&Settings::Instance(), &Settings::ConfigChanged, this, on_config_changed);
connect(this, &QComboBox::currentIndexChanged, this, &ConfigStringChoice::Update);
Load();
}
void ConfigStringChoice::Update(int index)
{
if (m_text_is_data)
{
Config::SetBaseOrCurrent(m_setting, itemText(index).toStdString());
}
SaveValue(m_setting, itemText(index).toStdString());
else
{
Config::SetBaseOrCurrent(m_setting, itemData(index).toString().toStdString());
}
SaveValue(m_setting, itemData(index).toString().toStdString());
}
void ConfigStringChoice::Load()
{
const QString setting_value = QString::fromStdString(Config::Get(m_setting));
const QString setting_value = QString::fromStdString(ReadValue(m_setting));
const int index = m_text_is_data ? findText(setting_value) : findData(setting_value);
// This can be called publicly.
const QSignalBlocker block(this);
setCurrentIndex(index);
}
void ConfigStringChoice::OnConfigChanged()
{
Load();
}
ConfigComplexChoice::ConfigComplexChoice(const InfoVariant setting1, const InfoVariant setting2,
Config::Layer* layer)
: m_layer(layer), m_setting1(setting1), m_setting2(setting2)
{
connect(&Settings::Instance(), &Settings::ConfigChanged, this, &ConfigComplexChoice::Refresh);
connect(this, &QComboBox::currentIndexChanged, this, &ConfigComplexChoice::SaveValue);
}
void ConfigComplexChoice::Refresh()
{
auto& location = GetLocation();
QFont bf = font();
if (m_layer != nullptr)
{
bf.setBold(m_layer->Exists(location.first) || m_layer->Exists(location.second));
}
else
{
bf.setBold(Config::GetActiveLayerForConfig(location.first) != Config::LayerType::Base ||
Config::GetActiveLayerForConfig(location.second) != Config::LayerType::Base);
}
setFont(bf);
UpdateComboIndex();
}
void ConfigComplexChoice::Add(const QString& name, const OptionVariant option1,
const OptionVariant option2)
{
const QSignalBlocker blocker(this);
addItem(name);
m_options.push_back(std::make_pair(option1, option2));
}
void ConfigComplexChoice::Reset()
{
clear();
m_options.clear();
}
void ConfigComplexChoice::SaveValue(int choice)
{
auto Set = [this](auto& setting, auto& value) {
if (m_layer != nullptr)
{
m_layer->Set(setting.GetLocation(), value);
Config::OnConfigChanged();
return;
}
Config::SetBaseOrCurrent(setting, value);
};
std::visit(Set, m_setting1, m_options[choice].first);
std::visit(Set, m_setting2, m_options[choice].second);
}
void ConfigComplexChoice::UpdateComboIndex()
{
auto Get = [this](auto& setting) {
if (m_layer != nullptr)
return static_cast<OptionVariant>(m_layer->Get(setting));
return static_cast<OptionVariant>(Config::Get(setting));
};
std::pair<OptionVariant, OptionVariant> values =
std::make_pair(std::visit(Get, m_setting1), std::visit(Get, m_setting2));
auto it = std::find(m_options.begin(), m_options.end(), values);
int index = static_cast<int>(std::distance(m_options.begin(), it));
const QSignalBlocker blocker(this);
setCurrentIndex(index);
}
const std::pair<Config::Location, Config::Location> ConfigComplexChoice::GetLocation() const
{
auto visit = [](auto& v) { return v.GetLocation(); };
return {std::visit(visit, m_setting1), std::visit(visit, m_setting2)};
}
void ConfigComplexChoice::mousePressEvent(QMouseEvent* event)
{
if (event->button() == Qt::RightButton && m_layer != nullptr)
{
auto& location = GetLocation();
m_layer->DeleteKey(location.first);
m_layer->DeleteKey(location.second);
Config::OnConfigChanged();
}
else
{
QComboBox::mousePressEvent(event);
}
};

Some files were not shown because too many files have changed in this diff Show More