Compare commits

..

64 Commits

Author SHA1 Message Date
cristian64 dc88326d60
Merge 788f140936 into 2c83a256ae 2025-01-17 14:44:27 +05: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
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
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
Jordan Woyak 0938fca6e3 Core/VideoCommon: Fix some weird (!eof) logic. 2024-12-30 20:13:59 -06: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
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
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
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
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
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
JosJuice 2fdeb85f0e Android: Replace deprecated setSystemUiVisibility
No functional change.
2024-06-15 15:02:50 +02:00
62 changed files with 1233 additions and 1937 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() endif()
endfunction() endfunction()
function(dolphin_optional_system_library library) function(dolphin_optional_system_library out_use_system library)
string(TOUPPER ${library} upperlib) 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.") 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("${USE_SYSTEM_${upperlib}}" STREQUAL "")
if(APPROVED_VENDORED_DEPENDENCIES) if(APPROVED_VENDORED_DEPENDENCIES)
string(TOLOWER ${library} lowerlib) string(TOLOWER ${library} lowerlib)
if(lowerlib IN_LIST APPROVED_VENDORED_DEPENDENCIES) if(lowerlib IN_LIST APPROVED_VENDORED_DEPENDENCIES)
set(RESOLVED_USE_SYSTEM_${upperlib} AUTO PARENT_SCOPE) set(${out_use_system} AUTO PARENT_SCOPE)
else() else()
set(RESOLVED_USE_SYSTEM_${upperlib} ON PARENT_SCOPE) set(${out_use_system} ON PARENT_SCOPE)
endif() endif()
else() else()
set(RESOLVED_USE_SYSTEM_${upperlib} ${USE_SYSTEM_LIBS} PARENT_SCOPE) set(${out_use_system} ${USE_SYSTEM_LIBS} PARENT_SCOPE)
endif() endif()
else() else()
set(RESOLVED_USE_SYSTEM_${upperlib} ${USE_SYSTEM_${upperlib}} PARENT_SCOPE) set(${out_use_system} ${USE_SYSTEM_${upperlib}} PARENT_SCOPE)
endif() endif()
endfunction() endfunction()
function(dolphin_add_bundled_library library bundled_path) function(dolphin_add_bundled_library library use_system bundled_path)
string(TOUPPER ${library} upperlib) if (${use_system} STREQUAL "AUTO")
if (${RESOLVED_USE_SYSTEM_${upperlib}} STREQUAL "AUTO")
message(STATUS "No system ${library} was found. Using static ${library} from Externals.") message(STATUS "No system ${library} was found. Using static ${library} from Externals.")
else() else()
message(STATUS "Using static ${library} from Externals") message(STATUS "Using static ${library} from Externals")
@ -52,9 +51,9 @@ function(dolphin_add_bundled_library library bundled_path)
endfunction() endfunction()
function(dolphin_find_optional_system_library library bundled_path) 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) string(TOUPPER ${library} upperlib)
if(RESOLVED_USE_SYSTEM_${upperlib}) if(use_system)
find_package(${library} ${ARGN}) find_package(${library} ${ARGN})
# Yay for cmake packages being inconsistent # Yay for cmake packages being inconsistent
if(DEFINED ${library}_FOUND) if(DEFINED ${library}_FOUND)
@ -62,7 +61,7 @@ function(dolphin_find_optional_system_library library bundled_path)
else() else()
set(prefix ${upperlib}) set(prefix ${upperlib})
endif() 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.") message(FATAL_ERROR "No system ${library} was found. Please install it or set USE_SYSTEM_${upperlib} to AUTO or OFF.")
endif() endif()
endif() endif()
@ -70,17 +69,17 @@ function(dolphin_find_optional_system_library library bundled_path)
message(STATUS "Using system ${library}") message(STATUS "Using system ${library}")
set(${prefix}_TYPE "System" PARENT_SCOPE) set(${prefix}_TYPE "System" PARENT_SCOPE)
else() else()
dolphin_add_bundled_library(${library} ${bundled_path}) dolphin_add_bundled_library(${library} ${use_system} ${bundled_path})
set(${prefix}_TYPE "Bundled" PARENT_SCOPE) set(${prefix}_TYPE "Bundled" PARENT_SCOPE)
endif() endif()
endfunction() endfunction()
function(dolphin_find_optional_system_library_pkgconfig library search alias bundled_path) 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) string(TOUPPER ${library} upperlib)
if(RESOLVED_USE_SYSTEM_${upperlib}) if(use_system)
pkg_search_module(${library} ${search} ${ARGN} IMPORTED_TARGET) 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.") message(FATAL_ERROR "No system ${library} was found. Please install it or set USE_SYSTEM_${upperlib} to AUTO or OFF.")
endif() endif()
endif() endif()
@ -89,7 +88,7 @@ function(dolphin_find_optional_system_library_pkgconfig library search alias bun
dolphin_alias_library(${alias} PkgConfig::${library}) dolphin_alias_library(${alias} PkgConfig::${library})
set(${library}_TYPE "System" PARENT_SCOPE) set(${library}_TYPE "System" PARENT_SCOPE)
else() else()
dolphin_add_bundled_library(${library} ${bundled_path}) dolphin_add_bundled_library(${library} ${use_system} ${bundled_path})
set(${library}_TYPE "Bundled" PARENT_SCOPE) set(${library}_TYPE "Bundled" PARENT_SCOPE)
endif() endif()
endfunction() endfunction()

View File

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

View File

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

View File

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

View File

@ -1,62 +1,53 @@
include_directories(${CMAKE_CURRENT_SOURCE_DIR}) set(BUILD_CURL_EXE OFF)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/curl/include) set(BUILD_EXAMPLES OFF)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/curl/lib) 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) set(HTTP_ONLY ON)
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(use_core_foundation ON)
find_library(SYSTEMCONFIGURATION_FRAMEWORK "SystemConfiguration") set(CURL_USE_LIBPSL OFF)
if(NOT SYSTEMCONFIGURATION_FRAMEWORK) set(CURL_USE_LIBSSH2 OFF)
message(FATAL_ERROR "SystemConfiguration framework not found") set(CURL_ZLIB OFF CACHE BOOL "" FORCE)
endif() 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() endif()
file(GLOB SRCS curl/lib/*.c curl/lib/vauth/*.c curl/lib/vquic/*.c curl/lib/vssh/*.c curl/lib/vtls/*.c) if(WIN32)
add_library( set(CURL_USE_SCHANNEL ON)
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()
endif() endif()
dolphin_disable_warnings(curl) if(ANDROID)
target_link_libraries(curl ${CURL_LIBS}) set(CURL_CA_PATH "/system/etc/security/cacerts" CACHE STRING "")
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()
endif() 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_memrchr.c" />
<ClCompile Include="curl\lib\curl_multibyte.c" /> <ClCompile Include="curl\lib\curl_multibyte.c" />
<ClCompile Include="curl\lib\curl_ntlm_core.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_range.c" />
<ClCompile Include="curl\lib\curl_rtmp.c" /> <ClCompile Include="curl\lib\curl_rtmp.c" />
<ClCompile Include="curl\lib\curl_sasl.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_sspi.c" />
<ClCompile Include="curl\lib\curl_threads.c" /> <ClCompile Include="curl\lib\curl_threads.c" />
<ClCompile Include="curl\lib\curl_trc.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\dict.c" />
<ClCompile Include="curl\lib\doh.c" /> <ClCompile Include="curl\lib\doh.c" />
<ClCompile Include="curl\lib\dynbuf.c" /> <ClCompile Include="curl\lib\dynbuf.c" />
@ -120,6 +120,7 @@
<ClCompile Include="curl\lib\psl.c" /> <ClCompile Include="curl\lib\psl.c" />
<ClCompile Include="curl\lib\rand.c" /> <ClCompile Include="curl\lib\rand.c" />
<ClCompile Include="curl\lib\rename.c" /> <ClCompile Include="curl\lib\rename.c" />
<ClCompile Include="curl\lib\request.c" />
<ClCompile Include="curl\lib\rtsp.c" /> <ClCompile Include="curl\lib\rtsp.c" />
<ClCompile Include="curl\lib\select.c" /> <ClCompile Include="curl\lib\select.c" />
<ClCompile Include="curl\lib\sendf.c" /> <ClCompile Include="curl\lib\sendf.c" />
@ -167,12 +168,16 @@
<ClCompile Include="curl\lib\vauth\vauth.c" /> <ClCompile Include="curl\lib\vauth\vauth.c" />
<ClCompile Include="curl\lib\vquic\curl_msh3.c" /> <ClCompile Include="curl\lib\vquic\curl_msh3.c" />
<ClCompile Include="curl\lib\vquic\curl_ngtcp2.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\curl_quiche.c" />
<ClCompile Include="curl\lib\vquic\vquic.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\libssh.c" />
<ClCompile Include="curl\lib\vssh\libssh2.c" /> <ClCompile Include="curl\lib\vssh\libssh2.c" />
<ClCompile Include="curl\lib\vssh\wolfssh.c" /> <ClCompile Include="curl\lib\vssh\wolfssh.c" />
<ClCompile Include="curl\lib\vtls\bearssl.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\gtls.c" />
<ClCompile Include="curl\lib\vtls\hostcheck.c" /> <ClCompile Include="curl\lib\vtls\hostcheck.c" />
<ClCompile Include="curl\lib\vtls\keylog.c" /> <ClCompile Include="curl\lib\vtls\keylog.c" />
@ -244,8 +249,6 @@
<ClInclude Include="curl\lib\curl_memrchr.h" /> <ClInclude Include="curl\lib\curl_memrchr.h" />
<ClInclude Include="curl\lib\curl_multibyte.h" /> <ClInclude Include="curl\lib\curl_multibyte.h" />
<ClInclude Include="curl\lib\curl_ntlm_core.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_printf.h" />
<ClInclude Include="curl\lib\curl_range.h" /> <ClInclude Include="curl\lib\curl_range.h" />
<ClInclude Include="curl\lib\curl_rtmp.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.h" />
<ClInclude Include="curl\lib\curl_setup_once.h" /> <ClInclude Include="curl\lib\curl_setup_once.h" />
<ClInclude Include="curl\lib\curl_sha256.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_sspi.h" />
<ClInclude Include="curl\lib\curl_threads.h" /> <ClInclude Include="curl\lib\curl_threads.h" />
<ClInclude Include="curl\lib\curl_trc.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\dict.h" />
<ClInclude Include="curl\lib\doh.h" /> <ClInclude Include="curl\lib\doh.h" />
<ClInclude Include="curl\lib\dynbuf.h" /> <ClInclude Include="curl\lib\dynbuf.h" />
@ -308,6 +313,7 @@
<ClInclude Include="curl\lib\psl.h" /> <ClInclude Include="curl\lib\psl.h" />
<ClInclude Include="curl\lib\rand.h" /> <ClInclude Include="curl\lib\rand.h" />
<ClInclude Include="curl\lib\rename.h" /> <ClInclude Include="curl\lib\rename.h" />
<ClInclude Include="curl\lib\request.h" />
<ClInclude Include="curl\lib\rtsp.h" /> <ClInclude Include="curl\lib\rtsp.h" />
<ClInclude Include="curl\lib\select.h" /> <ClInclude Include="curl\lib\select.h" />
<ClInclude Include="curl\lib\sendf.h" /> <ClInclude Include="curl\lib\sendf.h" />
@ -347,11 +353,15 @@
<ClInclude Include="curl\lib\vauth\vauth.h" /> <ClInclude Include="curl\lib\vauth\vauth.h" />
<ClInclude Include="curl\lib\vquic\curl_msh3.h" /> <ClInclude Include="curl\lib\vquic\curl_msh3.h" />
<ClInclude Include="curl\lib\vquic\curl_ngtcp2.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\curl_quiche.h" />
<ClInclude Include="curl\lib\vquic\vquic.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\vquic\vquic_int.h" />
<ClInclude Include="curl\lib\vssh\curl_path.h" />
<ClInclude Include="curl\lib\vssh\ssh.h" /> <ClInclude Include="curl\lib\vssh\ssh.h" />
<ClInclude Include="curl\lib\vtls\bearssl.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\gtls.h" />
<ClInclude Include="curl\lib\vtls\hostcheck.h" /> <ClInclude Include="curl\lib\vtls\hostcheck.h" />
<ClInclude Include="curl\lib\vtls\keylog.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.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
@ -326,12 +328,11 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider {
} }
private fun enableFullscreenImmersive() { private fun enableFullscreenImmersive() {
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or WindowCompat.setDecorFitsSystemWindows(window, false)
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or WindowInsetsControllerCompat(window, window.decorView).let { controller ->
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or controller.hide(WindowInsetsCompat.Type.systemBars())
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
View.SYSTEM_UI_FLAG_FULLSCREEN or }
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
} }
private fun updateDisplaySettings() { private fun updateDisplaySettings() {

View File

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

View File

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

View File

@ -191,7 +191,7 @@ Java_org_dolphinemu_dolphinemu_features_cheats_model_GeckoCheat_downloadCodes(JN
const std::string gametdb_id = GetJString(env, jGameTdbId); const std::string gametdb_id = GetJString(env, jGameTdbId);
bool success = true; 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) if (!success)
return nullptr; return nullptr;

View File

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

View File

@ -85,7 +85,8 @@ bool IniFile::Section::Delete(std::string_view key)
return false; return false;
values.erase(it); 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; 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), static_assert(NumFields == sizeof...(args),
"Unexpected number of replacement fields in format string; did you pass too few or " "Unexpected number of replacement fields in format string; did you pass too few or "
"too many arguments?"); "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 } // namespace Common::Log

View File

@ -1,48 +1,48 @@
// Copyright 2019 Dolphin Emulator Project // Copyright 2019 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <algorithm> #include <algorithm>
#include <mz_compat.h> #include <mz_compat.h>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/ScopeGuard.h" #include "Common/ScopeGuard.h"
namespace Common namespace Common
{ {
// Reads all of the current file. destination must be big enough to fit the whole file. // 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) inline bool ReadFileFromZip(unzFile file, u8* destination, u64 len)
{ {
const u64 MAX_BUFFER_SIZE = 65535; const u64 MAX_BUFFER_SIZE = 65535;
if (unzOpenCurrentFile(file) != UNZ_OK) if (unzOpenCurrentFile(file) != UNZ_OK)
return false; return false;
Common::ScopeGuard guard{[&] { unzCloseCurrentFile(file); }}; Common::ScopeGuard guard{[&] { unzCloseCurrentFile(file); }};
u64 bytes_to_go = len; u64 bytes_to_go = len;
while (bytes_to_go > 0) while (bytes_to_go > 0)
{ {
// NOTE: multiples of 4G can't cause read_len == 0 && bytes_to_go > 0, as MAX_BUFFER_SIZE is // NOTE: multiples of 4G can't cause read_len == 0 && bytes_to_go > 0, as MAX_BUFFER_SIZE is
// small. // small.
const u32 read_len = static_cast<u32>(std::min(bytes_to_go, MAX_BUFFER_SIZE)); const u32 read_len = static_cast<u32>(std::min(bytes_to_go, MAX_BUFFER_SIZE));
const int rv = unzReadCurrentFile(file, destination, read_len); const int rv = unzReadCurrentFile(file, destination, read_len);
if (rv < 0) if (rv < 0)
return false; return false;
const u32 bytes_read = static_cast<u32>(rv); const u32 bytes_read = static_cast<u32>(rv);
bytes_to_go -= bytes_read; bytes_to_go -= bytes_read;
destination += bytes_read; destination += bytes_read;
} }
return unzEndOfFile(file) == 1; return unzEndOfFile(file) == 1;
} }
template <typename ContiguousContainer> template <typename ContiguousContainer>
bool ReadFileFromZip(unzFile file, ContiguousContainer* destination) bool ReadFileFromZip(unzFile file, ContiguousContainer* destination)
{ {
return ReadFileFromZip(file, reinterpret_cast<u8*>(destination->data()), destination->size()); return ReadFileFromZip(file, reinterpret_cast<u8*>(destination->data()), destination->size());
} }
} // namespace Common } // 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), static_assert(NumFields == sizeof...(args),
"Unexpected number of replacement fields in format string; did you pass too few or " "Unexpected number of replacement fields in format string; did you pass too few or "
"too many arguments?"); "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); static_assert(fmt::detail::is_compile_string<S>::value);
auto&& format_str = format;
#else #else
static_assert(fmt::is_compile_string<S>::value); static_assert(fmt::is_compile_string<S>::value);
auto&& format_str = format;
#endif #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...)); 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), static_assert(NumFields == sizeof...(args),
"Unexpected number of replacement fields in format string; did you pass too few or " "Unexpected number of replacement fields in format string; did you pass too few or "
"too many arguments?"); "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); static_assert(fmt::detail::is_compile_string<S>::value);
#else #else
static_assert(fmt::is_compile_string<S>::value); static_assert(fmt::is_compile_string<S>::value);

View File

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

View File

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

View File

@ -17,13 +17,10 @@
namespace Gecko 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. // 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; Common::HttpRequest http;
// The server always redirects once to the same location. // The server always redirects once to the same location.

View File

@ -17,8 +17,7 @@ class IniFile;
namespace Gecko namespace Gecko
{ {
std::vector<GeckoCode> LoadCodes(const Common::IniFile& globalIni, const Common::IniFile& localIni); std::vector<GeckoCode> LoadCodes(const Common::IniFile& globalIni, const Common::IniFile& localIni);
std::vector<GeckoCode> DownloadCodes(std::string gametdb_id, bool* succeeded, std::vector<GeckoCode> DownloadCodes(std::string gametdb_id, bool* succeeded);
bool use_https = true);
void SaveCodes(Common::IniFile& inifile, const std::vector<GeckoCode>& gcodes); void SaveCodes(Common::IniFile& inifile, const std::vector<GeckoCode>& gcodes);
std::optional<GeckoCode::Code> DeserializeLine(const std::string& line); 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: default:
ERROR_LOG_FMT(SP1, "Unsupported EtherType {#06x}", *ethertype); ERROR_LOG_FMT(SP1, "Unsupported EtherType {:#06x}", *ethertype);
return false; return false;
} }

View File

@ -132,7 +132,8 @@ struct DTMHeader
bool bUseFMA; bool bUseFMA;
u8 GBAControllers; // GBA Controllers plugged in (the bits are ports 1-4) 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 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<char, 40> discChange; // Name of iso file to switch to, for two disc games.
std::array<u8, 20> revision; // Git hash std::array<u8, 20> revision; // Git hash
u32 DSPiromHash; u32 DSPiromHash;

View File

@ -765,6 +765,14 @@ void JitArm64::rlwinmx_internal(UGeckoInstruction inst, u32 sh)
return; return;
} }
if (mask == 0)
{
gpr.SetImmediate(a, 0);
if (inst.Rc)
ComputeRC0(0);
return;
}
gpr.BindToRegister(a, a == s); gpr.BindToRegister(a, a == s);
if (sh == 0 && mask == 0xFFFFFFFF) if (sh == 0 && mask == 0xFFFFFFFF)
@ -1128,47 +1136,85 @@ void JitArm64::addzex(UGeckoInstruction inst)
int a = inst.RA, d = inst.RD; int a = inst.RA, d = inst.RD;
switch (js.carryFlag) if (gpr.IsImm(a) && (gpr.GetImm(a) == 0 || HasConstantCarry()))
{ {
case CarryFlag::InPPCState: const u32 imm = gpr.GetImm(a);
{ const bool is_all_ones = imm == 0xFFFFFFFF;
const bool allocate_reg = d == a;
gpr.BindToRegister(d, allocate_reg);
switch (js.carryFlag)
{ {
auto WA = allocate_reg ? gpr.GetScopedReg() : Arm64GPRCache::ScopedARM64Reg(gpr.R(d)); case CarryFlag::InPPCState:
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); 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) if (inst.Rc)
@ -1216,40 +1262,62 @@ void JitArm64::subfex(UGeckoInstruction inst)
if (gpr.IsImm(a) && (mex || gpr.IsImm(b))) if (gpr.IsImm(a) && (mex || gpr.IsImm(b)))
{ {
u32 i = gpr.GetImm(a), j = mex ? -1 : gpr.GetImm(b); const u32 i = gpr.GetImm(a);
const u32 j = mex ? -1 : gpr.GetImm(b);
gpr.BindToRegister(d, false); const u32 imm = ~i + j;
const bool is_zero = imm == 0;
const bool is_all_ones = imm == 0xFFFFFFFF;
switch (js.carryFlag) switch (js.carryFlag)
{ {
case CarryFlag::InPPCState: case CarryFlag::InPPCState:
{ {
auto WA = gpr.GetScopedReg(); gpr.BindToRegister(d, false);
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca)); ARM64Reg RD = gpr.R(d);
ADDI2R(gpr.R(d), WA, ~i + j, 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; break;
} }
case CarryFlag::InHostCarry: case CarryFlag::InHostCarry:
{ {
auto WA = gpr.GetScopedReg(); gpr.BindToRegister(d, false);
MOVI2R(WA, ~i + j); ARM64Reg RD = gpr.R(d);
ADC(gpr.R(d), WA, ARM64Reg::WZR); 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; break;
} }
case CarryFlag::ConstantTrue: case CarryFlag::ConstantTrue:
{ {
gpr.SetImmediate(d, ~i + j + 1); gpr.SetImmediate(d, imm + 1);
break; break;
} }
case CarryFlag::ConstantFalse: case CarryFlag::ConstantFalse:
{ {
gpr.SetImmediate(d, ~i + j); gpr.SetImmediate(d, imm);
break; break;
} }
} }
const bool must_have_carry = Interpreter::Helper_Carry(~i, j); 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) if (must_have_carry)
{ {
@ -1337,39 +1405,49 @@ void JitArm64::subfzex(UGeckoInstruction inst)
int a = inst.RA, d = inst.RD; 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) switch (js.carryFlag)
{
case CarryFlag::InPPCState:
{
{ {
auto WA = gpr.GetScopedReg(); case CarryFlag::InPPCState:
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); 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) if (inst.Rc)
@ -1436,40 +1514,66 @@ void JitArm64::addex(UGeckoInstruction inst)
if (gpr.IsImm(a) && (mex || gpr.IsImm(b))) if (gpr.IsImm(a) && (mex || gpr.IsImm(b)))
{ {
u32 i = gpr.GetImm(a), j = mex ? -1 : gpr.GetImm(b); const u32 i = gpr.GetImm(a), j = mex ? -1 : gpr.GetImm(b);
const u32 imm = i + j;
gpr.BindToRegister(d, false); const bool is_zero = imm == 0;
const bool is_all_ones = imm == 0xFFFFFFFF;
switch (js.carryFlag) switch (js.carryFlag)
{ {
case CarryFlag::InPPCState: case CarryFlag::InPPCState:
{ {
auto WA = gpr.GetScopedReg(); gpr.BindToRegister(d, false);
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca)); ARM64Reg RD = gpr.R(d);
ADDI2R(gpr.R(d), WA, i + j, 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; break;
} }
case CarryFlag::InHostCarry: case CarryFlag::InHostCarry:
{ {
gpr.BindToRegister(d, false);
ARM64Reg RD = gpr.R(d); ARM64Reg RD = gpr.R(d);
MOVI2R(RD, i + j); if (is_zero)
ADC(RD, RD, ARM64Reg::WZR); {
// 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; break;
} }
case CarryFlag::ConstantTrue: case CarryFlag::ConstantTrue:
{ {
gpr.SetImmediate(d, i + j + 1); gpr.SetImmediate(d, imm + 1);
break; break;
} }
case CarryFlag::ConstantFalse: case CarryFlag::ConstantFalse:
{ {
gpr.SetImmediate(d, i + j); gpr.SetImmediate(d, imm);
break; break;
} }
} }
const bool must_have_carry = Interpreter::Helper_Carry(i, j); 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) 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)); STR(IndexType::Unsigned, loop_counter, PPC_REG, PPCSTATE_OFF_SPR(SPR_CTR));
// downcount -= (WA * reg_cycle_count) // 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). // ^ 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)); STR(IndexType::Unsigned, reg_downcount, PPC_REG, PPCSTATE_OFF(downcount));
SetJumpTarget(downcount_is_zero_or_negative); SetJumpTarget(downcount_is_zero_or_negative);

View File

@ -110,7 +110,7 @@ JitBase::~JitBase()
CPUThreadConfigCallback::RemoveConfigChangedCallback(m_registered_config_callback_id); CPUThreadConfigCallback::RemoveConfigChangedCallback(m_registered_config_callback_id);
} }
bool JitBase::DoesConfigNeedRefresh() bool JitBase::DoesConfigNeedRefresh() const
{ {
return std::ranges::any_of(JIT_SETTINGS, [this](const auto& pair) { return std::ranges::any_of(JIT_SETTINGS, [this](const auto& pair) {
return this->*pair.first != Config::Get(*pair.second); return this->*pair.first != Config::Get(*pair.second);
@ -276,7 +276,7 @@ bool JitBase::CanMergeNextInstructions(int count) const
return true; return true;
} }
bool JitBase::ShouldHandleFPExceptionForInstruction(const PPCAnalyst::CodeOp* op) bool JitBase::ShouldHandleFPExceptionForInstruction(const PPCAnalyst::CodeOp* op) const
{ {
if (jo.fp_exceptions) if (jo.fp_exceptions)
return (op->opinfo->flags & FL_FLOAT_EXCEPTION) != 0; 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; static const std::array<std::pair<bool JitBase::*, const Config::Info<bool>*>, 23> JIT_SETTINGS;
bool DoesConfigNeedRefresh(); bool DoesConfigNeedRefresh() const;
void RefreshConfig(); void RefreshConfig();
void InitFastmemArena(); void InitFastmemArena();
@ -178,8 +178,16 @@ protected:
void CleanUpAfterStackFault(); void CleanUpAfterStackFault();
bool CanMergeNextInstructions(int count) const; 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: public:
explicit JitBase(Core::System& system); explicit JitBase(Core::System& system);

View File

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

View File

@ -467,12 +467,7 @@ OnlineSystemUpdater::Response OnlineSystemUpdater::GetSystemTitles()
// but the backing data CDN is still active and accessible from other URLs. We take advantage // 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 // 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. // 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"; base_url = "https://fakenus.dolphin-emu.org";
#endif
} }
const std::string url = fmt::format("{}/nus/services/NetUpdateSOAP", base_url); const std::string url = fmt::format("{}/nus/services/NetUpdateSOAP", base_url);

View File

@ -573,12 +573,11 @@ endif()
if(APPLE) if(APPLE)
include(BundleUtilities) include(BundleUtilities)
set(BUNDLE_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/DolphinQt.app)
# Ask for an application bundle. # Ask for an application bundle.
set_target_properties(dolphin-emu PROPERTIES set_target_properties(dolphin-emu PROPERTIES
MACOSX_BUNDLE true 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 "" XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ""
OUTPUT_NAME DolphinQt OUTPUT_NAME DolphinQt
) )
@ -615,6 +614,9 @@ if(APPLE)
source_group("Resources" FILES "${CMAKE_SOURCE_DIR}/Data/${res}") source_group("Resources" FILES "${CMAKE_SOURCE_DIR}/Data/${res}")
endforeach() endforeach()
include(DolphinInjectVersionInfo)
dolphin_inject_version_info(dolphin-emu)
# Copy MoltenVK into the bundle # Copy MoltenVK into the bundle
if(ENABLE_VULKAN) if(ENABLE_VULKAN)
if(USE_BUNDLED_MOLTENVK) if(USE_BUNDLED_MOLTENVK)
@ -649,7 +651,7 @@ if(APPLE)
COMMAND "${CMAKE_SOURCE_DIR}/Tools/mac-codesign.sh" COMMAND "${CMAKE_SOURCE_DIR}/Tools/mac-codesign.sh"
"-e" "${CMAKE_CURRENT_SOURCE_DIR}/DolphinEmu$<$<CONFIG:Debug>:Debug>.entitlements" "-e" "${CMAKE_CURRENT_SOURCE_DIR}/DolphinEmu$<$<CONFIG:Debug>:Debug>.entitlements"
"${MACOS_CODE_SIGNING_IDENTITY}" "${MACOS_CODE_SIGNING_IDENTITY}"
"${BUNDLE_PATH}" "$<TARGET_BUNDLE_DIR:dolphin-emu>"
) )
endif() endif()
else() else()

View File

@ -74,7 +74,7 @@ void ConfigStringChoice::OnConfigChanged()
ConfigComplexChoice::ConfigComplexChoice(const InfoVariant setting1, const InfoVariant setting2, ConfigComplexChoice::ConfigComplexChoice(const InfoVariant setting1, const InfoVariant setting2,
Config::Layer* layer) Config::Layer* layer)
: m_setting1(setting1), m_setting2(setting2), m_layer(layer) : m_layer(layer), m_setting1(setting1), m_setting2(setting2)
{ {
connect(&Settings::Instance(), &Settings::ConfigChanged, this, &ConfigComplexChoice::Refresh); connect(&Settings::Instance(), &Settings::ConfigChanged, this, &ConfigComplexChoice::Refresh);
connect(this, &QComboBox::currentIndexChanged, this, &ConfigComplexChoice::SaveValue); connect(this, &QComboBox::currentIndexChanged, this, &ConfigComplexChoice::SaveValue);
@ -115,7 +115,7 @@ void ConfigComplexChoice::Reset()
void ConfigComplexChoice::SaveValue(int choice) void ConfigComplexChoice::SaveValue(int choice)
{ {
auto Set = [this, choice](auto& setting, auto& value) { auto Set = [this](auto& setting, auto& value) {
if (m_layer != nullptr) if (m_layer != nullptr)
{ {
m_layer->Set(setting.GetLocation(), value); m_layer->Set(setting.GetLocation(), value);

View File

@ -34,7 +34,8 @@ using SIDeviceName = std::pair<SerialInterface::SIDevices, const char*>;
static constexpr std::array s_gc_types = { static constexpr std::array s_gc_types = {
SIDeviceName{SerialInterface::SIDEVICE_NONE, _trans("None")}, SIDeviceName{SerialInterface::SIDEVICE_NONE, _trans("None")},
SIDeviceName{SerialInterface::SIDEVICE_GC_CONTROLLER, _trans("Standard Controller")}, SIDeviceName{SerialInterface::SIDEVICE_GC_CONTROLLER, _trans("Standard Controller")},
SIDeviceName{SerialInterface::SIDEVICE_WIIU_ADAPTER, _trans("GameCube Adapter for Wii U")}, SIDeviceName{SerialInterface::SIDEVICE_WIIU_ADAPTER,
_trans("GameCube Controller Adapter (USB)")},
SIDeviceName{SerialInterface::SIDEVICE_GC_STEERING, _trans("Steering Wheel")}, SIDeviceName{SerialInterface::SIDEVICE_GC_STEERING, _trans("Steering Wheel")},
SIDeviceName{SerialInterface::SIDEVICE_DANCEMAT, _trans("Dance Mat")}, SIDeviceName{SerialInterface::SIDEVICE_DANCEMAT, _trans("Dance Mat")},
SIDeviceName{SerialInterface::SIDEVICE_GC_TARUKONGA, _trans("DK Bongos")}, SIDeviceName{SerialInterface::SIDEVICE_GC_TARUKONGA, _trans("DK Bongos")},

View File

@ -36,7 +36,7 @@ AdvancedWidget::AdvancedWidget(GraphicsWindow* parent)
OnEmulationStateChanged(state != Core::State::Uninitialized); OnEmulationStateChanged(state != Core::State::Uninitialized);
}); });
connect(m_manual_texture_sampling, &QCheckBox::toggled, connect(m_manual_texture_sampling, &QCheckBox::toggled,
[this, parent] { emit parent->UseFastTextureSamplingChanged(); }); [parent] { emit parent->UseFastTextureSamplingChanged(); });
OnBackendChanged(); OnBackendChanged();
OnEmulationStateChanged(!Core::IsUninitialized(Core::System::GetInstance())); OnEmulationStateChanged(!Core::IsUninitialized(Core::System::GetInstance()));
@ -262,7 +262,7 @@ void AdvancedWidget::ConnectWidgets()
m_dump_base_textures->setEnabled(checked); m_dump_base_textures->setEnabled(checked);
}); });
connect(m_enable_graphics_mods, &QCheckBox::toggled, this, connect(m_enable_graphics_mods, &QCheckBox::toggled, this,
[this](bool checked) { emit Settings::Instance().EnableGfxModsChanged(checked); }); [](bool checked) { emit Settings::Instance().EnableGfxModsChanged(checked); });
#if defined(HAVE_FFMPEG) #if defined(HAVE_FFMPEG)
connect(m_dump_use_lossless, &QCheckBox::toggled, this, connect(m_dump_use_lossless, &QCheckBox::toggled, this,
[this](bool checked) { m_dump_bitrate->setEnabled(!checked); }); [this](bool checked) { m_dump_bitrate->setEnabled(!checked); });

View File

@ -32,7 +32,7 @@ GCPadWiiUConfigDialog::~GCPadWiiUConfigDialog()
void GCPadWiiUConfigDialog::CreateLayout() void GCPadWiiUConfigDialog::CreateLayout()
{ {
setWindowTitle(tr("GameCube Adapter for Wii U at Port %1").arg(m_port + 1)); setWindowTitle(tr("GameCube Controller Adapter at Port %1").arg(m_port + 1));
m_layout = new QVBoxLayout(); m_layout = new QVBoxLayout();
m_status_label = new QLabel(); m_status_label = new QLabel();

View File

@ -4,7 +4,6 @@
#include "DolphinQt/Config/Mapping/IOWindow.h" #include "DolphinQt/Config/Mapping/IOWindow.h"
#include <optional> #include <optional>
#include <thread>
#include <QBrush> #include <QBrush>
#include <QColor> #include <QColor>
@ -20,16 +19,15 @@
#include <QSlider> #include <QSlider>
#include <QSpinBox> #include <QSpinBox>
#include <QTableWidget> #include <QTableWidget>
#include <QTimer>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "Core/Core.h"
#include "DolphinQt/Config/Mapping/MappingCommon.h" #include "DolphinQt/Config/Mapping/MappingCommon.h"
#include "DolphinQt/Config/Mapping/MappingIndicator.h" #include "DolphinQt/Config/Mapping/MappingIndicator.h"
#include "DolphinQt/Config/Mapping/MappingWidget.h"
#include "DolphinQt/Config/Mapping/MappingWindow.h" #include "DolphinQt/Config/Mapping/MappingWindow.h"
#include "DolphinQt/QtUtils/BlockUserInputFilter.h" #include "DolphinQt/QtUtils/BlockUserInputFilter.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h" #include "DolphinQt/QtUtils/ModalMessageBox.h"
#include "DolphinQt/QtUtils/SetWindowDecorations.h"
#include "DolphinQt/Settings.h" #include "DolphinQt/Settings.h"
#include "InputCommon/ControlReference/ControlReference.h" #include "InputCommon/ControlReference/ControlReference.h"
@ -40,6 +38,9 @@
namespace namespace
{ {
constexpr auto INPUT_DETECT_TIME = std::chrono::seconds(2);
constexpr auto OUTPUT_TEST_TIME = std::chrono::seconds(2);
QTextCharFormat GetSpecialCharFormat() QTextCharFormat GetSpecialCharFormat()
{ {
QTextCharFormat format; QTextCharFormat format;
@ -228,15 +229,17 @@ private:
bool m_should_paint_state_indicator = false; bool m_should_paint_state_indicator = false;
}; };
IOWindow::IOWindow(MappingWidget* parent, ControllerEmu::EmulatedController* controller, IOWindow::IOWindow(MappingWindow* window, ControllerEmu::EmulatedController* controller,
ControlReference* ref, IOWindow::Type type) ControlReference* ref, IOWindow::Type type)
: QDialog(parent), m_reference(ref), m_original_expression(ref->GetExpression()), : QDialog(window), m_reference(ref), m_original_expression(ref->GetExpression()),
m_controller(controller), m_type(type) m_controller(controller), m_type(type)
{ {
SetQWidgetWindowDecorations(this);
CreateMainLayout(); CreateMainLayout();
connect(parent, &MappingWidget::Update, this, &IOWindow::Update); connect(window, &MappingWindow::Update, this, &IOWindow::Update);
connect(parent->GetParent(), &MappingWindow::ConfigChanged, this, &IOWindow::ConfigChanged); connect(window, &MappingWindow::ConfigChanged, this, &IOWindow::ConfigChanged);
connect(&Settings::Instance(), &Settings::ConfigChanged, this, &IOWindow::ConfigChanged); connect(&Settings::Instance(), &Settings::ConfigChanged, this, &IOWindow::ConfigChanged);
setWindowTitle(type == IOWindow::Type::Input ? tr("Configure Input") : tr("Configure Output")); setWindowTitle(type == IOWindow::Type::Input ? tr("Configure Input") : tr("Configure Output"));
@ -258,18 +261,29 @@ void IOWindow::CreateMainLayout()
m_devices_combo = new QComboBox(); m_devices_combo = new QComboBox();
m_option_list = new QTableWidget(); m_option_list = new QTableWidget();
m_select_button = new QPushButton(tr("Select"));
m_detect_button = new QPushButton(tr("Detect"), this); m_select_button =
m_test_button = new QPushButton(tr("Test"), this); new QPushButton(m_type == IOWindow::Type::Input ? tr("Insert Input") : tr("Insert Output"));
m_detect_button = new QPushButton(tr("Detect Input"), this);
m_test_button = new QPushButton(tr("Test Output"), this);
m_button_box = new QDialogButtonBox(); m_button_box = new QDialogButtonBox();
m_clear_button = new QPushButton(tr("Clear")); m_clear_button = new QPushButton(tr("Clear"));
m_scalar_spinbox = new QSpinBox(); m_scalar_spinbox = new QSpinBox();
m_parse_text = new InputStateLineEdit([this] { if (m_type == Type::Input)
const auto lock = m_controller->GetStateLock(); {
return m_reference->GetState<ControlState>(); m_parse_text = new InputStateLineEdit([this] {
}); const auto lock = m_controller->GetStateLock();
m_parse_text->setReadOnly(true); return m_reference->GetState<ControlState>();
});
}
else
{
m_parse_text = new InputStateLineEdit([this] {
const auto lock = m_controller->GetStateLock();
return m_output_test_timer->isActive() * m_reference->range;
});
}
m_expression_text = new QPlainTextEdit(); m_expression_text = new QPlainTextEdit();
m_expression_text->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); m_expression_text->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
@ -419,11 +433,17 @@ void IOWindow::CreateMainLayout()
m_button_box->addButton(m_clear_button, QDialogButtonBox::ActionRole); m_button_box->addButton(m_clear_button, QDialogButtonBox::ActionRole);
m_button_box->addButton(QDialogButtonBox::Ok); m_button_box->addButton(QDialogButtonBox::Ok);
m_output_test_timer = new QTimer(this);
m_output_test_timer->setSingleShot(true);
setLayout(m_main_layout); setLayout(m_main_layout);
} }
void IOWindow::ConfigChanged() void IOWindow::ConfigChanged()
{ {
emit DetectInputComplete();
emit TestOutputComplete();
const QSignalBlocker blocker(this); const QSignalBlocker blocker(this);
const auto lock = ControllerEmu::EmulatedController::GetStateLock(); const auto lock = ControllerEmu::EmulatedController::GetStateLock();
@ -444,6 +464,31 @@ void IOWindow::Update()
{ {
m_option_list->viewport()->update(); m_option_list->viewport()->update();
m_parse_text->update(); m_parse_text->update();
if (!m_input_detector)
return;
if (m_input_detector->IsComplete())
{
const auto results = m_input_detector->TakeResults();
emit DetectInputComplete();
if (results.empty())
return;
// Select the first detected input.
auto list = m_option_list->findItems(QString::fromStdString(results.front().input->GetName()),
Qt::MatchFixedString);
if (list.empty())
return;
m_option_list->setCurrentItem(list.front());
}
else
{
m_input_detector->Update(INPUT_DETECT_TIME, {}, INPUT_DETECT_TIME);
}
} }
void IOWindow::ConnectWidgets() void IOWindow::ConnectWidgets()
@ -453,8 +498,50 @@ void IOWindow::ConnectWidgets()
connect(&Settings::Instance(), &Settings::ReleaseDevices, this, &IOWindow::ReleaseDevices); connect(&Settings::Instance(), &Settings::ReleaseDevices, this, &IOWindow::ReleaseDevices);
connect(&Settings::Instance(), &Settings::DevicesChanged, this, &IOWindow::UpdateDeviceList); connect(&Settings::Instance(), &Settings::DevicesChanged, this, &IOWindow::UpdateDeviceList);
connect(m_detect_button, &QPushButton::clicked, this, &IOWindow::OnDetectButtonPressed); // Input detection:
connect(m_test_button, &QPushButton::clicked, this, &IOWindow::OnTestButtonPressed); // Clicking "Detect" button starts a timer before the actual detection.
auto* const input_detect_start_timer = new QTimer(this);
input_detect_start_timer->setSingleShot(true);
connect(m_detect_button, &QPushButton::clicked, [this, input_detect_start_timer] {
m_detect_button->setText(tr("[ ... ]"));
input_detect_start_timer->start(MappingCommon::INPUT_DETECT_INITIAL_DELAY);
});
connect(input_detect_start_timer, &QTimer::timeout, [this] {
m_detect_button->setText(tr("[ Press Now ]"));
m_input_detector = std::make_unique<ciface::Core::InputDetector>();
const auto lock = m_controller->GetStateLock();
m_input_detector->Start(g_controller_interface, {m_devq.ToString()});
QtUtils::InstallKeyboardBlocker(m_detect_button, this, &IOWindow::DetectInputComplete);
});
connect(this, &IOWindow::DetectInputComplete,
[this, initial_text = m_detect_button->text(), input_detect_start_timer] {
input_detect_start_timer->stop();
m_input_detector.reset();
m_detect_button->setText(initial_text);
});
// Rumble testing:
connect(m_test_button, &QPushButton::clicked, [this] {
// Stop if already started.
if (m_output_test_timer->isActive())
{
emit IOWindow::TestOutputComplete();
return;
}
m_test_button->setText(QStringLiteral("[ ... ]"));
m_output_test_timer->start(OUTPUT_TEST_TIME);
const auto lock = m_controller->GetStateLock();
m_reference->State(1.0);
});
connect(m_output_test_timer, &QTimer::timeout,
[this, initial_text = m_test_button->text()] { emit TestOutputComplete(); });
connect(this, &IOWindow::TestOutputComplete, [this, initial_text = m_test_button->text()] {
m_output_test_timer->stop();
m_test_button->setText(initial_text);
const auto lock = m_controller->GetStateLock();
m_reference->State(0.0);
});
connect(this, &IOWindow::closeEvent, this, &IOWindow::TestOutputComplete);
connect(m_button_box, &QDialogButtonBox::clicked, this, &IOWindow::OnDialogButtonPressed); connect(m_button_box, &QDialogButtonBox::clicked, this, &IOWindow::OnDialogButtonPressed);
connect(m_devices_combo, &QComboBox::currentTextChanged, this, &IOWindow::OnDeviceChanged); connect(m_devices_combo, &QComboBox::currentTextChanged, this, &IOWindow::OnDeviceChanged);
@ -546,30 +633,10 @@ void IOWindow::OnDialogButtonPressed(QAbstractButton* button)
} }
} }
void IOWindow::OnDetectButtonPressed()
{
const auto expression =
MappingCommon::DetectExpression(m_detect_button, g_controller_interface, {m_devq.ToString()},
m_devq, ciface::MappingCommon::Quote::Off);
if (expression.isEmpty())
return;
const auto list = m_option_list->findItems(expression, Qt::MatchFixedString);
// Try to select the first. If this fails, the last selected item would still appear as such
if (!list.empty())
m_option_list->setCurrentItem(list[0]);
}
void IOWindow::OnTestButtonPressed()
{
MappingCommon::TestOutput(m_test_button, static_cast<OutputReference*>(m_reference));
}
void IOWindow::OnRangeChanged(int value) void IOWindow::OnRangeChanged(int value)
{ {
m_reference->range = value / 100.0; m_reference->range = value / 100.0;
emit TestOutputComplete();
} }
void IOWindow::ReleaseDevices() void IOWindow::ReleaseDevices()
@ -670,6 +737,8 @@ void IOWindow::UpdateDeviceList()
void IOWindow::UpdateExpression(std::string new_expression, UpdateMode mode) void IOWindow::UpdateExpression(std::string new_expression, UpdateMode mode)
{ {
emit TestOutputComplete();
const auto lock = m_controller->GetStateLock(); const auto lock = m_controller->GetStateLock();
if (mode != UpdateMode::Force && new_expression == m_reference->GetExpression()) if (mode != UpdateMode::Force && new_expression == m_reference->GetExpression())
return; return;
@ -719,6 +788,7 @@ InputStateDelegate::InputStateDelegate(IOWindow* parent, int column,
InputStateLineEdit::InputStateLineEdit(std::function<ControlState()> state_evaluator) InputStateLineEdit::InputStateLineEdit(std::function<ControlState()> state_evaluator)
: m_state_evaluator(std::move(state_evaluator)) : m_state_evaluator(std::move(state_evaluator))
{ {
setReadOnly(true);
} }
static void PaintStateIndicator(QPainter& painter, const QRect& region, ControlState state) static void PaintStateIndicator(QPainter& painter, const QRect& region, ControlState state)

View File

@ -12,11 +12,10 @@
#include <QString> #include <QString>
#include <QSyntaxHighlighter> #include <QSyntaxHighlighter>
#include "Common/Flag.h"
#include "InputCommon/ControllerInterface/CoreDevice.h" #include "InputCommon/ControllerInterface/CoreDevice.h"
class ControlReference; class ControlReference;
class MappingWidget; class MappingWindow;
class QAbstractButton; class QAbstractButton;
class QDialogButtonBox; class QDialogButtonBox;
class QLineEdit; class QLineEdit;
@ -66,9 +65,13 @@ public:
Output Output
}; };
explicit IOWindow(MappingWidget* parent, ControllerEmu::EmulatedController* m_controller, explicit IOWindow(MappingWindow* window, ControllerEmu::EmulatedController* m_controller,
ControlReference* ref, Type type); ControlReference* ref, Type type);
signals:
void DetectInputComplete();
void TestOutputComplete();
private: private:
std::shared_ptr<ciface::Core::Device> GetSelectedDevice() const; std::shared_ptr<ciface::Core::Device> GetSelectedDevice() const;
@ -79,8 +82,6 @@ private:
void OnDialogButtonPressed(QAbstractButton* button); void OnDialogButtonPressed(QAbstractButton* button);
void OnDeviceChanged(); void OnDeviceChanged();
void OnDetectButtonPressed();
void OnTestButtonPressed();
void OnRangeChanged(int range); void OnRangeChanged(int range);
void AppendSelectedOption(); void AppendSelectedOption();
@ -115,10 +116,12 @@ private:
// Input actions // Input actions
QPushButton* m_detect_button; QPushButton* m_detect_button;
std::unique_ptr<ciface::Core::InputDetector> m_input_detector;
QComboBox* m_functions_combo; QComboBox* m_functions_combo;
// Output actions // Output actions
QPushButton* m_test_button; QPushButton* m_test_button;
QTimer* m_output_test_timer;
// Textarea // Textarea
QPlainTextEdit* m_expression_text; QPlainTextEdit* m_expression_text;

View File

@ -9,13 +9,12 @@
#include <QString> #include <QString>
#include "DolphinQt/Config/Mapping/IOWindow.h" #include "DolphinQt/Config/Mapping/IOWindow.h"
#include "DolphinQt/Config/Mapping/MappingCommon.h"
#include "DolphinQt/Config/Mapping/MappingWidget.h" #include "DolphinQt/Config/Mapping/MappingWidget.h"
#include "DolphinQt/Config/Mapping/MappingWindow.h" #include "DolphinQt/Config/Mapping/MappingWindow.h"
#include "DolphinQt/QtUtils/BlockUserInputFilter.h"
#include "DolphinQt/QtUtils/SetWindowDecorations.h" #include "DolphinQt/QtUtils/SetWindowDecorations.h"
#include "InputCommon/ControlReference/ControlReference.h" #include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerEmu/ControlGroup/Buttons.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h" #include "InputCommon/ControllerEmu/ControllerEmu.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h" #include "InputCommon/ControllerInterface/ControllerInterface.h"
@ -74,7 +73,7 @@ bool MappingButton::IsInput() const
} }
MappingButton::MappingButton(MappingWidget* parent, ControlReference* ref, bool indicator) MappingButton::MappingButton(MappingWidget* parent, ControlReference* ref, bool indicator)
: ElidedButton(RefToDisplayString(ref)), m_parent(parent), m_reference(ref) : ElidedButton(RefToDisplayString(ref)), m_mapping_window(parent->GetParent()), m_reference(ref)
{ {
if (IsInput()) if (IsInput())
{ {
@ -92,17 +91,22 @@ MappingButton::MappingButton(MappingWidget* parent, ControlReference* ref, bool
connect(parent, &MappingWidget::Update, this, &MappingButton::UpdateIndicator); connect(parent, &MappingWidget::Update, this, &MappingButton::UpdateIndicator);
connect(parent, &MappingWidget::ConfigChanged, this, &MappingButton::ConfigChanged); connect(parent, &MappingWidget::ConfigChanged, this, &MappingButton::ConfigChanged);
connect(this, &MappingButton::ConfigChanged, [this] {
setText(RefToDisplayString(m_reference));
m_is_mapping = false;
});
} }
void MappingButton::AdvancedPressed() void MappingButton::AdvancedPressed()
{ {
IOWindow io(m_parent, m_parent->GetController(), m_reference, m_mapping_window->CancelMapping();
IOWindow io(m_mapping_window, m_mapping_window->GetController(), m_reference,
m_reference->IsInput() ? IOWindow::Type::Input : IOWindow::Type::Output); m_reference->IsInput() ? IOWindow::Type::Input : IOWindow::Type::Output);
SetQWidgetWindowDecorations(&io);
io.exec(); io.exec();
ConfigChanged(); ConfigChanged();
m_parent->SaveSettings(); m_mapping_window->Save();
} }
void MappingButton::Clicked() void MappingButton::Clicked()
@ -113,31 +117,8 @@ void MappingButton::Clicked()
return; return;
} }
const auto default_device_qualifier = m_parent->GetController()->GetDefaultDevice(); m_is_mapping = true;
m_mapping_window->QueueInputDetection(this);
QString expression;
if (m_parent->GetParent()->IsMappingAllDevices())
{
expression = MappingCommon::DetectExpression(this, g_controller_interface,
g_controller_interface.GetAllDeviceStrings(),
default_device_qualifier);
}
else
{
expression = MappingCommon::DetectExpression(this, g_controller_interface,
{default_device_qualifier.ToString()},
default_device_qualifier);
}
if (expression.isEmpty())
return;
m_reference->SetExpression(expression.toStdString());
m_parent->GetController()->UpdateSingleControlReference(g_controller_interface, m_reference);
ConfigChanged();
m_parent->SaveSettings();
} }
void MappingButton::Clear() void MappingButton::Clear()
@ -145,22 +126,21 @@ void MappingButton::Clear()
m_reference->range = 100.0 / SLIDER_TICK_COUNT; m_reference->range = 100.0 / SLIDER_TICK_COUNT;
m_reference->SetExpression(""); m_reference->SetExpression("");
m_parent->GetController()->UpdateSingleControlReference(g_controller_interface, m_reference); m_mapping_window->GetController()->UpdateSingleControlReference(g_controller_interface,
m_reference);
m_parent->SaveSettings(); m_mapping_window->Save();
ConfigChanged();
m_mapping_window->UnQueueInputDetection(this);
} }
void MappingButton::UpdateIndicator() void MappingButton::UpdateIndicator()
{ {
if (!isActiveWindow()) QFont f = m_mapping_window->font();
return;
QFont f = m_parent->font(); if (isActiveWindow() && m_reference->IsInput() && m_reference->GetState<bool>() && !m_is_mapping)
// If the input state is "true" (we can't know the state of outputs), show it in bold.
if (m_reference->IsInput() && m_reference->GetState<bool>())
f.setBold(true); f.setBold(true);
// If the expression has failed to parse, show it in italic. // If the expression has failed to parse, show it in italic.
// Some expressions still work even the failed to parse so don't prevent the GetState() above. // Some expressions still work even the failed to parse so don't prevent the GetState() above.
if (m_reference->GetParseStatus() == ciface::ExpressionParser::ParseStatus::SyntaxError) if (m_reference->GetParseStatus() == ciface::ExpressionParser::ParseStatus::SyntaxError)
@ -169,9 +149,12 @@ void MappingButton::UpdateIndicator()
setFont(f); setFont(f);
} }
void MappingButton::ConfigChanged() void MappingButton::StartMapping()
{ {
setText(RefToDisplayString(m_reference)); // Focus just makes it more clear which button is currently being mapped.
setFocus();
setText(tr("[ Press Now ]"));
QtUtils::InstallKeyboardBlocker(this, this, &MappingButton::ConfigChanged);
} }
void MappingButton::mouseReleaseEvent(QMouseEvent* event) void MappingButton::mouseReleaseEvent(QMouseEvent* event)
@ -189,3 +172,8 @@ void MappingButton::mouseReleaseEvent(QMouseEvent* event)
return; return;
} }
} }
ControlReference* MappingButton::GetControlReference()
{
return m_reference;
}

View File

@ -3,11 +3,11 @@
#pragma once #pragma once
#include "Common/Flag.h"
#include "DolphinQt/QtUtils/ElidedButton.h" #include "DolphinQt/QtUtils/ElidedButton.h"
class ControlReference; class ControlReference;
class MappingWidget; class MappingWidget;
class MappingWindow;
class QEvent; class QEvent;
class QMouseEvent; class QMouseEvent;
@ -18,16 +18,21 @@ public:
MappingButton(MappingWidget* widget, ControlReference* ref, bool indicator); MappingButton(MappingWidget* widget, ControlReference* ref, bool indicator);
bool IsInput() const; bool IsInput() const;
ControlReference* GetControlReference();
void StartMapping();
signals:
void ConfigChanged();
private: private:
void Clear(); void Clear();
void UpdateIndicator(); void UpdateIndicator();
void ConfigChanged();
void AdvancedPressed(); void AdvancedPressed();
void Clicked(); void Clicked();
void mouseReleaseEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override;
MappingWidget* m_parent; MappingWindow* const m_mapping_window;
ControlReference* m_reference; ControlReference* const m_reference;
bool m_is_mapping = false;
}; };

View File

@ -3,19 +3,18 @@
#include "DolphinQt/Config/Mapping/MappingCommon.h" #include "DolphinQt/Config/Mapping/MappingCommon.h"
#include <chrono> #include <deque>
#include <memory>
#include <QApplication>
#include <QPushButton>
#include <QRegularExpression>
#include <QString>
#include <QTimer> #include <QTimer>
#include "DolphinQt/QtUtils/BlockUserInputFilter.h" #include "DolphinQt/Config/Mapping/MappingButton.h"
#include "InputCommon/ControlReference/ControlReference.h" #include "DolphinQt/Config/Mapping/MappingWindow.h"
#include "InputCommon/ControllerInterface/MappingCommon.h"
#include "Common/Thread.h" #include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h"
#include "InputCommon/ControllerInterface/MappingCommon.h"
namespace MappingCommon namespace MappingCommon
{ {
@ -23,65 +22,128 @@ constexpr auto INPUT_DETECT_INITIAL_TIME = std::chrono::seconds(3);
constexpr auto INPUT_DETECT_CONFIRMATION_TIME = std::chrono::milliseconds(0); constexpr auto INPUT_DETECT_CONFIRMATION_TIME = std::chrono::milliseconds(0);
constexpr auto INPUT_DETECT_MAXIMUM_TIME = std::chrono::seconds(5); constexpr auto INPUT_DETECT_MAXIMUM_TIME = std::chrono::seconds(5);
constexpr auto OUTPUT_TEST_TIME = std::chrono::seconds(2); class MappingProcessor : public QWidget
QString DetectExpression(QPushButton* button, ciface::Core::DeviceContainer& device_container,
const std::vector<std::string>& device_strings,
const ciface::Core::DeviceQualifier& default_device,
ciface::MappingCommon::Quote quote)
{ {
const auto filter = new BlockUserInputFilter(button); public:
MappingProcessor(MappingWindow* parent) : QWidget{parent}, m_parent{parent}
{
using MW = MappingWindow;
using MP = MappingProcessor;
button->installEventFilter(filter); connect(parent, &MW::Update, this, &MP::ProcessMappingButtons);
button->grabKeyboard(); connect(parent, &MW::ConfigChanged, this, &MP::CancelMapping);
button->grabMouse();
const auto old_text = button->text(); connect(parent, &MW::UnQueueInputDetection, this, &MP::UnQueueInputDetection);
button->setText(QStringLiteral("...")); connect(parent, &MW::QueueInputDetection, this, &MP::QueueInputDetection);
connect(parent, &MW::CancelMapping, this, &MP::CancelMapping);
// The button text won't be updated if we don't process events here m_input_detection_start_timer = new QTimer(this);
QApplication::processEvents(); m_input_detection_start_timer->setSingleShot(true);
connect(m_input_detection_start_timer, &QTimer::timeout, this, &MP::StartInputDetection);
}
// Avoid that the button press itself is registered as an event void StartInputDetection()
Common::SleepCurrentThread(50); {
const auto& default_device = m_parent->GetController()->GetDefaultDevice();
auto& button = m_clicked_mapping_buttons.front();
auto detections = button->StartMapping();
device_container.DetectInput(device_strings, INPUT_DETECT_INITIAL_TIME,
INPUT_DETECT_CONFIRMATION_TIME, INPUT_DETECT_MAXIMUM_TIME);
ciface::MappingCommon::RemoveSpuriousTriggerCombinations(&detections); std::vector device_strings{default_device.ToString()};
if (m_parent->IsMappingAllDevices())
device_strings = g_controller_interface.GetAllDeviceStrings();
const auto timer = new QTimer(button); m_input_detector = std::make_unique<ciface::Core::InputDetector>();
const auto lock = m_parent->GetController()->GetStateLock();
m_input_detector->Start(g_controller_interface, device_strings);
}
timer->setSingleShot(true); void ProcessMappingButtons()
{
if (!m_input_detector)
return;
button->connect(timer, &QTimer::timeout, [button, filter] { m_input_detector->Update(INPUT_DETECT_INITIAL_TIME, INPUT_DETECT_CONFIRMATION_TIME,
button->releaseMouse(); INPUT_DETECT_MAXIMUM_TIME);
button->releaseKeyboard();
button->removeEventFilter(filter);
});
// Prevent mappings of "space", "return", or mouse clicks from re-activating detection. if (m_input_detector->IsComplete())
timer->start(500); {
auto detections = m_input_detector->TakeResults();
ciface::MappingCommon::RemoveSpuriousTriggerCombinations(&detections);
button->setText(old_text); // No inputs detected. Cancel this and any other queued mappings.
if (detections.empty())
{
CancelMapping();
return;
}
return QString::fromStdString(BuildExpression(detections, default_device, quote)); const auto& default_device = m_parent->GetController()->GetDefaultDevice();
} auto& button = m_clicked_mapping_buttons.front();
auto* const control_reference = button->GetControlReference();
void TestOutput(QPushButton* button, OutputReference* reference) control_reference->SetExpression(
BuildExpression(detections, default_device, ciface::MappingCommon::Quote::On));
m_parent->Save();
m_parent->GetController()->UpdateSingleControlReference(g_controller_interface,
control_reference);
UnQueueInputDetection(button);
}
}
void UpdateInputDetectionStartTimer()
{
m_input_detector.reset();
if (m_clicked_mapping_buttons.empty())
m_input_detection_start_timer->stop();
else
m_input_detection_start_timer->start(INPUT_DETECT_INITIAL_DELAY);
}
void UnQueueInputDetection(MappingButton* button)
{
std::erase(m_clicked_mapping_buttons, button);
button->ConfigChanged();
UpdateInputDetectionStartTimer();
}
void QueueInputDetection(MappingButton* button)
{
// UnQueue if already queued.
if (std::erase(m_clicked_mapping_buttons, button))
{
button->ConfigChanged();
UpdateInputDetectionStartTimer();
return;
}
button->setText(QStringLiteral("[ ... ]"));
m_clicked_mapping_buttons.push_back(button);
UpdateInputDetectionStartTimer();
}
void CancelMapping()
{
// Signal buttons to take on their proper input expression text.
for (auto* button : m_clicked_mapping_buttons)
button->ConfigChanged();
m_clicked_mapping_buttons = {};
UpdateInputDetectionStartTimer();
}
private:
std::deque<MappingButton*> m_clicked_mapping_buttons;
std::unique_ptr<ciface::Core::InputDetector> m_input_detector;
QTimer* m_input_detection_start_timer;
MappingWindow* const m_parent;
};
void CreateMappingProcessor(MappingWindow* window)
{ {
const auto old_text = button->text(); new MappingProcessor{window};
button->setText(QStringLiteral("..."));
// The button text won't be updated if we don't process events here
QApplication::processEvents();
reference->State(1.0);
std::this_thread::sleep_for(OUTPUT_TEST_TIME);
reference->State(0.0);
button->setText(old_text);
} }
} // namespace MappingCommon } // namespace MappingCommon

View File

@ -3,23 +3,14 @@
#pragma once #pragma once
#include <string> #include <chrono>
#include <vector>
#include "InputCommon/ControllerInterface/CoreDevice.h" class MappingWindow;
#include "InputCommon/ControllerInterface/MappingCommon.h"
class QString;
class OutputReference;
class QPushButton;
namespace MappingCommon namespace MappingCommon
{ {
QString DetectExpression(QPushButton* button, ciface::Core::DeviceContainer& device_container, // A slight delay improves behavior when "clicking" the detect button via key-press.
const std::vector<std::string>& device_strings, static constexpr auto INPUT_DETECT_INITIAL_DELAY = std::chrono::milliseconds{100};
const ciface::Core::DeviceQualifier& default_device,
ciface::MappingCommon::Quote quote = ciface::MappingCommon::Quote::On);
void TestOutput(QPushButton* button, OutputReference* reference);
void CreateMappingProcessor(MappingWindow*);
} // namespace MappingCommon } // namespace MappingCommon

View File

@ -20,7 +20,6 @@
#include "DolphinQt/Config/Mapping/MappingWindow.h" #include "DolphinQt/Config/Mapping/MappingWindow.h"
#include "DolphinQt/QtUtils/SetWindowDecorations.h" #include "DolphinQt/QtUtils/SetWindowDecorations.h"
#include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerEmu/Control/Control.h" #include "InputCommon/ControllerEmu/Control/Control.h"
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h" #include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
#include "InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h" #include "InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h"
@ -340,10 +339,10 @@ MappingWidget::CreateSettingAdvancedMappingButton(ControllerEmu::NumericSettingB
setting.SetExpressionFromValue(); setting.SetExpressionFromValue();
// Ensure the UI has the game-controller indicator while editing the expression. // Ensure the UI has the game-controller indicator while editing the expression.
// And cancel in-progress mappings.
ConfigChanged(); ConfigChanged();
IOWindow io(this, GetController(), &setting.GetInputReference(), IOWindow::Type::Input); IOWindow io(GetParent(), GetController(), &setting.GetInputReference(), IOWindow::Type::Input);
SetQWidgetWindowDecorations(&io);
io.exec(); io.exec();
setting.SimplifyIfPossible(); setting.SimplifyIfPossible();

View File

@ -3,15 +3,10 @@
#pragma once #pragma once
#include <memory>
#include <vector>
#include <QString> #include <QString>
#include <QWidget> #include <QWidget>
class ControlGroupBox;
class InputConfig; class InputConfig;
class MappingButton;
class MappingNumeric; class MappingNumeric;
class MappingWindow; class MappingWindow;
class QFormLayout; class QFormLayout;

View File

@ -41,6 +41,7 @@
#include "DolphinQt/Config/Mapping/HotkeyTAS.h" #include "DolphinQt/Config/Mapping/HotkeyTAS.h"
#include "DolphinQt/Config/Mapping/HotkeyUSBEmu.h" #include "DolphinQt/Config/Mapping/HotkeyUSBEmu.h"
#include "DolphinQt/Config/Mapping/HotkeyWii.h" #include "DolphinQt/Config/Mapping/HotkeyWii.h"
#include "DolphinQt/Config/Mapping/MappingCommon.h"
#include "DolphinQt/Config/Mapping/WiimoteEmuExtension.h" #include "DolphinQt/Config/Mapping/WiimoteEmuExtension.h"
#include "DolphinQt/Config/Mapping/WiimoteEmuExtensionMotionInput.h" #include "DolphinQt/Config/Mapping/WiimoteEmuExtensionMotionInput.h"
#include "DolphinQt/Config/Mapping/WiimoteEmuExtensionMotionSimulation.h" #include "DolphinQt/Config/Mapping/WiimoteEmuExtensionMotionSimulation.h"
@ -93,6 +94,8 @@ MappingWindow::MappingWindow(QWidget* parent, Type type, int port_num)
[] { HotkeyManagerEmu::Enable(true); }); [] { HotkeyManagerEmu::Enable(true); });
filter->connect(filter, &WindowActivationEventFilter::windowActivated, filter->connect(filter, &WindowActivationEventFilter::windowActivated,
[] { HotkeyManagerEmu::Enable(false); }); [] { HotkeyManagerEmu::Enable(false); });
MappingCommon::CreateMappingProcessor(this);
} }
void MappingWindow::CreateDevicesLayout() void MappingWindow::CreateDevicesLayout()
@ -185,9 +188,8 @@ void MappingWindow::CreateMainLayout()
void MappingWindow::ConnectWidgets() void MappingWindow::ConnectWidgets()
{ {
connect(&Settings::Instance(), &Settings::DevicesChanged, this, connect(&Settings::Instance(), &Settings::DevicesChanged, this, &MappingWindow::ConfigChanged);
&MappingWindow::OnGlobalDevicesChanged); connect(this, &MappingWindow::ConfigChanged, this, &MappingWindow::UpdateDeviceList);
connect(this, &MappingWindow::ConfigChanged, this, &MappingWindow::OnGlobalDevicesChanged);
connect(m_devices_combo, &QComboBox::currentIndexChanged, this, &MappingWindow::OnSelectDevice); connect(m_devices_combo, &QComboBox::currentIndexChanged, this, &MappingWindow::OnSelectDevice);
connect(m_reset_clear, &QPushButton::clicked, this, &MappingWindow::OnClearFieldsPressed); connect(m_reset_clear, &QPushButton::clicked, this, &MappingWindow::OnClearFieldsPressed);
@ -203,6 +205,8 @@ void MappingWindow::ConnectWidgets()
// We currently use the "Close" button as an "Accept" button so we must save on reject. // We currently use the "Close" button as an "Accept" button so we must save on reject.
connect(this, &QDialog::rejected, [this] { emit Save(); }); connect(this, &QDialog::rejected, [this] { emit Save(); });
connect(m_button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(m_button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(m_tab_widget, &QTabWidget::currentChanged, this, &MappingWindow::CancelMapping);
} }
void MappingWindow::UpdateProfileIndex() void MappingWindow::UpdateProfileIndex()
@ -345,6 +349,8 @@ void MappingWindow::OnSelectDevice(int)
const auto device = m_devices_combo->currentData().toString().toStdString(); const auto device = m_devices_combo->currentData().toString().toStdString();
m_controller->SetDefaultDevice(device); m_controller->SetDefaultDevice(device);
emit ConfigChanged();
m_controller->UpdateReferences(g_controller_interface); m_controller->UpdateReferences(g_controller_interface);
} }
@ -358,7 +364,7 @@ void MappingWindow::RefreshDevices()
g_controller_interface.RefreshDevices(); g_controller_interface.RefreshDevices();
} }
void MappingWindow::OnGlobalDevicesChanged() void MappingWindow::UpdateDeviceList()
{ {
const QSignalBlocker blocker(m_devices_combo); const QSignalBlocker blocker(m_devices_combo);

View File

@ -5,9 +5,6 @@
#include <QDialog> #include <QDialog>
#include <QString> #include <QString>
#include <memory>
#include "InputCommon/ControllerInterface/CoreDevice.h"
namespace ControllerEmu namespace ControllerEmu
{ {
@ -15,6 +12,8 @@ class EmulatedController;
} }
class InputConfig; class InputConfig;
class MappingButton;
class QComboBox; class QComboBox;
class QDialogButtonBox; class QDialogButtonBox;
class QEvent; class QEvent;
@ -58,10 +57,14 @@ public:
signals: signals:
// Emitted when config has changed so widgets can update to reflect the change. // Emitted when config has changed so widgets can update to reflect the change.
void ConfigChanged(); void ConfigChanged();
// Emitted at 30hz for real-time indicators to be updated. // Emitted at INDICATOR_UPDATE_FREQ Hz for real-time indicators to be updated.
void Update(); void Update();
void Save(); void Save();
void UnQueueInputDetection(MappingButton*);
void QueueInputDetection(MappingButton*);
void CancelMapping();
private: private:
void SetMappingType(Type type); void SetMappingType(Type type);
void CreateDevicesLayout(); void CreateDevicesLayout();
@ -82,11 +85,11 @@ private:
void UpdateProfileIndex(); void UpdateProfileIndex();
void UpdateProfileButtonState(); void UpdateProfileButtonState();
void PopulateProfileSelection(); void PopulateProfileSelection();
void UpdateDeviceList();
void OnDefaultFieldsPressed(); void OnDefaultFieldsPressed();
void OnClearFieldsPressed(); void OnClearFieldsPressed();
void OnSelectDevice(int index); void OnSelectDevice(int index);
void OnGlobalDevicesChanged();
ControllerEmu::EmulatedController* m_controller = nullptr; ControllerEmu::EmulatedController* m_controller = nullptr;

View File

@ -39,12 +39,6 @@
<string>English</string> <string>English</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${DOLPHIN_WC_DESCRIBE}</string>
<key>CFBundleLongVersionString</key>
<string>${DOLPHIN_WC_REVISION}</string>
<key>CFBundleVersion</key>
<string>${DOLPHIN_VERSION_MAJOR}.${DOLPHIN_VERSION_MINOR}</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>Licensed under GPL version 2 or later (GPLv2+)</string> <string>Licensed under GPL version 2 or later (GPLv2+)</string>
<key>LSApplicationCategoryType</key> <key>LSApplicationCategoryType</key>

View File

@ -3,12 +3,34 @@
#include "DolphinQt/QtUtils/BlockUserInputFilter.h" #include "DolphinQt/QtUtils/BlockUserInputFilter.h"
#include <QEvent> #include <chrono>
bool BlockUserInputFilter::eventFilter(QObject* object, QEvent* event) #include <QEvent>
#include <QTimer>
namespace QtUtils
{ {
const QEvent::Type event_type = event->type();
return event_type == QEvent::KeyPress || event_type == QEvent::KeyRelease || // Leave filter active for a bit to prevent Return/Space detection from reactivating the button.
event_type == QEvent::MouseButtonPress || event_type == QEvent::MouseButtonRelease || constexpr auto REMOVAL_DELAY = std::chrono::milliseconds{100};
event_type == QEvent::MouseButtonDblClick;
BlockKeyboardInputFilter::BlockKeyboardInputFilter(QObject* parent) : QObject{parent}
{
parent->installEventFilter(this);
} }
void BlockKeyboardInputFilter::ScheduleRemoval()
{
auto* const timer = new QTimer(this);
timer->setSingleShot(true);
connect(timer, &QTimer::timeout, [this] { delete this; });
timer->start(REMOVAL_DELAY);
}
bool BlockKeyboardInputFilter::eventFilter(QObject* object, QEvent* event)
{
const auto event_type = event->type();
return event_type == QEvent::KeyPress || event_type == QEvent::KeyRelease;
}
} // namespace QtUtils

View File

@ -5,14 +5,26 @@
#include <QObject> #include <QObject>
class QEvent; namespace QtUtils
{
class BlockUserInputFilter : public QObject class BlockKeyboardInputFilter : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
using QObject::QObject; BlockKeyboardInputFilter(QObject* parent);
void ScheduleRemoval();
private: private:
bool eventFilter(QObject* object, QEvent* event) override; bool eventFilter(QObject* object, QEvent* event) override;
}; };
template <typename T>
void InstallKeyboardBlocker(QObject* obj, T* removal_signal_object, void (T::*removal_signal)())
{
removal_signal_object->connect(removal_signal_object, removal_signal,
new QtUtils::BlockKeyboardInputFilter{obj},
&QtUtils::BlockKeyboardInputFilter::ScheduleRemoval);
}
} // namespace QtUtils

View File

@ -345,6 +345,20 @@ auto DeviceContainer::DetectInput(const std::vector<std::string>& device_strings
std::chrono::milliseconds confirmation_wait, std::chrono::milliseconds confirmation_wait,
std::chrono::milliseconds maximum_wait) const std::chrono::milliseconds maximum_wait) const
-> std::vector<InputDetection> -> std::vector<InputDetection>
{
InputDetector input_detector;
input_detector.Start(*this, device_strings);
while (!input_detector.IsComplete())
{
Common::SleepCurrentThread(10);
input_detector.Update(initial_wait, confirmation_wait, maximum_wait);
}
return input_detector.TakeResults();
}
struct InputDetector::Impl
{ {
struct InputState struct InputState
{ {
@ -355,7 +369,7 @@ auto DeviceContainer::DetectInput(const std::vector<std::string>& device_strings
ControlState last_state = initial_state; ControlState last_state = initial_state;
MathUtil::RunningVariance<ControlState> stats; MathUtil::RunningVariance<ControlState> stats;
// Prevent multiiple detections until after release. // Prevent multiple detections until after release.
bool is_ready = true; bool is_ready = true;
void Update() void Update()
@ -392,18 +406,32 @@ auto DeviceContainer::DetectInput(const std::vector<std::string>& device_strings
std::vector<InputState> input_states; std::vector<InputState> input_states;
}; };
// Acquire devices and initial input states.
std::vector<DeviceState> device_states; std::vector<DeviceState> device_states;
};
InputDetector::InputDetector() : m_start_time{}, m_state{}
{
}
void InputDetector::Start(const DeviceContainer& container,
const std::vector<std::string>& device_strings)
{
m_start_time = Clock::now();
m_detections = {};
m_state = std::make_unique<Impl>();
// Acquire devices and initial input states.
for (const auto& device_string : device_strings) for (const auto& device_string : device_strings)
{ {
DeviceQualifier dq; DeviceQualifier dq;
dq.FromString(device_string); dq.FromString(device_string);
auto device = FindDevice(dq); auto device = container.FindDevice(dq);
if (!device) if (!device)
continue; continue;
std::vector<InputState> input_states; std::vector<Impl::InputState> input_states;
for (auto* input : device->Inputs()) for (auto* input : device->Inputs())
{ {
@ -413,38 +441,42 @@ auto DeviceContainer::DetectInput(const std::vector<std::string>& device_strings
// Undesirable axes will have negative values here when trying to map a // Undesirable axes will have negative values here when trying to map a
// "FullAnalogSurface". // "FullAnalogSurface".
input_states.push_back(InputState{input}); input_states.push_back(Impl::InputState{input});
} }
if (!input_states.empty()) if (!input_states.empty())
device_states.emplace_back(DeviceState{std::move(device), std::move(input_states)}); {
m_state->device_states.emplace_back(
Impl::DeviceState{std::move(device), std::move(input_states)});
}
} }
if (device_states.empty()) // If no inputs were found via the supplied device strings, immediately complete.
return {}; if (m_state->device_states.empty())
m_state.reset();
}
std::vector<InputDetection> detections; void InputDetector::Update(std::chrono::milliseconds initial_wait,
std::chrono::milliseconds confirmation_wait,
const auto start_time = Clock::now(); std::chrono::milliseconds maximum_wait)
while (true) {
if (m_state)
{ {
const auto now = Clock::now(); const auto now = Clock::now();
const auto elapsed_time = now - start_time; const auto elapsed_time = now - m_start_time;
if (elapsed_time >= maximum_wait || (detections.empty() && elapsed_time >= initial_wait) || if (elapsed_time >= maximum_wait || (m_detections.empty() && elapsed_time >= initial_wait) ||
(!detections.empty() && detections.back().release_time.has_value() && (!m_detections.empty() && m_detections.back().release_time.has_value() &&
now >= *detections.back().release_time + confirmation_wait)) now >= *m_detections.back().release_time + confirmation_wait))
{ {
break; m_state.reset();
return;
} }
Common::SleepCurrentThread(10); for (auto& device_state : m_state->device_states)
for (auto& device_state : device_states)
{ {
for (std::size_t i = 0; i != device_state.input_states.size(); ++i) for (auto& input_state : device_state.input_states)
{ {
auto& input_state = device_state.input_states[i];
input_state.Update(); input_state.Update();
if (input_state.IsPressed()) if (input_state.IsPressed())
@ -456,26 +488,42 @@ auto DeviceContainer::DetectInput(const std::vector<std::string>& device_strings
const auto smoothness = const auto smoothness =
1 / std::sqrt(input_state.stats.Variance() / input_state.stats.Mean()); 1 / std::sqrt(input_state.stats.Variance() / input_state.stats.Mean());
InputDetection new_detection; Detection new_detection;
new_detection.device = device_state.device; new_detection.device = device_state.device;
new_detection.input = input_state.input; new_detection.input = input_state.input;
new_detection.press_time = Clock::now(); new_detection.press_time = Clock::now();
new_detection.smoothness = smoothness; new_detection.smoothness = smoothness;
// We found an input. Add it to our detections. // We found an input. Add it to our detections.
detections.emplace_back(std::move(new_detection)); m_detections.emplace_back(std::move(new_detection));
} }
} }
} }
// Check for any releases of our detected inputs. // Check for any releases of our detected inputs.
for (auto& d : detections) for (auto& d : m_detections)
{ {
if (!d.release_time.has_value() && d.input->GetState() < (1 - INPUT_DETECT_THRESHOLD)) if (!d.release_time.has_value() && d.input->GetState() < (1 - INPUT_DETECT_THRESHOLD))
d.release_time = Clock::now(); d.release_time = Clock::now();
} }
} }
return detections;
} }
InputDetector::~InputDetector() = default;
bool InputDetector::IsComplete() const
{
return !m_state;
}
auto InputDetector::GetResults() const -> const std::vector<Detection>&
{
return m_detections;
}
auto InputDetector::TakeResults() -> std::vector<Detection>
{
return std::move(m_detections);
}
} // namespace ciface::Core } // namespace ciface::Core

View File

@ -245,5 +245,32 @@ protected:
mutable std::recursive_mutex m_devices_mutex; mutable std::recursive_mutex m_devices_mutex;
std::vector<std::shared_ptr<Device>> m_devices; std::vector<std::shared_ptr<Device>> m_devices;
}; };
class InputDetector
{
public:
using Detection = DeviceContainer::InputDetection;
InputDetector();
~InputDetector();
void Start(const DeviceContainer& container, const std::vector<std::string>& device_strings);
void Update(std::chrono::milliseconds initial_wait, std::chrono::milliseconds confirmation_wait,
std::chrono::milliseconds maximum_wait);
bool IsComplete() const;
const std::vector<Detection>& GetResults() const;
// move-return'd to prevent copying.
std::vector<Detection> TakeResults();
private:
struct Impl;
Clock::time_point m_start_time;
std::vector<Detection> m_detections;
std::unique_ptr<Impl> m_state;
};
} // namespace Core } // namespace Core
} // namespace ciface } // namespace ciface

View File

@ -16,7 +16,7 @@ add_dependencies(MacUpdater dolphin_scmrev)
set_target_properties(MacUpdater PROPERTIES set_target_properties(MacUpdater PROPERTIES
MACOSX_BUNDLE true MACOSX_BUNDLE true
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/Info.plist" MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in"
OUTPUT_NAME "Dolphin Updater") OUTPUT_NAME "Dolphin Updater")
target_compile_options(MacUpdater PRIVATE -x objective-c++) target_compile_options(MacUpdater PRIVATE -x objective-c++)
@ -53,6 +53,9 @@ foreach(sb ${STORYBOARDS})
COMMENT "Compiling Storyboard ${sb}...") COMMENT "Compiling Storyboard ${sb}...")
endforeach() endforeach()
include(DolphinInjectVersionInfo)
dolphin_inject_version_info(MacUpdater)
if(NOT SKIP_POSTPROCESS_BUNDLE) if(NOT SKIP_POSTPROCESS_BUNDLE)
# Update library references to make the bundle portable # Update library references to make the bundle portable
include(DolphinPostprocessBundle) include(DolphinPostprocessBundle)

View File

@ -16,10 +16,6 @@
<string>Dolphin Updater</string> <string>Dolphin Updater</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${DOLPHIN_WC_DESCRIBE}</string>
<key>CFBundleVersion</key>
<string>${DOLPHIN_VERSION_MAJOR}.${DOLPHIN_VERSION_MINOR}</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string> <string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- This plist file is merged with the application's Info.plist to set its version info. -->
<key>CFBundleShortVersionString</key>
<string>${DOLPHIN_WC_DESCRIBE}</string>
<key>CFBundleLongVersionString</key>
<string>${DOLPHIN_WC_REVISION}</string>
<key>CFBundleVersion</key>
<string>${DOLPHIN_VERSION_MAJOR}.${DOLPHIN_VERSION_MINOR}</string>
</dict>
</plist>

View File

@ -259,6 +259,10 @@ void VideoBackend::Shutdown()
void VideoBackend::PrepareWindow(WindowSystemInfo& wsi) void VideoBackend::PrepareWindow(WindowSystemInfo& wsi)
{ {
#if defined(VK_USE_PLATFORM_METAL_EXT) #if defined(VK_USE_PLATFORM_METAL_EXT)
// We only need to manually create the CAMetalLayer on macOS.
if (wsi.type != WindowSystemType::MacOS)
return;
// This is kinda messy, but it avoids having to write Objective C++ just to create a metal layer. // This is kinda messy, but it avoids having to write Objective C++ just to create a metal layer.
id view = reinterpret_cast<id>(wsi.render_surface); id view = reinterpret_cast<id>(wsi.render_surface);
Class clsCAMetalLayer = objc_getClass("CAMetalLayer"); Class clsCAMetalLayer = objc_getClass("CAMetalLayer");

View File

@ -77,11 +77,6 @@ std::pair<std::string, bool> GetNameArbPair(const TextureInfo& texture_info)
} }
} // namespace } // namespace
void HiresTexture::Init()
{
Update();
}
void HiresTexture::Shutdown() void HiresTexture::Shutdown()
{ {
Clear(); Clear();

View File

@ -22,7 +22,6 @@ std::set<std::string> GetTextureDirectoriesWithGameId(const std::string& root_di
class HiresTexture class HiresTexture
{ {
public: public:
static void Init();
static void Update(); static void Update();
static void Clear(); static void Clear();
static void Shutdown(); static void Shutdown();

View File

@ -137,43 +137,40 @@ void PostProcessingConfiguration::LoadOptions(const std::string& code)
std::vector<GLSLStringOption> option_strings; std::vector<GLSLStringOption> option_strings;
GLSLStringOption* current_strings = nullptr; GLSLStringOption* current_strings = nullptr;
while (!in.eof()) std::string line_str;
while (std::getline(in, line_str))
{ {
std::string line_str; std::string_view line = line_str;
if (std::getline(in, line_str))
{
std::string_view line = line_str;
#ifndef _WIN32 #ifndef _WIN32
// Check for CRLF eol and convert it to LF // Check for CRLF eol and convert it to LF
if (!line.empty() && line.at(line.size() - 1) == '\r') if (!line.empty() && line.at(line.size() - 1) == '\r')
line.remove_suffix(1); line.remove_suffix(1);
#endif #endif
if (!line.empty()) if (!line.empty())
{
if (line[0] == '[')
{ {
if (line[0] == '[') size_t endpos = line.find("]");
{
size_t endpos = line.find("]");
if (endpos != std::string::npos) if (endpos != std::string::npos)
{ {
// New section! // New section!
std::string_view sub = line.substr(1, endpos - 1); std::string_view sub = line.substr(1, endpos - 1);
option_strings.push_back({std::string(sub)}); option_strings.push_back({std::string(sub)});
current_strings = &option_strings.back(); current_strings = &option_strings.back();
}
} }
else }
else
{
if (current_strings)
{ {
if (current_strings) std::string key, value;
{ Common::IniFile::ParseLine(line, &key, &value);
std::string key, value;
Common::IniFile::ParseLine(line, &key, &value);
if (!(key.empty() && value.empty())) if (!(key.empty() && value.empty()))
current_strings->m_options.emplace_back(key, value); current_strings->m_options.emplace_back(key, value);
}
} }
} }
} }

View File

@ -99,8 +99,6 @@ TextureCacheBase::TextureCacheBase()
TexDecoder_SetTexFmtOverlayOptions(m_backup_config.texfmt_overlay, TexDecoder_SetTexFmtOverlayOptions(m_backup_config.texfmt_overlay,
m_backup_config.texfmt_overlay_center); m_backup_config.texfmt_overlay_center);
HiresTexture::Init();
TMEM::InvalidateAll(); TMEM::InvalidateAll();
} }

View File

@ -24,7 +24,7 @@ protected:
} }
} }
void SetUp() void SetUp() override
{ {
if (m_parent_directory.empty()) if (m_parent_directory.empty())
{ {

View File

@ -44,7 +44,7 @@ TEST(TMDReader, Validity)
class TMDReaderTest : public ::testing::Test class TMDReaderTest : public ::testing::Test
{ {
protected: protected:
virtual void SetUp() = 0; void SetUp() override = 0;
// Common tests. // Common tests.
void TestGeneralInfo(); void TestGeneralInfo();

View File

@ -41,7 +41,7 @@ protected:
m_fs.reset(); m_fs.reset();
File::DeleteDirRecursively(m_profile_path); File::DeleteDirRecursively(m_profile_path);
} }
void SetUp() void SetUp() override
{ {
if (UserDirectoryCreationFailed()) if (UserDirectoryCreationFailed())
{ {