Compare commits
123 Commits
a87f817c3b
...
52401d6808
Author | SHA1 | Date |
---|---|---|
Jay M. White | 52401d6808 | |
Admiral H. Curtiss | 2c83a256ae | |
Jordan Woyak | c3f66e83e6 | |
OatmealDome | c344514ba2 | |
Admiral H. Curtiss | 3ea870ef8c | |
Admiral H. Curtiss | ede963d4db | |
Admiral H. Curtiss | b0e5ebc80d | |
JosJuice | da0a0c87c2 | |
Daniel Peukert | f28e134c88 | |
dreamsyntax | b9a2d89035 | |
Admiral H. Curtiss | 93fc5c02ac | |
Admiral H. Curtiss | 98a80239f1 | |
Admiral H. Curtiss | 75abda6a3a | |
Admiral H. Curtiss | d10cb9dfc4 | |
Tilka | e24e107b3c | |
Tilka | ffa312f8e8 | |
Admiral H. Curtiss | 0e5d7d0f2d | |
OatmealDome | 2633b84b98 | |
Admiral H. Curtiss | 8f9e3ffd83 | |
Admiral H. Curtiss | 22dc21cca4 | |
Admiral H. Curtiss | c567248b73 | |
Admiral H. Curtiss | 7133bfbb0e | |
OatmealDome | d525776ae6 | |
OatmealDome | 4dc368d8df | |
OatmealDome | 0b7f399436 | |
OatmealDome | b5918effeb | |
OatmealDome | 696b363f47 | |
Sintendo | 532e25be12 | |
JosJuice | b35f7af355 | |
JosJuice | eec2e2f07a | |
JosJuice | 43d5f61a60 | |
OatmealDome | 8d9ec2fde9 | |
JMC47 | 6b686be5f1 | |
Ferdinand Bachmann | b79bdb13c0 | |
Ferdinand Bachmann | 825092ad33 | |
Sepalani | bbf835b30b | |
Sepalani | 77e77863dc | |
Sepalani | 5778cb42db | |
Sepalani | 5c151c11ac | |
JMC47 | 9b3b6bea9d | |
Jordan Woyak | f12846a0e9 | |
Jordan Woyak | bc95c001c8 | |
JMC47 | f15a78ed38 | |
mitaclaw | 527841f1df | |
mitaclaw | d92c68e1de | |
mitaclaw | 110d32729e | |
mitaclaw | 6f10acea3f | |
Admiral H. Curtiss | b8921b1338 | |
Admiral H. Curtiss | c1c80adf1a | |
Jordan Woyak | 0938fca6e3 | |
TryTwo | 3d248d000f | |
CasualPokePlayer | c1698c93e2 | |
Sintendo | d81bfe94eb | |
Sintendo | c817b4779d | |
Sintendo | 14641b06fc | |
Sintendo | a4ba13b4c9 | |
Sintendo | d2bfa157dc | |
Sintendo | ad7dba5413 | |
Sintendo | 7410bc2025 | |
Sintendo | fa13457abb | |
Sintendo | 18dd3f69f1 | |
Sintendo | e54bfd6605 | |
Sintendo | 5cc9bde1c1 | |
Sintendo | 50d991780f | |
dreamsyntax | 07f712f8a0 | |
Tilka | 05cad38abc | |
Admiral H. Curtiss | 4fc50226c6 | |
Admiral H. Curtiss | 98ee3836e5 | |
JMC47 | 532a8621da | |
Niel Lebeck | bae4616dd1 | |
JosJuice | 6384ea97f1 | |
Gamer64 | 764bf314e1 | |
JMC47 | f9ce2b9d76 | |
TryTwo | 27ac5fa777 | |
JMC47 | c528a70e64 | |
JMC47 | a1d6aa7d3e | |
JosJuice | 5641b83d4e | |
Admiral H. Curtiss | bb8c0a795f | |
JMC47 | 1ba8541da9 | |
JMC47 | ac0d6cbaaa | |
OatmealDome | 01f6810a9d | |
mitaclaw | 2b0cd16c8c | |
mitaclaw | 140252ffc0 | |
mitaclaw | 860e6cf5cb | |
mitaclaw | 826e2bbf98 | |
Jordan Woyak | b4a1967310 | |
JosJuice | ad24ddb6bb | |
JosJuice | 84ab15e020 | |
Sintendo | d81213c4a5 | |
TryTwo | 9541bb6cf7 | |
TryTwo | ac129d318b | |
TryTwo | 84a937ae65 | |
JosJuice | 7a31c8f10b | |
TryTwo | 08df9a66e0 | |
JosJuice | e3bfff5cb6 | |
Sintendo | 065165f749 | |
OatmealDome | e05b033dd2 | |
OatmealDome | 3c27c38e71 | |
OatmealDome | e6f335bfcf | |
TellowKrinkle | 613c959bda | |
TellowKrinkle | 432d4a2a06 | |
JosJuice | 980a726313 | |
JosJuice | 71e9766519 | |
JosJuice | 9246bcad55 | |
JosJuice | 7fddd39d97 | |
JosJuice | bbe271eec6 | |
Jordan Woyak | bffc26da27 | |
Sintendo | e6fd843c0b | |
mitaclaw | 3d0d03b871 | |
mitaclaw | 5f3a8ff0de | |
mitaclaw | be0b13da97 | |
mitaclaw | 4fde0f2868 | |
mitaclaw | 0352f24a8e | |
JosJuice | dcf8ab0189 | |
Jared M. White | a277d66c50 | |
Jared M. White | d062310de0 | |
Jared M. White | af86eaad1a | |
Jared M. White | 619cd0aa38 | |
Jared M. White | e252c4a729 | |
Jared M. White | 4b53e9b52b | |
Jared M. White | 32020df8c0 | |
Jared M. White | 6184b8c0cb | |
JosJuice | 2fdeb85f0e |
|
@ -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()
|
|
@ -19,28 +19,27 @@ function(dolphin_make_imported_target_if_missing target lib)
|
|||
endif()
|
||||
endfunction()
|
||||
|
||||
function(dolphin_optional_system_library library)
|
||||
function(dolphin_optional_system_library out_use_system library)
|
||||
string(TOUPPER ${library} upperlib)
|
||||
set(USE_SYSTEM_${upperlib} "" CACHE STRING "Use system ${library} instead of bundled. ON - Always use system and fail if unavailable, OFF - Always use bundled, AUTO - Use system if available, otherwise use bundled, blank - Delegate to USE_SYSTEM_LIBS. Default is blank.")
|
||||
if("${USE_SYSTEM_${upperlib}}" STREQUAL "")
|
||||
if(APPROVED_VENDORED_DEPENDENCIES)
|
||||
string(TOLOWER ${library} lowerlib)
|
||||
if(lowerlib IN_LIST APPROVED_VENDORED_DEPENDENCIES)
|
||||
set(RESOLVED_USE_SYSTEM_${upperlib} AUTO PARENT_SCOPE)
|
||||
set(${out_use_system} AUTO PARENT_SCOPE)
|
||||
else()
|
||||
set(RESOLVED_USE_SYSTEM_${upperlib} ON PARENT_SCOPE)
|
||||
set(${out_use_system} ON PARENT_SCOPE)
|
||||
endif()
|
||||
else()
|
||||
set(RESOLVED_USE_SYSTEM_${upperlib} ${USE_SYSTEM_LIBS} PARENT_SCOPE)
|
||||
set(${out_use_system} ${USE_SYSTEM_LIBS} PARENT_SCOPE)
|
||||
endif()
|
||||
else()
|
||||
set(RESOLVED_USE_SYSTEM_${upperlib} ${USE_SYSTEM_${upperlib}} PARENT_SCOPE)
|
||||
set(${out_use_system} ${USE_SYSTEM_${upperlib}} PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(dolphin_add_bundled_library library bundled_path)
|
||||
string(TOUPPER ${library} upperlib)
|
||||
if (${RESOLVED_USE_SYSTEM_${upperlib}} STREQUAL "AUTO")
|
||||
function(dolphin_add_bundled_library library use_system bundled_path)
|
||||
if (${use_system} STREQUAL "AUTO")
|
||||
message(STATUS "No system ${library} was found. Using static ${library} from Externals.")
|
||||
else()
|
||||
message(STATUS "Using static ${library} from Externals")
|
||||
|
@ -52,9 +51,9 @@ function(dolphin_add_bundled_library library bundled_path)
|
|||
endfunction()
|
||||
|
||||
function(dolphin_find_optional_system_library library bundled_path)
|
||||
dolphin_optional_system_library(${library})
|
||||
dolphin_optional_system_library(use_system ${library})
|
||||
string(TOUPPER ${library} upperlib)
|
||||
if(RESOLVED_USE_SYSTEM_${upperlib})
|
||||
if(use_system)
|
||||
find_package(${library} ${ARGN})
|
||||
# Yay for cmake packages being inconsistent
|
||||
if(DEFINED ${library}_FOUND)
|
||||
|
@ -62,7 +61,7 @@ function(dolphin_find_optional_system_library library bundled_path)
|
|||
else()
|
||||
set(prefix ${upperlib})
|
||||
endif()
|
||||
if((NOT ${found}) AND (NOT ${RESOLVED_USE_SYSTEM_${upperlib}} STREQUAL "AUTO"))
|
||||
if((NOT ${prefix}_FOUND) AND (NOT ${use_system} STREQUAL "AUTO"))
|
||||
message(FATAL_ERROR "No system ${library} was found. Please install it or set USE_SYSTEM_${upperlib} to AUTO or OFF.")
|
||||
endif()
|
||||
endif()
|
||||
|
@ -70,17 +69,17 @@ function(dolphin_find_optional_system_library library bundled_path)
|
|||
message(STATUS "Using system ${library}")
|
||||
set(${prefix}_TYPE "System" PARENT_SCOPE)
|
||||
else()
|
||||
dolphin_add_bundled_library(${library} ${bundled_path})
|
||||
dolphin_add_bundled_library(${library} ${use_system} ${bundled_path})
|
||||
set(${prefix}_TYPE "Bundled" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(dolphin_find_optional_system_library_pkgconfig library search alias bundled_path)
|
||||
dolphin_optional_system_library(${library})
|
||||
dolphin_optional_system_library(use_system ${library})
|
||||
string(TOUPPER ${library} upperlib)
|
||||
if(RESOLVED_USE_SYSTEM_${upperlib})
|
||||
if(use_system)
|
||||
pkg_search_module(${library} ${search} ${ARGN} IMPORTED_TARGET)
|
||||
if((NOT ${library}_FOUND) AND (NOT ${RESOLVED_USE_SYSTEM_${upperlib}} STREQUAL "AUTO"))
|
||||
if((NOT ${library}_FOUND) AND (NOT ${use_system} STREQUAL "AUTO"))
|
||||
message(FATAL_ERROR "No system ${library} was found. Please install it or set USE_SYSTEM_${upperlib} to AUTO or OFF.")
|
||||
endif()
|
||||
endif()
|
||||
|
@ -89,7 +88,7 @@ function(dolphin_find_optional_system_library_pkgconfig library search alias bun
|
|||
dolphin_alias_library(${alias} PkgConfig::${library})
|
||||
set(${library}_TYPE "System" PARENT_SCOPE)
|
||||
else()
|
||||
dolphin_add_bundled_library(${library} ${bundled_path})
|
||||
dolphin_add_bundled_library(${library} ${use_system} ${bundled_path})
|
||||
set(${library}_TYPE "Bundled" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
|
|
@ -65,6 +65,5 @@ endfunction()
|
|||
configure_source_file("Source/Core/Common/scmrev.h")
|
||||
|
||||
if(APPLE)
|
||||
configure_source_file("Source/Core/DolphinQt/Info.plist")
|
||||
configure_source_file("Source/Core/MacUpdater/Info.plist")
|
||||
configure_source_file("Source/Core/VersionInfo.plist")
|
||||
endif()
|
||||
|
|
|
@ -783,14 +783,9 @@ if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/Common/scmrev.h)
|
|||
endif()
|
||||
|
||||
if(APPLE)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/DolphinQt)
|
||||
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/DolphinQt/Info.plist)
|
||||
file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/DolphinQt/Info.plist)
|
||||
endif()
|
||||
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/MacUpdater)
|
||||
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/MacUpdater/Info.plist)
|
||||
file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/MacUpdater/Info.plist)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Source/Core)
|
||||
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/VersionInfo.plist)
|
||||
file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/VersionInfo.plist)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
|
@ -1,41 +1,41 @@
|
|||
# RZDK01 - The Legend of Zelda: Twilight Princess [Wii]
|
||||
|
||||
[OnFrame]
|
||||
# Add memory patches to be applied every frame here.
|
||||
$Hyrule Field Speed Hack
|
||||
0x80047DEC:dword:0x60000000
|
||||
0x80047E08:dword:0x60000000
|
||||
0x80047E20:dword:0x60000000
|
||||
0x80047E3C:dword:0x60000000
|
||||
|
||||
0x80047E40:dword:0x60000000
|
||||
0x80047E44:dword:0x60000000
|
||||
0x80047E48:dword:0x60000000
|
||||
0x80047E4C:dword:0x60000000
|
||||
0x80047E50:dword:0x60000000
|
||||
0x80047E54:dword:0x60000000
|
||||
0x80047E58:dword:0x60000000
|
||||
0x80047E5C:dword:0x60000000
|
||||
0x80047E60:dword:0x60000000
|
||||
0x80047E64:dword:0x60000000
|
||||
0x80047E68:dword:0x60000000
|
||||
0x80047E6C:dword:0x60000000
|
||||
0x80047E70:dword:0x60000000
|
||||
0x80047E74:dword:0x60000000
|
||||
0x80047E78:dword:0x60000000
|
||||
0x80047E7C:dword:0x60000000
|
||||
0x80047E80:dword:0x60000000
|
||||
0x80047E84:dword:0x60000000
|
||||
0x80047E88:dword:0x60000000
|
||||
0x80047E8C:dword:0x60000000
|
||||
|
||||
0x80047E94:dword:0x60000000
|
||||
0x80047EB0:dword:0x60000000
|
||||
0x80047EC8:dword:0x60000000
|
||||
0x80047EE4:dword:0x60000000
|
||||
|
||||
[Patches_RetroAchievements_Verified]
|
||||
$Hyrule Field Speed Hack
|
||||
|
||||
[ActionReplay]
|
||||
# Add action replay cheats here.
|
||||
# RZDK01 - The Legend of Zelda: Twilight Princess [Wii]
|
||||
|
||||
[OnFrame]
|
||||
# Add memory patches to be applied every frame here.
|
||||
$Hyrule Field Speed Hack
|
||||
0x80047DEC:dword:0x60000000
|
||||
0x80047E08:dword:0x60000000
|
||||
0x80047E20:dword:0x60000000
|
||||
0x80047E3C:dword:0x60000000
|
||||
|
||||
0x80047E40:dword:0x60000000
|
||||
0x80047E44:dword:0x60000000
|
||||
0x80047E48:dword:0x60000000
|
||||
0x80047E4C:dword:0x60000000
|
||||
0x80047E50:dword:0x60000000
|
||||
0x80047E54:dword:0x60000000
|
||||
0x80047E58:dword:0x60000000
|
||||
0x80047E5C:dword:0x60000000
|
||||
0x80047E60:dword:0x60000000
|
||||
0x80047E64:dword:0x60000000
|
||||
0x80047E68:dword:0x60000000
|
||||
0x80047E6C:dword:0x60000000
|
||||
0x80047E70:dword:0x60000000
|
||||
0x80047E74:dword:0x60000000
|
||||
0x80047E78:dword:0x60000000
|
||||
0x80047E7C:dword:0x60000000
|
||||
0x80047E80:dword:0x60000000
|
||||
0x80047E84:dword:0x60000000
|
||||
0x80047E88:dword:0x60000000
|
||||
0x80047E8C:dword:0x60000000
|
||||
|
||||
0x80047E94:dword:0x60000000
|
||||
0x80047EB0:dword:0x60000000
|
||||
0x80047EC8:dword:0x60000000
|
||||
0x80047EE4:dword:0x60000000
|
||||
|
||||
[Patches_RetroAchievements_Verified]
|
||||
$Hyrule Field Speed Hack
|
||||
|
||||
[ActionReplay]
|
||||
# Add action replay cheats here.
|
||||
|
|
|
@ -1,62 +1,53 @@
|
|||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/curl/include)
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/curl/lib)
|
||||
set(BUILD_CURL_EXE OFF)
|
||||
set(BUILD_EXAMPLES OFF)
|
||||
set(BUILD_LIBCURL_DOCS OFF)
|
||||
set(BUILD_MISC_DOCS OFF)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
set(BUILD_STATIC_LIBS ON)
|
||||
set(BUILD_TESTING OFF)
|
||||
set(CURL_ENABLE_EXPORT_TARGET OFF)
|
||||
|
||||
set(CURL_LIBS MbedTLS::mbedtls zlibstatic)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
set(use_core_foundation ON)
|
||||
set(HTTP_ONLY ON)
|
||||
|
||||
find_library(SYSTEMCONFIGURATION_FRAMEWORK "SystemConfiguration")
|
||||
if(NOT SYSTEMCONFIGURATION_FRAMEWORK)
|
||||
message(FATAL_ERROR "SystemConfiguration framework not found")
|
||||
endif()
|
||||
set(CURL_USE_LIBPSL OFF)
|
||||
set(CURL_USE_LIBSSH2 OFF)
|
||||
set(CURL_ZLIB OFF CACHE BOOL "" FORCE)
|
||||
set(CURL_ZSTD OFF)
|
||||
set(USE_LIBIDN2 OFF)
|
||||
set(USE_NGHTTP2 OFF)
|
||||
|
||||
list(APPEND CURL_LIBS "-framework SystemConfiguration")
|
||||
if(UNIX)
|
||||
# We use mbedtls on Unix(-like) systems and Android.
|
||||
set(CURL_USE_OPENSSL OFF)
|
||||
|
||||
# This is set if the dolphin_find_optional_system_library call from the main CMakeLists.txt
|
||||
# is able to find mbedtls on the system.
|
||||
if(MBEDTLS_FOUND)
|
||||
# We can just enable CURL_USE_MBEDTLS.
|
||||
set(CURL_USE_MBEDTLS ON)
|
||||
else()
|
||||
# HACK: Set some internal variables to pretend like mbedtls was found on the system.
|
||||
# We can't use CURL_USE_MBEDTLS with our copy from Externals, as that flag expects
|
||||
# mbedtls to be installed (the CMakeLists attempts to search for it with find_package).
|
||||
set(_ssl_enabled ON)
|
||||
set(USE_MBEDTLS ON)
|
||||
set(_curl_ca_bundle_supported TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
file(GLOB SRCS curl/lib/*.c curl/lib/vauth/*.c curl/lib/vquic/*.c curl/lib/vssh/*.c curl/lib/vtls/*.c)
|
||||
add_library(
|
||||
curl
|
||||
STATIC
|
||||
curl/include/curl/curl.h
|
||||
${SRCS}
|
||||
)
|
||||
|
||||
set(SEARCH_CA_BUNDLE_PATHS
|
||||
/etc/ssl/certs/ca-certificates.crt
|
||||
/etc/pki/tls/certs/ca-bundle.crt
|
||||
/usr/share/ssl/certs/ca-bundle.crt
|
||||
/usr/local/share/certs/ca-root-nss.crt
|
||||
/etc/ssl/cert.pem)
|
||||
|
||||
foreach(SEARCH_CA_BUNDLE_PATH ${SEARCH_CA_BUNDLE_PATHS})
|
||||
if(EXISTS "${SEARCH_CA_BUNDLE_PATH}")
|
||||
message(STATUS "Found CA bundle: ${SEARCH_CA_BUNDLE_PATH}")
|
||||
set(CURL_CA_BUNDLE "${SEARCH_CA_BUNDLE_PATH}")
|
||||
set(CURL_CA_BUNDLE_SET TRUE)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(NOT CURL_CA_PATH_SET)
|
||||
if(EXISTS "/etc/ssl/certs")
|
||||
set(CURL_CA_PATH "/etc/ssl/certs")
|
||||
set(CURL_CA_PATH_SET TRUE)
|
||||
endif()
|
||||
if(WIN32)
|
||||
set(CURL_USE_SCHANNEL ON)
|
||||
endif()
|
||||
|
||||
dolphin_disable_warnings(curl)
|
||||
target_link_libraries(curl ${CURL_LIBS})
|
||||
target_include_directories(curl PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/curl/include")
|
||||
target_compile_definitions(curl PRIVATE "BUILDING_LIBCURL=1")
|
||||
if (WIN32)
|
||||
target_compile_definitions(curl PUBLIC "CURL_STATICLIB=1" "CURL_DISABLE_LDAP" "USE_WINDOWS_SSPI" "USE_SCHANNEL")
|
||||
target_link_libraries(curl Crypt32.lib)
|
||||
else()
|
||||
target_compile_definitions(curl PUBLIC "CURL_STATICLIB=1" "USE_MBEDTLS=1" "HAVE_CONFIG_H=1" "CURL_DISABLE_LDAP")
|
||||
if (CURL_CA_PATH_SET)
|
||||
target_compile_definitions(curl PUBLIC CURL_CA_PATH="${CURL_CA_PATH}")
|
||||
endif()
|
||||
if(ANDROID)
|
||||
set(CURL_CA_PATH "/system/etc/security/cacerts" CACHE STRING "")
|
||||
endif()
|
||||
|
||||
add_library(CURL::libcurl ALIAS curl)
|
||||
add_subdirectory(curl)
|
||||
|
||||
if(UNIX AND NOT MBEDTLS_FOUND)
|
||||
# HACK: Manually link with the mbedtls libraries.
|
||||
target_link_libraries(libcurl_static PRIVATE
|
||||
MbedTLS::mbedtls
|
||||
MbedTLS::mbedx509)
|
||||
endif()
|
||||
|
|
|
@ -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.
|
|
@ -1 +1 @@
|
|||
Subproject commit d755a5f7c009dd63a61b2c745180d8ba937cbfeb
|
||||
Subproject commit 75a2079d5c28debb2eaa848ca9430f1fe0d7844c
|
|
@ -50,14 +50,14 @@
|
|||
<ClCompile Include="curl\lib\curl_memrchr.c" />
|
||||
<ClCompile Include="curl\lib\curl_multibyte.c" />
|
||||
<ClCompile Include="curl\lib\curl_ntlm_core.c" />
|
||||
<ClCompile Include="curl\lib\curl_ntlm_wb.c" />
|
||||
<ClCompile Include="curl\lib\curl_path.c" />
|
||||
<ClCompile Include="curl\lib\curl_range.c" />
|
||||
<ClCompile Include="curl\lib\curl_rtmp.c" />
|
||||
<ClCompile Include="curl\lib\curl_sasl.c" />
|
||||
<ClCompile Include="curl\lib\curl_sha512_256.c" />
|
||||
<ClCompile Include="curl\lib\curl_sspi.c" />
|
||||
<ClCompile Include="curl\lib\curl_threads.c" />
|
||||
<ClCompile Include="curl\lib\curl_trc.c" />
|
||||
<ClCompile Include="curl\lib\cw-out.c" />
|
||||
<ClCompile Include="curl\lib\dict.c" />
|
||||
<ClCompile Include="curl\lib\doh.c" />
|
||||
<ClCompile Include="curl\lib\dynbuf.c" />
|
||||
|
@ -120,6 +120,7 @@
|
|||
<ClCompile Include="curl\lib\psl.c" />
|
||||
<ClCompile Include="curl\lib\rand.c" />
|
||||
<ClCompile Include="curl\lib\rename.c" />
|
||||
<ClCompile Include="curl\lib\request.c" />
|
||||
<ClCompile Include="curl\lib\rtsp.c" />
|
||||
<ClCompile Include="curl\lib\select.c" />
|
||||
<ClCompile Include="curl\lib\sendf.c" />
|
||||
|
@ -167,12 +168,16 @@
|
|||
<ClCompile Include="curl\lib\vauth\vauth.c" />
|
||||
<ClCompile Include="curl\lib\vquic\curl_msh3.c" />
|
||||
<ClCompile Include="curl\lib\vquic\curl_ngtcp2.c" />
|
||||
<ClCompile Include="curl\lib\vquic\curl_osslq.c" />
|
||||
<ClCompile Include="curl\lib\vquic\curl_quiche.c" />
|
||||
<ClCompile Include="curl\lib\vquic\vquic.c" />
|
||||
<ClCompile Include="curl\lib\vquic\vquic-tls.c" />
|
||||
<ClCompile Include="curl\lib\vssh\curl_path.c" />
|
||||
<ClCompile Include="curl\lib\vssh\libssh.c" />
|
||||
<ClCompile Include="curl\lib\vssh\libssh2.c" />
|
||||
<ClCompile Include="curl\lib\vssh\wolfssh.c" />
|
||||
<ClCompile Include="curl\lib\vtls\bearssl.c" />
|
||||
<ClCompile Include="curl\lib\vtls\cipher_suite.c" />
|
||||
<ClCompile Include="curl\lib\vtls\gtls.c" />
|
||||
<ClCompile Include="curl\lib\vtls\hostcheck.c" />
|
||||
<ClCompile Include="curl\lib\vtls\keylog.c" />
|
||||
|
@ -244,8 +249,6 @@
|
|||
<ClInclude Include="curl\lib\curl_memrchr.h" />
|
||||
<ClInclude Include="curl\lib\curl_multibyte.h" />
|
||||
<ClInclude Include="curl\lib\curl_ntlm_core.h" />
|
||||
<ClInclude Include="curl\lib\curl_ntlm_wb.h" />
|
||||
<ClInclude Include="curl\lib\curl_path.h" />
|
||||
<ClInclude Include="curl\lib\curl_printf.h" />
|
||||
<ClInclude Include="curl\lib\curl_range.h" />
|
||||
<ClInclude Include="curl\lib\curl_rtmp.h" />
|
||||
|
@ -253,9 +256,11 @@
|
|||
<ClInclude Include="curl\lib\curl_setup.h" />
|
||||
<ClInclude Include="curl\lib\curl_setup_once.h" />
|
||||
<ClInclude Include="curl\lib\curl_sha256.h" />
|
||||
<ClInclude Include="curl\lib\curl_sha512_256.h" />
|
||||
<ClInclude Include="curl\lib\curl_sspi.h" />
|
||||
<ClInclude Include="curl\lib\curl_threads.h" />
|
||||
<ClInclude Include="curl\lib\curl_trc.h" />
|
||||
<ClInclude Include="curl\lib\cw_out.h" />
|
||||
<ClInclude Include="curl\lib\dict.h" />
|
||||
<ClInclude Include="curl\lib\doh.h" />
|
||||
<ClInclude Include="curl\lib\dynbuf.h" />
|
||||
|
@ -308,6 +313,7 @@
|
|||
<ClInclude Include="curl\lib\psl.h" />
|
||||
<ClInclude Include="curl\lib\rand.h" />
|
||||
<ClInclude Include="curl\lib\rename.h" />
|
||||
<ClInclude Include="curl\lib\request.h" />
|
||||
<ClInclude Include="curl\lib\rtsp.h" />
|
||||
<ClInclude Include="curl\lib\select.h" />
|
||||
<ClInclude Include="curl\lib\sendf.h" />
|
||||
|
@ -347,11 +353,15 @@
|
|||
<ClInclude Include="curl\lib\vauth\vauth.h" />
|
||||
<ClInclude Include="curl\lib\vquic\curl_msh3.h" />
|
||||
<ClInclude Include="curl\lib\vquic\curl_ngtcp2.h" />
|
||||
<ClInclude Include="curl\lib\vquic\curl_osslq.h" />
|
||||
<ClInclude Include="curl\lib\vquic\curl_quiche.h" />
|
||||
<ClInclude Include="curl\lib\vquic\vquic.h" />
|
||||
<ClInclude Include="curl\lib\vquic\vquic-tls.h" />
|
||||
<ClInclude Include="curl\lib\vquic\vquic_int.h" />
|
||||
<ClInclude Include="curl\lib\vssh\curl_path.h" />
|
||||
<ClInclude Include="curl\lib\vssh\ssh.h" />
|
||||
<ClInclude Include="curl\lib\vtls\bearssl.h" />
|
||||
<ClInclude Include="curl\lib\vtls\cipher_suites.h" />
|
||||
<ClInclude Include="curl\lib\vtls\gtls.h" />
|
||||
<ClInclude Include="curl\lib\vtls\hostcheck.h" />
|
||||
<ClInclude Include="curl\lib\vtls\keylog.h" />
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -19,7 +19,9 @@ import android.widget.Toast
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.fragment.app.FragmentManager
|
||||
|
@ -326,12 +328,11 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider {
|
|||
}
|
||||
|
||||
private fun enableFullscreenImmersive() {
|
||||
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
|
||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
|
||||
View.SYSTEM_UI_FLAG_FULLSCREEN or
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
WindowInsetsControllerCompat(window, window.decorView).let { controller ->
|
||||
controller.hide(WindowInsetsCompat.Type.systemBars())
|
||||
controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateDisplaySettings() {
|
||||
|
|
|
@ -124,7 +124,7 @@ class Settings : Closeable {
|
|||
const val SECTION_INI_DSP = "DSP"
|
||||
const val SECTION_LOGGER_LOGS = "Logs"
|
||||
const val SECTION_LOGGER_OPTIONS = "Options"
|
||||
const val SECTION_GFX_HARDWARE = "Settings"
|
||||
const val SECTION_GFX_HARDWARE = "Hardware"
|
||||
const val SECTION_GFX_SETTINGS = "Settings"
|
||||
const val SECTION_GFX_ENHANCEMENTS = "Enhancements"
|
||||
const val SECTION_GFX_COLOR_CORRECTION = "ColorCorrection"
|
||||
|
|
|
@ -1,132 +1,132 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.features.skylanders.ui
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.Intent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import android.widget.AdapterView.OnItemClickListener
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Toast
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.dolphinemu.dolphinemu.R
|
||||
import org.dolphinemu.dolphinemu.activities.EmulationActivity
|
||||
import org.dolphinemu.dolphinemu.databinding.DialogCreateSkylanderBinding
|
||||
import org.dolphinemu.dolphinemu.databinding.ListItemNfcFigureSlotBinding
|
||||
import org.dolphinemu.dolphinemu.features.skylanders.SkylanderConfig
|
||||
import org.dolphinemu.dolphinemu.features.skylanders.SkylanderConfig.removeSkylander
|
||||
import org.dolphinemu.dolphinemu.features.skylanders.model.SkylanderPair
|
||||
|
||||
class SkylanderSlotAdapter(
|
||||
private val slots: List<SkylanderSlot>,
|
||||
private val activity: EmulationActivity
|
||||
) : RecyclerView.Adapter<SkylanderSlotAdapter.ViewHolder>(), OnItemClickListener {
|
||||
class ViewHolder(var binding: ListItemNfcFigureSlotBinding) :
|
||||
RecyclerView.ViewHolder(binding.getRoot())
|
||||
|
||||
private lateinit var binding: DialogCreateSkylanderBinding
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
val binding = ListItemNfcFigureSlotBinding.inflate(inflater, parent, false)
|
||||
return ViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val slot = slots[position]
|
||||
holder.binding.textFigureName.text = slot.label
|
||||
|
||||
holder.binding.buttonClearFigure.setOnClickListener {
|
||||
removeSkylander(slot.portalSlot)
|
||||
activity.clearSkylander(slot.slotNum)
|
||||
}
|
||||
|
||||
holder.binding.buttonLoadFigure.setOnClickListener {
|
||||
val loadSkylander = Intent(Intent.ACTION_OPEN_DOCUMENT)
|
||||
loadSkylander.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
loadSkylander.type = "*/*"
|
||||
activity.setSkylanderData(0, 0, "", position)
|
||||
activity.startActivityForResult(
|
||||
loadSkylander,
|
||||
EmulationActivity.REQUEST_SKYLANDER_FILE
|
||||
)
|
||||
}
|
||||
|
||||
val inflater = LayoutInflater.from(activity)
|
||||
binding = DialogCreateSkylanderBinding.inflate(inflater)
|
||||
|
||||
val nameList = SkylanderConfig.REVERSE_LIST_SKYLANDERS.keys.toMutableList()
|
||||
nameList.sort()
|
||||
val skylanderNames: ArrayList<String> = ArrayList(nameList)
|
||||
|
||||
binding.skylanderDropdown.setAdapter(
|
||||
ArrayAdapter(
|
||||
activity, R.layout.support_simple_spinner_dropdown_item,
|
||||
skylanderNames
|
||||
)
|
||||
)
|
||||
binding.skylanderDropdown.onItemClickListener = this
|
||||
|
||||
holder.binding.buttonCreateFigure.setOnClickListener {
|
||||
if (binding.getRoot().parent != null) {
|
||||
(binding.getRoot().parent as ViewGroup).removeAllViews()
|
||||
}
|
||||
val createDialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.create_skylander_title)
|
||||
.setView(binding.getRoot())
|
||||
.setPositiveButton(R.string.create_figure, null)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show()
|
||||
createDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
|
||||
if (binding.skylanderId.text.toString().isNotBlank() &&
|
||||
binding.skylanderVar.text.toString().isNotBlank()
|
||||
) {
|
||||
val createSkylander = Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||
createSkylander.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
createSkylander.type = "*/*"
|
||||
val id = binding.skylanderId.text.toString().toInt()
|
||||
val variant = binding.skylanderVar.text.toString().toInt()
|
||||
val name = SkylanderConfig.LIST_SKYLANDERS[SkylanderPair(id, variant)]
|
||||
if (name != null) {
|
||||
createSkylander.putExtra(
|
||||
Intent.EXTRA_TITLE,
|
||||
"$name.sky"
|
||||
)
|
||||
activity.setSkylanderData(id, variant, name, position)
|
||||
} else {
|
||||
createSkylander.putExtra(
|
||||
Intent.EXTRA_TITLE,
|
||||
"Unknown(ID: " + id + "Variant: " + variant + ").sky"
|
||||
)
|
||||
activity.setSkylanderData(id, variant, "Unknown", position)
|
||||
}
|
||||
activity.startActivityForResult(
|
||||
createSkylander,
|
||||
EmulationActivity.REQUEST_CREATE_SKYLANDER
|
||||
)
|
||||
createDialog.dismiss()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
activity, R.string.invalid_skylander,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return slots.size
|
||||
}
|
||||
|
||||
override fun onItemClick(parent: AdapterView<*>, view: View, position: Int, id: Long) {
|
||||
val skylanderIdVar =
|
||||
SkylanderConfig.REVERSE_LIST_SKYLANDERS[parent.getItemAtPosition(position)]
|
||||
binding.skylanderId.setText(skylanderIdVar!!.id.toString())
|
||||
binding.skylanderVar.setText(skylanderIdVar.variant.toString())
|
||||
}
|
||||
}
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.features.skylanders.ui
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.Intent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import android.widget.AdapterView.OnItemClickListener
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Toast
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.dolphinemu.dolphinemu.R
|
||||
import org.dolphinemu.dolphinemu.activities.EmulationActivity
|
||||
import org.dolphinemu.dolphinemu.databinding.DialogCreateSkylanderBinding
|
||||
import org.dolphinemu.dolphinemu.databinding.ListItemNfcFigureSlotBinding
|
||||
import org.dolphinemu.dolphinemu.features.skylanders.SkylanderConfig
|
||||
import org.dolphinemu.dolphinemu.features.skylanders.SkylanderConfig.removeSkylander
|
||||
import org.dolphinemu.dolphinemu.features.skylanders.model.SkylanderPair
|
||||
|
||||
class SkylanderSlotAdapter(
|
||||
private val slots: List<SkylanderSlot>,
|
||||
private val activity: EmulationActivity
|
||||
) : RecyclerView.Adapter<SkylanderSlotAdapter.ViewHolder>(), OnItemClickListener {
|
||||
class ViewHolder(var binding: ListItemNfcFigureSlotBinding) :
|
||||
RecyclerView.ViewHolder(binding.getRoot())
|
||||
|
||||
private lateinit var binding: DialogCreateSkylanderBinding
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
val binding = ListItemNfcFigureSlotBinding.inflate(inflater, parent, false)
|
||||
return ViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val slot = slots[position]
|
||||
holder.binding.textFigureName.text = slot.label
|
||||
|
||||
holder.binding.buttonClearFigure.setOnClickListener {
|
||||
removeSkylander(slot.portalSlot)
|
||||
activity.clearSkylander(slot.slotNum)
|
||||
}
|
||||
|
||||
holder.binding.buttonLoadFigure.setOnClickListener {
|
||||
val loadSkylander = Intent(Intent.ACTION_OPEN_DOCUMENT)
|
||||
loadSkylander.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
loadSkylander.type = "*/*"
|
||||
activity.setSkylanderData(0, 0, "", position)
|
||||
activity.startActivityForResult(
|
||||
loadSkylander,
|
||||
EmulationActivity.REQUEST_SKYLANDER_FILE
|
||||
)
|
||||
}
|
||||
|
||||
val inflater = LayoutInflater.from(activity)
|
||||
binding = DialogCreateSkylanderBinding.inflate(inflater)
|
||||
|
||||
val nameList = SkylanderConfig.REVERSE_LIST_SKYLANDERS.keys.toMutableList()
|
||||
nameList.sort()
|
||||
val skylanderNames: ArrayList<String> = ArrayList(nameList)
|
||||
|
||||
binding.skylanderDropdown.setAdapter(
|
||||
ArrayAdapter(
|
||||
activity, R.layout.support_simple_spinner_dropdown_item,
|
||||
skylanderNames
|
||||
)
|
||||
)
|
||||
binding.skylanderDropdown.onItemClickListener = this
|
||||
|
||||
holder.binding.buttonCreateFigure.setOnClickListener {
|
||||
if (binding.getRoot().parent != null) {
|
||||
(binding.getRoot().parent as ViewGroup).removeAllViews()
|
||||
}
|
||||
val createDialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.create_skylander_title)
|
||||
.setView(binding.getRoot())
|
||||
.setPositiveButton(R.string.create_figure, null)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show()
|
||||
createDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
|
||||
if (binding.skylanderId.text.toString().isNotBlank() &&
|
||||
binding.skylanderVar.text.toString().isNotBlank()
|
||||
) {
|
||||
val createSkylander = Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||
createSkylander.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
createSkylander.type = "*/*"
|
||||
val id = binding.skylanderId.text.toString().toInt()
|
||||
val variant = binding.skylanderVar.text.toString().toInt()
|
||||
val name = SkylanderConfig.LIST_SKYLANDERS[SkylanderPair(id, variant)]
|
||||
if (name != null) {
|
||||
createSkylander.putExtra(
|
||||
Intent.EXTRA_TITLE,
|
||||
"$name.sky"
|
||||
)
|
||||
activity.setSkylanderData(id, variant, name, position)
|
||||
} else {
|
||||
createSkylander.putExtra(
|
||||
Intent.EXTRA_TITLE,
|
||||
"Unknown(ID: " + id + "Variant: " + variant + ").sky"
|
||||
)
|
||||
activity.setSkylanderData(id, variant, "Unknown", position)
|
||||
}
|
||||
activity.startActivityForResult(
|
||||
createSkylander,
|
||||
EmulationActivity.REQUEST_CREATE_SKYLANDER
|
||||
)
|
||||
createDialog.dismiss()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
activity, R.string.invalid_skylander,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return slots.size
|
||||
}
|
||||
|
||||
override fun onItemClick(parent: AdapterView<*>, view: View, position: Int, id: Long) {
|
||||
val skylanderIdVar =
|
||||
SkylanderConfig.REVERSE_LIST_SKYLANDERS[parent.getItemAtPosition(position)]
|
||||
binding.skylanderId.setText(skylanderIdVar!!.id.toString())
|
||||
binding.skylanderVar.setText(skylanderIdVar.variant.toString())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,72 +1,72 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/root"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="@dimen/spacing_medlarge">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/layout_skylander_dropdown"
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="@dimen/spacing_medlarge"
|
||||
android:paddingHorizontal="@dimen/spacing_medlarge"
|
||||
android:hint="@string/skylander_label"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.textfield.MaterialAutoCompleteTextView
|
||||
android:id="@+id/skylander_dropdown"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:spinnerMode="dialog"
|
||||
android:imeOptions="actionDone" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintTop_toBottomOf="@+id/layout_skylander_dropdown"
|
||||
android:gravity="center_vertical"
|
||||
android:baselineAligned="false">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/layout_skylander_id"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="@string/skylander_id"
|
||||
android:paddingHorizontal="@dimen/spacing_medlarge">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/skylander_id"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="number" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/layout_skylander_var"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="@string/skylander_variant"
|
||||
android:paddingHorizontal="@dimen/spacing_medlarge">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/skylander_var"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="number" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/root"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="@dimen/spacing_medlarge">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/layout_skylander_dropdown"
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="@dimen/spacing_medlarge"
|
||||
android:paddingHorizontal="@dimen/spacing_medlarge"
|
||||
android:hint="@string/skylander_label"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.textfield.MaterialAutoCompleteTextView
|
||||
android:id="@+id/skylander_dropdown"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:spinnerMode="dialog"
|
||||
android:imeOptions="actionDone" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintTop_toBottomOf="@+id/layout_skylander_dropdown"
|
||||
android:gravity="center_vertical"
|
||||
android:baselineAligned="false">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/layout_skylander_id"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="@string/skylander_id"
|
||||
android:paddingHorizontal="@dimen/spacing_medlarge">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/skylander_id"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="number" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/layout_skylander_var"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="@string/skylander_variant"
|
||||
android:paddingHorizontal="@dimen/spacing_medlarge">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/skylander_var"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="number" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
@ -62,7 +62,7 @@ bool IsPathAndroidContent(std::string_view uri)
|
|||
std::string OpenModeToAndroid(std::string mode)
|
||||
{
|
||||
// The 'b' specifier is not supported by Android. Since we're on POSIX, it's fine to just skip it.
|
||||
mode.erase(std::remove(mode.begin(), mode.end(), 'b'));
|
||||
std::erase(mode, 'b');
|
||||
|
||||
if (mode == "r")
|
||||
return "r";
|
||||
|
|
|
@ -191,7 +191,7 @@ Java_org_dolphinemu_dolphinemu_features_cheats_model_GeckoCheat_downloadCodes(JN
|
|||
const std::string gametdb_id = GetJString(env, jGameTdbId);
|
||||
|
||||
bool success = true;
|
||||
const std::vector<Gecko::GeckoCode> codes = Gecko::DownloadCodes(gametdb_id, &success, false);
|
||||
const std::vector<Gecko::GeckoCode> codes = Gecko::DownloadCodes(gametdb_id, &success);
|
||||
|
||||
if (!success)
|
||||
return nullptr;
|
||||
|
|
|
@ -51,10 +51,9 @@ Java_org_dolphinemu_dolphinemu_features_cheats_model_GraphicsModGroup_getMods(JN
|
|||
for (GraphicsModConfig& mod : mod_group->GetMods())
|
||||
{
|
||||
// If no group matches the mod's features, or if the mod has no features, skip it
|
||||
if (std::none_of(mod.m_features.begin(), mod.m_features.end(),
|
||||
[&groups](const GraphicsModFeatureConfig& feature) {
|
||||
return groups.contains(feature.m_group);
|
||||
}))
|
||||
if (std::ranges::none_of(mod.m_features, [&groups](const GraphicsModFeatureConfig& feature) {
|
||||
return groups.contains(feature.m_group);
|
||||
}))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
// Copyright 2003 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <android/log.h>
|
||||
#include <android/native_window_jni.h>
|
||||
#include <condition_variable>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <fmt/format.h>
|
||||
#include <jni.h>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
|
@ -15,6 +11,12 @@
|
|||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <android/log.h>
|
||||
#include <android/native_window_jni.h>
|
||||
#include <fmt/format.h>
|
||||
#include <jni.h>
|
||||
|
||||
#include "Common/AndroidAnalytics.h"
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/CPUDetect.h"
|
||||
|
@ -77,6 +79,8 @@ Common::Event s_update_main_frame_event;
|
|||
// This exists to prevent surfaces from being destroyed during the boot process,
|
||||
// as that can lead to the boot process dereferencing nullptr.
|
||||
std::mutex s_surface_lock;
|
||||
std::condition_variable s_surface_cv;
|
||||
|
||||
bool s_need_nonblocking_alert_msg;
|
||||
|
||||
Common::Flag s_is_booting;
|
||||
|
@ -482,6 +486,8 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceChang
|
|||
|
||||
if (g_presenter)
|
||||
g_presenter->ChangeSurface(s_surf);
|
||||
|
||||
s_surface_cv.notify_all();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestroyed(JNIEnv*,
|
||||
|
@ -515,6 +521,8 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestr
|
|||
ANativeWindow_release(s_surf);
|
||||
s_surf = nullptr;
|
||||
}
|
||||
|
||||
s_surface_cv.notify_all();
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_HasSurface(JNIEnv*, jclass)
|
||||
|
@ -606,12 +614,14 @@ static void Run(JNIEnv* env, std::unique_ptr<BootParameters>&& boot, bool riivol
|
|||
volume.GetDiscNumber()));
|
||||
}
|
||||
|
||||
WindowSystemInfo wsi(WindowSystemType::Android, nullptr, s_surf, s_surf);
|
||||
wsi.render_surface_scale = GetRenderSurfaceScale(env);
|
||||
|
||||
s_need_nonblocking_alert_msg = true;
|
||||
std::unique_lock<std::mutex> surface_guard(s_surface_lock);
|
||||
|
||||
s_surface_cv.wait(surface_guard, []() { return s_surf != nullptr; });
|
||||
|
||||
WindowSystemInfo wsi(WindowSystemType::Android, nullptr, s_surf, s_surf);
|
||||
wsi.render_surface_scale = GetRenderSurfaceScale(env);
|
||||
|
||||
if (BootManager::BootCore(Core::System::GetInstance(), std::move(boot), wsi))
|
||||
{
|
||||
static constexpr int WAIT_STEP = 25;
|
||||
|
|
|
@ -34,6 +34,7 @@ add_library(common
|
|||
Config/Enums.h
|
||||
Config/Layer.cpp
|
||||
Config/Layer.h
|
||||
Contains.h
|
||||
CPUDetect.h
|
||||
Crypto/AES.cpp
|
||||
Crypto/AES.h
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
namespace Common
|
||||
{
|
||||
struct ContainsFn
|
||||
{
|
||||
template <std::input_iterator I, std::sentinel_for<I> S, class T, class Proj = std::identity>
|
||||
requires std::indirect_binary_predicate < std::ranges::equal_to, std::projected<I, Proj>,
|
||||
const T* > constexpr bool operator()(I first, S last, const T& value, Proj proj = {}) const
|
||||
{
|
||||
return std::ranges::find(std::move(first), last, value, std::move(proj)) != last;
|
||||
}
|
||||
|
||||
template <std::ranges::input_range R, class T, class Proj = std::identity>
|
||||
requires std::indirect_binary_predicate < std::ranges::equal_to,
|
||||
std::projected<std::ranges::iterator_t<R>, Proj>,
|
||||
const T* > constexpr bool operator()(R&& r, const T& value, Proj proj = {}) const
|
||||
{
|
||||
return (*this)(std::ranges::begin(r), std::ranges::end(r), value, std::move(proj));
|
||||
}
|
||||
};
|
||||
|
||||
struct ContainsSubrangeFn
|
||||
{
|
||||
template <std::forward_iterator I1, std::sentinel_for<I1> S1, std::forward_iterator I2,
|
||||
std::sentinel_for<I2> S2, class Pred = std::ranges::equal_to,
|
||||
class Proj1 = std::identity, class Proj2 = std::identity>
|
||||
requires std::indirectly_comparable<I1, I2, Pred, Proj1, Proj2>
|
||||
constexpr bool operator()(I1 first1, S1 last1, I2 first2, S2 last2, Pred pred = {},
|
||||
Proj1 proj1 = {}, Proj2 proj2 = {}) const
|
||||
{
|
||||
return !std::ranges::search(std::move(first1), std::move(last1), std::move(first2),
|
||||
std::move(last2), std::move(pred), std::move(proj1),
|
||||
std::move(proj2))
|
||||
.empty();
|
||||
}
|
||||
|
||||
template <std::ranges::forward_range R1, std::ranges::forward_range R2,
|
||||
class Pred = std::ranges::equal_to, class Proj1 = std::identity,
|
||||
class Proj2 = std::identity>
|
||||
requires std::indirectly_comparable<std::ranges::iterator_t<R1>, std::ranges::iterator_t<R2>,
|
||||
Pred, Proj1, Proj2>
|
||||
constexpr bool operator()(R1&& r1, R2&& r2, Pred pred = {}, Proj1 proj1 = {},
|
||||
Proj2 proj2 = {}) const
|
||||
{
|
||||
return (*this)(std::ranges::begin(r1), std::ranges::end(r1), std::ranges::begin(r2),
|
||||
std::ranges::end(r2), std::move(pred), std::move(proj1), std::move(proj2));
|
||||
}
|
||||
};
|
||||
|
||||
// TODO C++23: Replace with std::ranges::contains.
|
||||
inline constexpr ContainsFn Contains{};
|
||||
// TODO C++23: Replace with std::ranges::contains_subrange.
|
||||
inline constexpr ContainsSubrangeFn ContainsSubrange{};
|
||||
} // namespace Common
|
|
@ -24,7 +24,7 @@ struct Elt
|
|||
{
|
||||
bool IsZero() const
|
||||
{
|
||||
return std::all_of(data.begin(), data.end(), [](u8 b) { return b == 0; });
|
||||
return std::ranges::all_of(data, [](u8 b) { return b == 0; });
|
||||
}
|
||||
|
||||
void MulX()
|
||||
|
|
|
@ -91,7 +91,7 @@ void MemoryPatches::DisablePatch(const Core::CPUThreadGuard& guard, std::size_t
|
|||
|
||||
bool MemoryPatches::HasEnabledPatch(u32 address) const
|
||||
{
|
||||
return std::any_of(m_patches.begin(), m_patches.end(), [address](const MemoryPatch& patch) {
|
||||
return std::ranges::any_of(m_patches, [address](const MemoryPatch& patch) {
|
||||
return patch.address == address && patch.is_enabled == MemoryPatch::State::Enabled;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ void Watches::DisableWatch(std::size_t index)
|
|||
|
||||
bool Watches::HasEnabledWatch(u32 address) const
|
||||
{
|
||||
return std::any_of(m_watches.begin(), m_watches.end(), [address](const auto& watch) {
|
||||
return std::ranges::any_of(m_watches, [address](const auto& watch) {
|
||||
return watch.address == address && watch.is_enabled == Watch::State::Enabled;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -729,7 +729,7 @@ static bool Unpack(const std::function<bool()>& cancelled, const std::string pat
|
|||
const bool is_path_traversal_attack =
|
||||
(childname.find("\\") != std::string_view::npos) ||
|
||||
(childname.find('/') != std::string_view::npos) ||
|
||||
std::all_of(childname.begin(), childname.end(), [](char c) { return c == '.'; });
|
||||
std::ranges::all_of(childname, [](char c) { return c == '.'; });
|
||||
if (is_path_traversal_attack)
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
|
|
|
@ -41,7 +41,7 @@ std::vector<std::string> DoFileSearch(const std::vector<std::string>& directorie
|
|||
// N.B. This avoids doing any copies
|
||||
auto ext_matches = [&native_exts](const fs::path& path) {
|
||||
const std::basic_string_view<fs::path::value_type> native_path = path.native();
|
||||
return std::any_of(native_exts.cbegin(), native_exts.cend(), [&native_path](const auto& ext) {
|
||||
return std::ranges::any_of(native_exts, [&native_path](const auto& ext) {
|
||||
const auto compare_len = ext.native().length();
|
||||
if (native_path.length() < compare_len)
|
||||
return false;
|
||||
|
@ -98,7 +98,8 @@ std::vector<std::string> DoFileSearch(const std::vector<std::string>& directorie
|
|||
// not because std::filesystem returns duplicates). Also note that this pathname-based uniqueness
|
||||
// isn't as thorough as std::filesystem::equivalent.
|
||||
std::ranges::sort(result);
|
||||
result.erase(std::unique(result.begin(), result.end()), result.end());
|
||||
const auto unique_result = std::ranges::unique(result);
|
||||
result.erase(unique_result.begin(), unique_result.end());
|
||||
|
||||
// Dolphin expects to be able to use "/" (DIR_SEP) everywhere.
|
||||
// std::filesystem uses the OS separator.
|
||||
|
|
|
@ -85,7 +85,8 @@ bool IniFile::Section::Delete(std::string_view key)
|
|||
return false;
|
||||
|
||||
values.erase(it);
|
||||
keys_order.erase(std::ranges::find(keys_order, key));
|
||||
keys_order.erase(std::ranges::find_if(
|
||||
keys_order, [&](std::string_view v) { return CaseInsensitiveEquals(key, v); }));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -99,7 +99,13 @@ void GenericLogFmt(LogLevel level, LogType type, const char* file, int line, con
|
|||
static_assert(NumFields == sizeof...(args),
|
||||
"Unexpected number of replacement fields in format string; did you pass too few or "
|
||||
"too many arguments?");
|
||||
GenericLogFmtImpl(level, type, file, line, format, fmt::make_format_args(args...));
|
||||
|
||||
#if FMT_VERSION >= 110000
|
||||
auto&& format_str = fmt::format_string<Args...>(format);
|
||||
#else
|
||||
auto&& format_str = format;
|
||||
#endif
|
||||
GenericLogFmtImpl(level, type, file, line, format_str, fmt::make_format_args(args...));
|
||||
}
|
||||
} // namespace Common::Log
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ struct WindowsMemoryFunctions
|
|||
void* m_address_UnmapViewOfFileEx = nullptr;
|
||||
void* m_address_VirtualAlloc2 = nullptr;
|
||||
void* m_address_MapViewOfFile3 = nullptr;
|
||||
void* m_address_VirtualProtect = nullptr;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
@ -111,6 +112,15 @@ public:
|
|||
///
|
||||
void UnmapFromMemoryRegion(void* view, size_t size);
|
||||
|
||||
///
|
||||
/// Virtual protect a section from the memory region previously mapped by CreateView.
|
||||
///
|
||||
/// @param data Pointer to data to protect.
|
||||
/// @param size Size of the protection.
|
||||
/// @param flag What new permission to protect with.
|
||||
///
|
||||
bool VirtualProtectMemoryRegion(u8* data, size_t size, u64 flag);
|
||||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
WindowsMemoryRegion* EnsureSplitRegionForMapping(void* address, size_t size);
|
||||
|
|
|
@ -34,6 +34,9 @@ using PMapViewOfFile3 = PVOID(WINAPI*)(HANDLE FileMapping, HANDLE Process, PVOID
|
|||
|
||||
using PUnmapViewOfFileEx = BOOL(WINAPI*)(PVOID BaseAddress, ULONG UnmapFlags);
|
||||
|
||||
using PVirtualProtect = BOOL(WINAPI*)(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect,
|
||||
PDWORD lpflOldProtect);
|
||||
|
||||
using PIsApiSetImplemented = BOOL(APIENTRY*)(PCSTR Contract);
|
||||
|
||||
namespace Common
|
||||
|
@ -78,11 +81,14 @@ static bool InitWindowsMemoryFunctions(WindowsMemoryFunctions* functions)
|
|||
functions->m_api_ms_win_core_memory_l1_1_6_handle.GetSymbolAddress("MapViewOfFile3FromApp");
|
||||
void* const address_UnmapViewOfFileEx =
|
||||
functions->m_kernel32_handle.GetSymbolAddress("UnmapViewOfFileEx");
|
||||
void* const address_VirtualProtect =
|
||||
functions->m_kernel32_handle.GetSymbolAddress("VirtualProtect");
|
||||
if (address_VirtualAlloc2 && address_MapViewOfFile3 && address_UnmapViewOfFileEx)
|
||||
{
|
||||
functions->m_address_VirtualAlloc2 = address_VirtualAlloc2;
|
||||
functions->m_address_MapViewOfFile3 = address_MapViewOfFile3;
|
||||
functions->m_address_UnmapViewOfFileEx = address_UnmapViewOfFileEx;
|
||||
functions->m_address_VirtualProtect = address_VirtualProtect;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -209,6 +215,13 @@ void MemArena::ReleaseMemoryRegion()
|
|||
}
|
||||
}
|
||||
|
||||
bool MemArena::VirtualProtectMemoryRegion(u8* data, size_t size, u64 flag)
|
||||
{
|
||||
DWORD lpflOldProtect = 0;
|
||||
return static_cast<PVirtualProtect>(m_memory_functions.m_address_VirtualProtect)(
|
||||
data, size, flag, &lpflOldProtect);
|
||||
}
|
||||
|
||||
WindowsMemoryRegion* MemArena::EnsureSplitRegionForMapping(void* start_address, size_t size)
|
||||
{
|
||||
u8* const address = static_cast<u8*>(start_address);
|
||||
|
|
|
@ -289,4 +289,15 @@ size_t MemPhysical()
|
|||
#endif
|
||||
}
|
||||
|
||||
size_t PageSize()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SYSTEM_INFO sysInfo;
|
||||
GetSystemInfo(&sysInfo);
|
||||
return sysInfo.dwPageSize;
|
||||
#else
|
||||
return sysconf(_SC_PAGESIZE);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
|
|
@ -34,5 +34,6 @@ bool ReadProtectMemory(void* ptr, size_t size);
|
|||
bool WriteProtectMemory(void* ptr, size_t size, bool executable = false);
|
||||
bool UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false);
|
||||
size_t MemPhysical();
|
||||
size_t PageSize();
|
||||
|
||||
} // namespace Common
|
||||
|
|
|
@ -1,48 +1,48 @@
|
|||
// Copyright 2019 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <mz_compat.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/ScopeGuard.h"
|
||||
|
||||
namespace Common
|
||||
{
|
||||
// Reads all of the current file. destination must be big enough to fit the whole file.
|
||||
inline bool ReadFileFromZip(unzFile file, u8* destination, u64 len)
|
||||
{
|
||||
const u64 MAX_BUFFER_SIZE = 65535;
|
||||
|
||||
if (unzOpenCurrentFile(file) != UNZ_OK)
|
||||
return false;
|
||||
|
||||
Common::ScopeGuard guard{[&] { unzCloseCurrentFile(file); }};
|
||||
|
||||
u64 bytes_to_go = len;
|
||||
while (bytes_to_go > 0)
|
||||
{
|
||||
// NOTE: multiples of 4G can't cause read_len == 0 && bytes_to_go > 0, as MAX_BUFFER_SIZE is
|
||||
// small.
|
||||
const u32 read_len = static_cast<u32>(std::min(bytes_to_go, MAX_BUFFER_SIZE));
|
||||
const int rv = unzReadCurrentFile(file, destination, read_len);
|
||||
if (rv < 0)
|
||||
return false;
|
||||
|
||||
const u32 bytes_read = static_cast<u32>(rv);
|
||||
bytes_to_go -= bytes_read;
|
||||
destination += bytes_read;
|
||||
}
|
||||
|
||||
return unzEndOfFile(file) == 1;
|
||||
}
|
||||
|
||||
template <typename ContiguousContainer>
|
||||
bool ReadFileFromZip(unzFile file, ContiguousContainer* destination)
|
||||
{
|
||||
return ReadFileFromZip(file, reinterpret_cast<u8*>(destination->data()), destination->size());
|
||||
}
|
||||
} // namespace Common
|
||||
// Copyright 2019 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <mz_compat.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/ScopeGuard.h"
|
||||
|
||||
namespace Common
|
||||
{
|
||||
// Reads all of the current file. destination must be big enough to fit the whole file.
|
||||
inline bool ReadFileFromZip(unzFile file, u8* destination, u64 len)
|
||||
{
|
||||
const u64 MAX_BUFFER_SIZE = 65535;
|
||||
|
||||
if (unzOpenCurrentFile(file) != UNZ_OK)
|
||||
return false;
|
||||
|
||||
Common::ScopeGuard guard{[&] { unzCloseCurrentFile(file); }};
|
||||
|
||||
u64 bytes_to_go = len;
|
||||
while (bytes_to_go > 0)
|
||||
{
|
||||
// NOTE: multiples of 4G can't cause read_len == 0 && bytes_to_go > 0, as MAX_BUFFER_SIZE is
|
||||
// small.
|
||||
const u32 read_len = static_cast<u32>(std::min(bytes_to_go, MAX_BUFFER_SIZE));
|
||||
const int rv = unzReadCurrentFile(file, destination, read_len);
|
||||
if (rv < 0)
|
||||
return false;
|
||||
|
||||
const u32 bytes_read = static_cast<u32>(rv);
|
||||
bytes_to_go -= bytes_read;
|
||||
destination += bytes_read;
|
||||
}
|
||||
|
||||
return unzEndOfFile(file) == 1;
|
||||
}
|
||||
|
||||
template <typename ContiguousContainer>
|
||||
bool ReadFileFromZip(unzFile file, ContiguousContainer* destination)
|
||||
{
|
||||
return ReadFileFromZip(file, reinterpret_cast<u8*>(destination->data()), destination->size());
|
||||
}
|
||||
} // namespace Common
|
||||
|
|
|
@ -41,12 +41,17 @@ bool MsgAlertFmt(bool yes_no, MsgType style, Common::Log::LogType log_type, cons
|
|||
static_assert(NumFields == sizeof...(args),
|
||||
"Unexpected number of replacement fields in format string; did you pass too few or "
|
||||
"too many arguments?");
|
||||
#if FMT_VERSION >= 90000
|
||||
#if FMT_VERSION >= 110000
|
||||
static_assert(std::is_base_of_v<fmt::detail::compile_string, S>);
|
||||
auto&& format_str = fmt::format_string<Args...>(format);
|
||||
#elif FMT_VERSION >= 90000
|
||||
static_assert(fmt::detail::is_compile_string<S>::value);
|
||||
auto&& format_str = format;
|
||||
#else
|
||||
static_assert(fmt::is_compile_string<S>::value);
|
||||
auto&& format_str = format;
|
||||
#endif
|
||||
return MsgAlertFmtImpl(yes_no, style, log_type, file, line, format,
|
||||
return MsgAlertFmtImpl(yes_no, style, log_type, file, line, format_str,
|
||||
fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
|
@ -60,7 +65,9 @@ bool MsgAlertFmtT(bool yes_no, MsgType style, Common::Log::LogType log_type, con
|
|||
static_assert(NumFields == sizeof...(args),
|
||||
"Unexpected number of replacement fields in format string; did you pass too few or "
|
||||
"too many arguments?");
|
||||
#if FMT_VERSION >= 90000
|
||||
#if FMT_VERSION >= 110000
|
||||
static_assert(std::is_base_of_v<fmt::detail::compile_string, S>);
|
||||
#elif FMT_VERSION >= 90000
|
||||
static_assert(fmt::detail::is_compile_string<S>::value);
|
||||
#else
|
||||
static_assert(fmt::is_compile_string<S>::value);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <fmt/ranges.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Contains.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
|
@ -105,15 +106,14 @@ bool IsTitlePath(const std::string& path, std::optional<FromWhichRoot> from, u64
|
|||
|
||||
static bool IsIllegalCharacter(char c)
|
||||
{
|
||||
static constexpr auto illegal_chars = {'\"', '*', '/', ':', '<', '>', '?', '\\', '|', '\x7f'};
|
||||
return static_cast<unsigned char>(c) <= 0x1F ||
|
||||
std::find(illegal_chars.begin(), illegal_chars.end(), c) != illegal_chars.end();
|
||||
static constexpr char illegal_chars[] = {'\"', '*', '/', ':', '<', '>', '?', '\\', '|', '\x7f'};
|
||||
return static_cast<unsigned char>(c) <= 0x1F || Common::Contains(illegal_chars, c);
|
||||
}
|
||||
|
||||
std::string EscapeFileName(const std::string& filename)
|
||||
{
|
||||
// Prevent paths from containing special names like ., .., ..., ...., and so on
|
||||
if (std::all_of(filename.begin(), filename.end(), [](char c) { return c == '.'; }))
|
||||
if (std::ranges::all_of(filename, [](char c) { return c == '.'; }))
|
||||
return ReplaceAll(filename, ".", "__2e__");
|
||||
|
||||
// Escape all double underscores since we will use double underscores for our escape sequences
|
||||
|
@ -170,8 +170,7 @@ std::string UnescapeFileName(const std::string& filename)
|
|||
|
||||
bool IsFileNameSafe(const std::string_view filename)
|
||||
{
|
||||
return !filename.empty() &&
|
||||
!std::all_of(filename.begin(), filename.end(), [](char c) { return c == '.'; }) &&
|
||||
std::none_of(filename.begin(), filename.end(), IsIllegalCharacter);
|
||||
return !filename.empty() && !std::ranges::all_of(filename, [](char c) { return c == '.'; }) &&
|
||||
std::ranges::none_of(filename, IsIllegalCharacter);
|
||||
}
|
||||
} // namespace Common
|
||||
|
|
|
@ -275,6 +275,21 @@ inline bool IsAlpha(char c)
|
|||
return std::isalpha(c, std::locale::classic());
|
||||
}
|
||||
|
||||
inline bool IsAlnum(char c)
|
||||
{
|
||||
return std::isalnum(c, std::locale::classic());
|
||||
}
|
||||
|
||||
inline bool IsUpper(char c)
|
||||
{
|
||||
return std::isupper(c, std::locale::classic());
|
||||
}
|
||||
|
||||
inline bool IsXDigit(char c)
|
||||
{
|
||||
return std::isxdigit(c /* no locale needed */) != 0;
|
||||
}
|
||||
|
||||
inline char ToLower(char ch)
|
||||
{
|
||||
return std::tolower(ch, std::locale::classic());
|
||||
|
|
|
@ -132,7 +132,6 @@ public:
|
|||
void DoIdle();
|
||||
|
||||
std::recursive_mutex& GetLock();
|
||||
void SetHardcoreMode();
|
||||
bool IsHardcoreModeActive() const;
|
||||
void SetGameIniId(const std::string& game_ini_id) { m_game_ini_id = game_ini_id; }
|
||||
|
||||
|
@ -199,6 +198,8 @@ private:
|
|||
void* userdata);
|
||||
void DisplayWelcomeMessage();
|
||||
|
||||
void SetHardcoreMode();
|
||||
|
||||
template <typename T>
|
||||
void FilterApprovedIni(std::vector<T>& codes, const std::string& game_ini_id) const;
|
||||
template <typename T>
|
||||
|
@ -275,11 +276,21 @@ private:
|
|||
|
||||
#include <string>
|
||||
|
||||
namespace ActionReplay
|
||||
{
|
||||
struct ARCode;
|
||||
}
|
||||
|
||||
namespace DiscIO
|
||||
{
|
||||
class Volume;
|
||||
}
|
||||
|
||||
namespace Gecko
|
||||
{
|
||||
class GeckoCode;
|
||||
}
|
||||
|
||||
class AchievementManager
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Contains.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Core/Host.h"
|
||||
#include "DiscIO/Enums.h"
|
||||
|
@ -38,17 +39,13 @@ static std::optional<DiscIO::Language> TryParseLanguage(const std::string& local
|
|||
// Special handling of Chinese due to its two writing systems
|
||||
if (split_locale[0] == "zh")
|
||||
{
|
||||
const auto locale_contains = [&split_locale](std::string_view str) {
|
||||
return std::find(split_locale.cbegin(), split_locale.cend(), str) != split_locale.cend();
|
||||
};
|
||||
|
||||
if (locale_contains("Hans"))
|
||||
if (Common::Contains(split_locale, "Hans"))
|
||||
return DiscIO::Language::SimplifiedChinese;
|
||||
if (locale_contains("Hant"))
|
||||
if (Common::Contains(split_locale, "Hant"))
|
||||
return DiscIO::Language::TraditionalChinese;
|
||||
|
||||
// Mainland China and Singapore use simplified characters
|
||||
if (locale_contains("CN") || locale_contains("SG"))
|
||||
if (Common::Contains(split_locale, "CN") || Common::Contains(split_locale, "SG"))
|
||||
return DiscIO::Language::SimplifiedChinese;
|
||||
else
|
||||
return DiscIO::Language::TraditionalChinese;
|
||||
|
@ -79,11 +76,9 @@ static DiscIO::Language ComputeDefaultLanguage()
|
|||
|
||||
static std::optional<std::string> TryParseCountryCode(const std::string& locale)
|
||||
{
|
||||
const auto is_upper = [](char c) { return std::isupper(c, std::locale::classic()); };
|
||||
|
||||
for (const std::string& part : SplitString(locale, '-'))
|
||||
{
|
||||
if (part.size() == 2 && is_upper(part[0]) && is_upper(part[1]))
|
||||
if (part.size() == 2 && Common::IsUpper(part[0]) && Common::IsUpper(part[1]))
|
||||
return part;
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ const Info<bool> GFX_CACHE_HIRES_TEXTURES{{System::GFX, "Settings", "CacheHiresT
|
|||
const Info<bool> GFX_DUMP_EFB_TARGET{{System::GFX, "Settings", "DumpEFBTarget"}, false};
|
||||
const Info<bool> GFX_DUMP_XFB_TARGET{{System::GFX, "Settings", "DumpXFBTarget"}, false};
|
||||
const Info<bool> GFX_DUMP_FRAMES_AS_IMAGES{{System::GFX, "Settings", "DumpFramesAsImages"}, false};
|
||||
const Info<bool> GFX_USE_FFV1{{System::GFX, "Settings", "UseFFV1"}, false};
|
||||
const Info<bool> GFX_USE_LOSSLESS{{System::GFX, "Settings", "UseLossless"}, false};
|
||||
const Info<std::string> GFX_DUMP_FORMAT{{System::GFX, "Settings", "DumpFormat"}, "avi"};
|
||||
const Info<std::string> GFX_DUMP_CODEC{{System::GFX, "Settings", "DumpCodec"}, ""};
|
||||
const Info<std::string> GFX_DUMP_PIXEL_FORMAT{{System::GFX, "Settings", "DumpPixelFormat"}, ""};
|
||||
|
|
|
@ -62,7 +62,7 @@ extern const Info<bool> GFX_CACHE_HIRES_TEXTURES;
|
|||
extern const Info<bool> GFX_DUMP_EFB_TARGET;
|
||||
extern const Info<bool> GFX_DUMP_XFB_TARGET;
|
||||
extern const Info<bool> GFX_DUMP_FRAMES_AS_IMAGES;
|
||||
extern const Info<bool> GFX_USE_FFV1;
|
||||
extern const Info<bool> GFX_USE_LOSSLESS;
|
||||
extern const Info<std::string> GFX_DUMP_FORMAT;
|
||||
extern const Info<std::string> GFX_DUMP_CODEC;
|
||||
extern const Info<std::string> GFX_DUMP_PIXEL_FORMAT;
|
||||
|
|
|
@ -114,9 +114,8 @@ public:
|
|||
for (const auto& value : section_map)
|
||||
{
|
||||
const Config::Location location{system.first, section_name, value.first};
|
||||
const bool load_disallowed =
|
||||
std::any_of(begin(s_setting_disallowed), end(s_setting_disallowed),
|
||||
[&location](const Config::Location* l) { return *l == location; });
|
||||
const bool load_disallowed = std::ranges::any_of(
|
||||
s_setting_disallowed, [&location](const auto* l) { return *l == location; });
|
||||
if (load_disallowed)
|
||||
continue;
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
#include "Common/Contains.h"
|
||||
#include "Core/Config/WiimoteSettings.h"
|
||||
|
||||
namespace ConfigLoaders
|
||||
|
@ -15,8 +16,7 @@ bool IsSettingSaveable(const Config::Location& config_location)
|
|||
static constexpr std::array systems_not_saveable = {Config::System::GCPad, Config::System::WiiPad,
|
||||
Config::System::GCKeyboard};
|
||||
|
||||
if (std::find(begin(systems_not_saveable), end(systems_not_saveable), config_location.system) ==
|
||||
end(systems_not_saveable))
|
||||
if (!Common::Contains(systems_not_saveable, config_location.system))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -27,9 +27,8 @@ bool IsSettingSaveable(const Config::Location& config_location)
|
|||
&Config::WIIMOTE_BB_SOURCE.GetLocation(),
|
||||
};
|
||||
|
||||
return std::any_of(begin(s_setting_saveable), end(s_setting_saveable),
|
||||
[&config_location](const Config::Location* location) {
|
||||
return *location == config_location;
|
||||
});
|
||||
return std::ranges::any_of(s_setting_saveable, [&config_location](const auto* location) {
|
||||
return *location == config_location;
|
||||
});
|
||||
}
|
||||
} // namespace ConfigLoaders
|
||||
|
|
|
@ -41,6 +41,7 @@ static void LoadFromDTM(Config::Layer* config_layer, Movie::DTMHeader* dtm)
|
|||
else
|
||||
config_layer->Set(Config::MAIN_GC_LANGUAGE, static_cast<int>(dtm->language));
|
||||
config_layer->Set(Config::SYSCONF_WIDESCREEN, dtm->bWidescreen);
|
||||
config_layer->Set(Config::SYSCONF_COUNTRY, dtm->countryCode);
|
||||
|
||||
config_layer->Set(Config::GFX_HACK_EFB_ACCESS_ENABLE, dtm->bEFBAccessEnable);
|
||||
config_layer->Set(Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM, dtm->bSkipEFBCopyToRam);
|
||||
|
@ -69,6 +70,7 @@ void SaveToDTM(Movie::DTMHeader* dtm)
|
|||
else
|
||||
dtm->language = Config::Get(Config::MAIN_GC_LANGUAGE);
|
||||
dtm->bWidescreen = Config::Get(Config::SYSCONF_WIDESCREEN);
|
||||
dtm->countryCode = Config::Get(Config::SYSCONF_COUNTRY);
|
||||
|
||||
dtm->bEFBAccessEnable = Config::Get(Config::GFX_HACK_EFB_ACCESS_ENABLE);
|
||||
dtm->bSkipEFBCopyToRam = Config::Get(Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM);
|
||||
|
|
|
@ -75,6 +75,7 @@
|
|||
#include "Core/State.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/WiiRoot.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
|
||||
#ifdef USE_MEMORYWATCHER
|
||||
#include "Core/MemoryWatcher.h"
|
||||
|
@ -194,7 +195,7 @@ void DisplayMessage(std::string message, int time_in_ms)
|
|||
return;
|
||||
|
||||
// Actually displaying non-ASCII could cause things to go pear-shaped
|
||||
if (!std::all_of(message.begin(), message.end(), Common::IsPrintableCharacter))
|
||||
if (!std::ranges::all_of(message, Common::IsPrintableCharacter))
|
||||
return;
|
||||
|
||||
OSD::AddMessage(std::move(message), time_in_ms);
|
||||
|
@ -385,8 +386,9 @@ static void CpuThread(Core::System& system, const std::optional<std::string>& sa
|
|||
// The JIT need to be able to intercept faults, both for fastmem and for the BLR optimization.
|
||||
const bool exception_handler = EMM::IsExceptionHandlerSupported();
|
||||
if (exception_handler)
|
||||
{
|
||||
EMM::InstallExceptionHandler();
|
||||
|
||||
}
|
||||
#ifdef USE_MEMORYWATCHER
|
||||
s_memory_watcher = std::make_unique<MemoryWatcher>();
|
||||
#endif
|
||||
|
@ -689,6 +691,9 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
|
|||
cpuThreadFunc(system, savestate_path, delete_savestate);
|
||||
}
|
||||
|
||||
if (cpuThreadFunc == CpuThread)
|
||||
system.GetMemory().InitDirtyPages();
|
||||
|
||||
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Stopping GDB ..."));
|
||||
GDBStub::Deinit();
|
||||
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "GDB stopped."));
|
||||
|
|
|
@ -776,14 +776,11 @@ bool DSPAssembler::AssemblePass(const std::string& text, int pass)
|
|||
m_location.line_num = 0;
|
||||
m_cur_pass = pass;
|
||||
|
||||
#define LINEBUF_SIZE 1024
|
||||
char line[LINEBUF_SIZE] = {0};
|
||||
while (!m_failed && !fsrc.fail() && !fsrc.eof())
|
||||
constexpr int LINEBUF_SIZE = 1024;
|
||||
char line[LINEBUF_SIZE] = {};
|
||||
while (!m_failed && fsrc.getline(line, LINEBUF_SIZE))
|
||||
{
|
||||
int opcode_size = 0;
|
||||
fsrc.getline(line, LINEBUF_SIZE);
|
||||
if (fsrc.fail())
|
||||
break;
|
||||
|
||||
m_location.line_text = line;
|
||||
m_location.line_num++;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <chrono>
|
||||
#include <regex>
|
||||
|
||||
#include "Common/Contains.h"
|
||||
#include "Common/Event.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/Debugger/PPCDebugInterface.h"
|
||||
|
@ -254,12 +255,9 @@ HitType CodeTrace::TraceLogic(const TraceOutput& current_instr, bool first_hit)
|
|||
// The reg_itr will be used later for erasing.
|
||||
auto reg_itr = std::ranges::find(m_reg_autotrack, instr.reg0);
|
||||
const bool match_reg123 =
|
||||
(!instr.reg1.empty() && std::find(m_reg_autotrack.begin(), m_reg_autotrack.end(),
|
||||
instr.reg1) != m_reg_autotrack.end()) ||
|
||||
(!instr.reg2.empty() && std::find(m_reg_autotrack.begin(), m_reg_autotrack.end(),
|
||||
instr.reg2) != m_reg_autotrack.end()) ||
|
||||
(!instr.reg3.empty() && std::find(m_reg_autotrack.begin(), m_reg_autotrack.end(),
|
||||
instr.reg3) != m_reg_autotrack.end());
|
||||
(!instr.reg1.empty() && Common::Contains(m_reg_autotrack, instr.reg1)) ||
|
||||
(!instr.reg2.empty() && Common::Contains(m_reg_autotrack, instr.reg2)) ||
|
||||
(!instr.reg3.empty() && Common::Contains(m_reg_autotrack, instr.reg3));
|
||||
const bool match_reg0 = reg_itr != m_reg_autotrack.end();
|
||||
|
||||
if (!match_reg0 && !match_reg123 && !mem_hit)
|
||||
|
@ -267,8 +265,8 @@ HitType CodeTrace::TraceLogic(const TraceOutput& current_instr, bool first_hit)
|
|||
|
||||
// Checks if the intstruction is a type that needs special handling.
|
||||
const auto CompareInstruction = [](std::string_view instruction, const auto& type_compare) {
|
||||
return std::any_of(type_compare.begin(), type_compare.end(),
|
||||
[&instruction](std::string_view s) { return instruction.starts_with(s); });
|
||||
return std::ranges::any_of(
|
||||
type_compare, [&instruction](std::string_view s) { return instruction.starts_with(s); });
|
||||
};
|
||||
|
||||
// Exclusions from updating tracking logic. mt operations are too complex and specialized.
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <fmt/format.h>
|
||||
|
||||
#include "Common/Align.h"
|
||||
#include "Common/Contains.h"
|
||||
#include "Common/GekkoDisassembler.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
|
@ -253,7 +254,7 @@ Common::Debug::Threads PPCDebugInterface::GetThreads(const Core::CPUThreadGuard&
|
|||
const auto insert_threads = [&guard, &threads, &visited_addrs](u32 addr, auto get_next_addr) {
|
||||
while (addr != 0 && PowerPC::MMU::HostIsRAMAddress(guard, addr))
|
||||
{
|
||||
if (std::find(visited_addrs.begin(), visited_addrs.end(), addr) != visited_addrs.end())
|
||||
if (Common::Contains(visited_addrs, addr))
|
||||
break;
|
||||
visited_addrs.push_back(addr);
|
||||
auto thread = std::make_unique<Core::Debug::OSThreadView>(guard, addr);
|
||||
|
|
|
@ -17,13 +17,10 @@
|
|||
|
||||
namespace Gecko
|
||||
{
|
||||
std::vector<GeckoCode> DownloadCodes(std::string gametdb_id, bool* succeeded, bool use_https)
|
||||
std::vector<GeckoCode> DownloadCodes(std::string gametdb_id, bool* succeeded)
|
||||
{
|
||||
// TODO: Fix https://bugs.dolphin-emu.org/issues/11772 so we don't need this workaround
|
||||
const std::string protocol = use_https ? "https://" : "http://";
|
||||
|
||||
// codes.rc24.xyz is a mirror of the now defunct geckocodes.org.
|
||||
std::string endpoint{protocol + "codes.rc24.xyz/txt.php?txt=" + gametdb_id};
|
||||
std::string endpoint{"https://codes.rc24.xyz/txt.php?txt=" + gametdb_id};
|
||||
Common::HttpRequest http;
|
||||
|
||||
// The server always redirects once to the same location.
|
||||
|
|
|
@ -17,8 +17,7 @@ class IniFile;
|
|||
namespace Gecko
|
||||
{
|
||||
std::vector<GeckoCode> LoadCodes(const Common::IniFile& globalIni, const Common::IniFile& localIni);
|
||||
std::vector<GeckoCode> DownloadCodes(std::string gametdb_id, bool* succeeded,
|
||||
bool use_https = true);
|
||||
std::vector<GeckoCode> DownloadCodes(std::string gametdb_id, bool* succeeded);
|
||||
void SaveCodes(Common::IniFile& inifile, const std::vector<GeckoCode>& gcodes);
|
||||
|
||||
std::optional<GeckoCode::Code> DeserializeLine(const std::string& line);
|
||||
|
|
|
@ -686,7 +686,7 @@ bool CEXIETHERNET::BuiltInBBAInterface::SendFrame(const u8* frame, u32 size)
|
|||
}
|
||||
|
||||
default:
|
||||
ERROR_LOG_FMT(SP1, "Unsupported EtherType {#06x}", *ethertype);
|
||||
ERROR_LOG_FMT(SP1, "Unsupported EtherType {:#06x}", *ethertype);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -101,8 +101,8 @@ std::pair<GCMemcardErrorCode, std::optional<GCMemcard>> GCMemcard::Open(std::str
|
|||
MBIT_SIZE_MEMORY_CARD_2043,
|
||||
}};
|
||||
|
||||
if (!std::any_of(valid_megabits.begin(), valid_megabits.end(),
|
||||
[filesize_megabits](u64 mbits) { return mbits == filesize_megabits; }))
|
||||
if (!std::ranges::any_of(valid_megabits,
|
||||
[filesize_megabits](u64 mbits) { return mbits == filesize_megabits; }))
|
||||
{
|
||||
error_code.Set(GCMemcardValidityIssues::INVALID_CARD_SIZE);
|
||||
return std::make_pair(error_code, std::nullopt);
|
||||
|
@ -1296,8 +1296,8 @@ GCMemcardErrorCode Header::CheckForErrors(u16 card_size_mbits) const
|
|||
error_code.Set(GCMemcardValidityIssues::MISMATCHED_CARD_SIZE);
|
||||
|
||||
// unused areas, should always be filled with 0xFF
|
||||
if (std::any_of(m_unused_1.begin(), m_unused_1.end(), [](u8 val) { return val != 0xFF; }) ||
|
||||
std::any_of(m_unused_2.begin(), m_unused_2.end(), [](u8 val) { return val != 0xFF; }))
|
||||
if (std::ranges::any_of(m_unused_1, [](u8 val) { return val != 0xFF; }) ||
|
||||
std::ranges::any_of(m_unused_2, [](u8 val) { return val != 0xFF; }))
|
||||
{
|
||||
error_code.Set(GCMemcardValidityIssues::DATA_IN_UNUSED_AREA);
|
||||
}
|
||||
|
@ -1361,7 +1361,7 @@ GCMemcardErrorCode Directory::CheckForErrors() const
|
|||
error_code.Set(GCMemcardValidityIssues::INVALID_CHECKSUM);
|
||||
|
||||
// unused area, should always be filled with 0xFF
|
||||
if (std::any_of(m_padding.begin(), m_padding.end(), [](u8 val) { return val != 0xFF; }))
|
||||
if (std::ranges::any_of(m_padding, [](u8 val) { return val != 0xFF; }))
|
||||
error_code.Set(GCMemcardValidityIssues::DATA_IN_UNUSED_AREA);
|
||||
|
||||
return error_code;
|
||||
|
|
|
@ -82,9 +82,9 @@ void Shutdown(Core::System& system)
|
|||
system.GetCoreTiming().Shutdown();
|
||||
}
|
||||
|
||||
void DoState(Core::System& system, PointerWrap& p)
|
||||
void DoState(Core::System& system, PointerWrap& p, bool delta)
|
||||
{
|
||||
system.GetMemory().DoState(p);
|
||||
system.GetMemory().DoState(p, delta);
|
||||
p.DoMarker("Memory");
|
||||
system.GetMemoryInterface().DoState(p);
|
||||
p.DoMarker("MemoryInterface");
|
||||
|
|
|
@ -14,5 +14,5 @@ namespace HW
|
|||
{
|
||||
void Init(Core::System& system, const Sram* override_sram);
|
||||
void Shutdown(Core::System& system);
|
||||
void DoState(Core::System& system, PointerWrap& p);
|
||||
void DoState(Core::System& system, PointerWrap& p, bool delta);
|
||||
} // namespace HW
|
||||
|
|
|
@ -47,6 +47,57 @@ MemoryManager::MemoryManager(Core::System& system) : m_system(system)
|
|||
|
||||
MemoryManager::~MemoryManager() = default;
|
||||
|
||||
u64 MemoryManager::GetDirtyPageIndexFromAddress(u64 address)
|
||||
{
|
||||
const size_t page_size = Common::PageSize();
|
||||
const size_t page_mask = page_size - 1;
|
||||
return address & ~page_mask;
|
||||
}
|
||||
|
||||
bool MemoryManager::HandleFault(uintptr_t fault_address)
|
||||
{
|
||||
u8* fault_address_bytes = reinterpret_cast<u8*>(fault_address);
|
||||
if (!IsAddressInEmulatedMemory(fault_address_bytes) || IsPageDirty(fault_address))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
SetPageDirtyBit(fault_address, 0x1, true);
|
||||
bool change_protection =
|
||||
m_arena.VirtualProtectMemoryRegion(fault_address_bytes, 0x1, PAGE_READWRITE);
|
||||
|
||||
if (!change_protection)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MemoryManager::WriteProtectPhysicalMemoryRegions()
|
||||
{
|
||||
for (const PhysicalMemoryRegion& region : m_physical_regions)
|
||||
{
|
||||
if (!region.active || !region.track)
|
||||
continue;
|
||||
|
||||
bool change_protection =
|
||||
m_arena.VirtualProtectMemoryRegion((*region.out_pointer), region.size, PAGE_READONLY);
|
||||
|
||||
if (!change_protection)
|
||||
{
|
||||
PanicAlertFmt("Memory::WriteProtectPhysicalMemoryRegions(): Failed to write protect for "
|
||||
"this block of memory at 0x{:08X}.",
|
||||
reinterpret_cast<u64>(*region.out_pointer));
|
||||
}
|
||||
const size_t page_size = Common::PageSize();
|
||||
const intptr_t out_pointer = reinterpret_cast<intptr_t>(*region.out_pointer);
|
||||
for (size_t i = out_pointer; i < region.size; i += page_size)
|
||||
{
|
||||
m_dirty_pages[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryManager::InitMMIO(bool is_wii)
|
||||
{
|
||||
m_mmio_mapping = std::make_unique<MMIO::Mapping>();
|
||||
|
@ -71,6 +122,11 @@ void MemoryManager::InitMMIO(bool is_wii)
|
|||
}
|
||||
}
|
||||
|
||||
void MemoryManager::InitDirtyPages()
|
||||
{
|
||||
WriteProtectPhysicalMemoryRegions();
|
||||
}
|
||||
|
||||
void MemoryManager::Init()
|
||||
{
|
||||
const auto get_mem1_size = [] {
|
||||
|
@ -95,13 +151,14 @@ void MemoryManager::Init()
|
|||
m_exram_mask = GetExRamSize() - 1;
|
||||
|
||||
m_physical_regions[0] = PhysicalMemoryRegion{
|
||||
&m_ram, 0x00000000, GetRamSize(), PhysicalMemoryRegion::ALWAYS, 0, false};
|
||||
&m_ram, 0x00000000, GetRamSize(), PhysicalMemoryRegion::ALWAYS, 0, false, true};
|
||||
m_physical_regions[1] = PhysicalMemoryRegion{
|
||||
&m_l1_cache, 0xE0000000, GetL1CacheSize(), PhysicalMemoryRegion::ALWAYS, 0, false};
|
||||
&m_l1_cache, 0xE0000000, GetL1CacheSize(), PhysicalMemoryRegion::ALWAYS, 0, false, false};
|
||||
m_physical_regions[2] = PhysicalMemoryRegion{
|
||||
&m_fake_vmem, 0x7E000000, GetFakeVMemSize(), PhysicalMemoryRegion::FAKE_VMEM, 0, false};
|
||||
&m_fake_vmem, 0x7E000000, GetFakeVMemSize(), PhysicalMemoryRegion::FAKE_VMEM, 0,
|
||||
false, false};
|
||||
m_physical_regions[3] = PhysicalMemoryRegion{
|
||||
&m_exram, 0x10000000, GetExRamSize(), PhysicalMemoryRegion::WII_ONLY, 0, false};
|
||||
&m_exram, 0x10000000, GetExRamSize(), PhysicalMemoryRegion::WII_ONLY, 0, false, true};
|
||||
|
||||
const bool wii = m_system.IsWii();
|
||||
const bool mmu = m_system.IsMMUMode();
|
||||
|
@ -164,6 +221,21 @@ bool MemoryManager::IsAddressInFastmemArea(const u8* address) const
|
|||
return address >= m_fastmem_arena && address < m_fastmem_arena + m_fastmem_arena_size;
|
||||
}
|
||||
|
||||
bool MemoryManager::IsAddressInEmulatedMemory(const u8* address) const
|
||||
{
|
||||
for (const PhysicalMemoryRegion& region : m_physical_regions)
|
||||
{
|
||||
if (!region.active || !region.track)
|
||||
continue;
|
||||
|
||||
if (address >= *region.out_pointer && address < *region.out_pointer + region.size)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MemoryManager::InitFastmemArena()
|
||||
{
|
||||
// Here we set up memory mappings for fastmem. The basic idea of fastmem is that we reserve 4 GiB
|
||||
|
@ -290,7 +362,7 @@ void MemoryManager::UpdateLogicalMemory(const PowerPC::BatTable& dbat_table)
|
|||
}
|
||||
}
|
||||
|
||||
void MemoryManager::DoState(PointerWrap& p)
|
||||
void MemoryManager::DoState(PointerWrap& p, bool delta)
|
||||
{
|
||||
const u32 current_ram_size = GetRamSize();
|
||||
const u32 current_l1_cache_size = GetL1CacheSize();
|
||||
|
@ -327,28 +399,60 @@ void MemoryManager::DoState(PointerWrap& p)
|
|||
p.SetVerifyMode();
|
||||
return;
|
||||
}
|
||||
|
||||
p.DoArray(m_ram, current_ram_size);
|
||||
p.DoArray(m_l1_cache, current_l1_cache_size);
|
||||
p.DoMarker("Memory RAM");
|
||||
if (current_have_fake_vmem)
|
||||
p.DoArray(m_fake_vmem, current_fake_vmem_size);
|
||||
p.DoMarker("Memory FakeVMEM");
|
||||
if (current_have_exram)
|
||||
p.DoArray(m_exram, current_exram_size);
|
||||
p.DoMarker("Memory EXRAM");
|
||||
if (delta)
|
||||
{
|
||||
const u32 page_size = static_cast<u32>(Common::PageSize());
|
||||
p.Do(m_dirty_pages);
|
||||
for (size_t i = 0; i < current_ram_size; i += page_size)
|
||||
{
|
||||
if (IsPageDirty(reinterpret_cast<uintptr_t>(&m_ram[i])))
|
||||
{
|
||||
p.DoArray(m_ram + i, page_size);
|
||||
}
|
||||
}
|
||||
p.DoArray(m_l1_cache, current_l1_cache_size);
|
||||
p.DoMarker("Memory RAM");
|
||||
if (current_have_fake_vmem)
|
||||
{
|
||||
p.DoArray(m_fake_vmem, current_fake_vmem_size);
|
||||
}
|
||||
p.DoMarker("Memory FakeVMEM");
|
||||
if (current_have_exram)
|
||||
{
|
||||
for (size_t i = 0; i < current_exram_size; i += page_size)
|
||||
{
|
||||
if (IsPageDirty(reinterpret_cast<uintptr_t>(&m_exram[i])))
|
||||
{
|
||||
p.DoArray(m_exram + i, page_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
p.DoMarker("Memory EXRAM");
|
||||
}
|
||||
else
|
||||
{
|
||||
p.DoArray(m_ram, current_ram_size);
|
||||
p.DoArray(m_l1_cache, current_l1_cache_size);
|
||||
p.DoMarker("Memory RAM");
|
||||
if (current_have_fake_vmem)
|
||||
p.DoArray(m_fake_vmem, current_fake_vmem_size);
|
||||
p.DoMarker("Memory FakeVMEM");
|
||||
if (current_have_exram)
|
||||
p.DoArray(m_exram, current_exram_size);
|
||||
p.DoMarker("Memory EXRAM");
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryManager::Shutdown()
|
||||
{
|
||||
ShutdownFastmemArena();
|
||||
m_dirty_pages.clear();
|
||||
|
||||
m_is_initialized = false;
|
||||
for (const PhysicalMemoryRegion& region : m_physical_regions)
|
||||
{
|
||||
if (!region.active)
|
||||
continue;
|
||||
|
||||
m_arena.ReleaseView(*region.out_pointer, region.size);
|
||||
*region.out_pointer = nullptr;
|
||||
}
|
||||
|
@ -573,4 +677,22 @@ void MemoryManager::Write_U64_Swap(u64 value, u32 address)
|
|||
CopyToEmu(address, &value, sizeof(value));
|
||||
}
|
||||
|
||||
bool MemoryManager::IsPageDirty(uintptr_t address)
|
||||
{
|
||||
return m_dirty_pages[GetDirtyPageIndexFromAddress(address)];
|
||||
}
|
||||
|
||||
void MemoryManager::SetPageDirtyBit(uintptr_t address, size_t size, bool dirty)
|
||||
{
|
||||
for (size_t i = 0; i < size; i++)
|
||||
{
|
||||
m_dirty_pages[GetDirtyPageIndexFromAddress(address + i)] = dirty;
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryManager::ResetDirtyPages()
|
||||
{
|
||||
WriteProtectPhysicalMemoryRegions();
|
||||
}
|
||||
|
||||
} // namespace Memory
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/MathUtil.h"
|
||||
|
@ -48,6 +49,7 @@ struct PhysicalMemoryRegion
|
|||
} flags;
|
||||
u32 shm_position;
|
||||
bool active;
|
||||
bool track;
|
||||
};
|
||||
|
||||
struct LogicalMemoryView
|
||||
|
@ -78,6 +80,7 @@ public:
|
|||
u32 GetExRamMask() const { return m_exram_mask; }
|
||||
|
||||
bool IsAddressInFastmemArea(const u8* address) const;
|
||||
bool IsAddressInEmulatedMemory(const u8* address) const;
|
||||
u8* GetPhysicalBase() const { return m_physical_base; }
|
||||
u8* GetLogicalBase() const { return m_logical_base; }
|
||||
u8* GetPhysicalPageMappingsBase() const { return m_physical_page_mappings_base; }
|
||||
|
@ -94,10 +97,11 @@ public:
|
|||
// Init and Shutdown
|
||||
bool IsInitialized() const { return m_is_initialized; }
|
||||
void Init();
|
||||
void InitDirtyPages();
|
||||
void Shutdown();
|
||||
bool InitFastmemArena();
|
||||
void ShutdownFastmemArena();
|
||||
void DoState(PointerWrap& p);
|
||||
void DoState(PointerWrap& p, bool delta);
|
||||
|
||||
void UpdateLogicalMemory(const PowerPC::BatTable& dbat_table);
|
||||
|
||||
|
@ -130,6 +134,13 @@ public:
|
|||
void Write_U32_Swap(u32 var, u32 address);
|
||||
void Write_U64_Swap(u64 var, u32 address);
|
||||
|
||||
bool IsPageDirty(uintptr_t address);
|
||||
void SetPageDirtyBit(uintptr_t address, size_t size, bool dirty);
|
||||
void ResetDirtyPages();
|
||||
bool HandleFault(uintptr_t fault_address);
|
||||
|
||||
std::map<u64, u8>& GetDirtyPages() { return m_dirty_pages; }
|
||||
|
||||
// Templated functions for byteswapped copies.
|
||||
template <typename T>
|
||||
void CopyFromEmuSwapped(T* data, u32 address, size_t size) const
|
||||
|
@ -254,6 +265,11 @@ private:
|
|||
|
||||
Core::System& m_system;
|
||||
|
||||
std::map<u64, u8> m_dirty_pages;
|
||||
|
||||
u64 GetDirtyPageIndexFromAddress(u64 address);
|
||||
void WriteProtectPhysicalMemoryRegions();
|
||||
|
||||
void InitMMIO(bool is_wii);
|
||||
};
|
||||
} // namespace Memory
|
||||
|
|
|
@ -613,8 +613,7 @@ void WiimoteScanner::SetScanMode(WiimoteScanMode scan_mode)
|
|||
bool WiimoteScanner::IsReady() const
|
||||
{
|
||||
std::lock_guard lg(m_backends_mutex);
|
||||
return std::any_of(m_backends.begin(), m_backends.end(),
|
||||
[](const auto& backend) { return backend->IsReady(); });
|
||||
return std::ranges::any_of(m_backends, &WiimoteScannerBackend::IsReady);
|
||||
}
|
||||
|
||||
static void CheckForDisconnectedWiimotes()
|
||||
|
|
|
@ -98,8 +98,8 @@ bool IOCtlVRequest::HasNumberOfValidVectors(const size_t in_count, const size_t
|
|||
return false;
|
||||
|
||||
auto IsValidVector = [](const auto& vector) { return vector.size == 0 || vector.address != 0; };
|
||||
return std::all_of(in_vectors.begin(), in_vectors.end(), IsValidVector) &&
|
||||
std::all_of(io_vectors.begin(), io_vectors.end(), IsValidVector);
|
||||
return std::ranges::all_of(in_vectors, IsValidVector) &&
|
||||
std::ranges::all_of(io_vectors, IsValidVector);
|
||||
}
|
||||
|
||||
void IOCtlRequest::Log(std::string_view device_name, Common::Log::LogType type,
|
||||
|
|
|
@ -298,7 +298,7 @@ std::string TMDReader::GetGameID() const
|
|||
std::memcpy(game_id, m_bytes.data() + offsetof(TMDHeader, title_id) + 4, 4);
|
||||
std::memcpy(game_id + 4, m_bytes.data() + offsetof(TMDHeader, group_id), 2);
|
||||
|
||||
if (std::all_of(std::begin(game_id), std::end(game_id), Common::IsPrintableCharacter))
|
||||
if (std::ranges::all_of(game_id, Common::IsPrintableCharacter))
|
||||
return std::string(game_id, sizeof(game_id));
|
||||
|
||||
return fmt::format("{:016x}", GetTitleId());
|
||||
|
|
|
@ -79,8 +79,7 @@ static bool IsValidPartOfTitleID(const std::string& string)
|
|||
{
|
||||
if (string.length() != 8)
|
||||
return false;
|
||||
return std::all_of(string.begin(), string.end(),
|
||||
[](const auto character) { return std::isxdigit(character) != 0; });
|
||||
return std::ranges::all_of(string, Common::IsXDigit);
|
||||
}
|
||||
|
||||
static std::vector<u64> GetTitlesInTitleOrImport(FS::FileSystem* fs, const std::string& titles_dir)
|
||||
|
|
|
@ -464,7 +464,7 @@ static bool HasAllRequiredContents(Kernel& ios, const ES::TMDReader& tmd)
|
|||
const u64 title_id = tmd.GetTitleId();
|
||||
const std::vector<ES::Content> contents = tmd.GetContents();
|
||||
const ES::SharedContentMap shared_content_map{ios.GetFSCore()};
|
||||
return std::all_of(contents.cbegin(), contents.cend(), [&](const ES::Content& content) {
|
||||
return std::ranges::all_of(contents, [&](const ES::Content& content) {
|
||||
if (content.IsOptional())
|
||||
return true;
|
||||
|
||||
|
@ -878,7 +878,7 @@ ReturnCode ESCore::DeleteSharedContent(const std::array<u8, 20>& sha1) const
|
|||
|
||||
// Check whether the shared content is used by a system title.
|
||||
const std::vector<u64> titles = GetInstalledTitles();
|
||||
const bool is_used_by_system_title = std::any_of(titles.begin(), titles.end(), [&](u64 id) {
|
||||
const bool is_used_by_system_title = std::ranges::any_of(titles, [&](u64 id) {
|
||||
if (!ES::IsTitleType(id, ES::TitleType::System))
|
||||
return false;
|
||||
|
||||
|
@ -887,8 +887,8 @@ ReturnCode ESCore::DeleteSharedContent(const std::array<u8, 20>& sha1) const
|
|||
return true;
|
||||
|
||||
const auto contents = tmd.GetContents();
|
||||
return std::any_of(contents.begin(), contents.end(),
|
||||
[&sha1](const auto& content) { return content.sha1 == sha1; });
|
||||
return std::ranges::any_of(contents,
|
||||
[&sha1](const auto& content) { return content.sha1 == sha1; });
|
||||
});
|
||||
|
||||
// Any shared content used by a system title cannot be deleted.
|
||||
|
|
|
@ -26,7 +26,7 @@ bool IsValidNonRootPath(std::string_view path)
|
|||
bool IsValidFilename(std::string_view filename)
|
||||
{
|
||||
return filename.length() <= MaxFilenameLength &&
|
||||
!std::any_of(filename.begin(), filename.end(), [](char c) { return c == '/'; });
|
||||
!std::ranges::any_of(filename, [](char c) { return c == '/'; });
|
||||
}
|
||||
|
||||
SplitPathResult SplitPathAndBasename(std::string_view path)
|
||||
|
|
|
@ -461,8 +461,7 @@ ResultCode HostFileSystem::Format(Uid uid)
|
|||
ResultCode HostFileSystem::CreateFileOrDirectory(Uid uid, Gid gid, const std::string& path,
|
||||
FileAttribute attr, Modes modes, bool is_file)
|
||||
{
|
||||
if (!IsValidNonRootPath(path) ||
|
||||
!std::all_of(path.begin(), path.end(), Common::IsPrintableCharacter))
|
||||
if (!IsValidNonRootPath(path) || !std::ranges::all_of(path, Common::IsPrintableCharacter))
|
||||
{
|
||||
return ResultCode::Invalid;
|
||||
}
|
||||
|
@ -516,14 +515,14 @@ ResultCode HostFileSystem::CreateDirectory(Uid uid, Gid gid, const std::string&
|
|||
|
||||
bool HostFileSystem::IsFileOpened(const std::string& path) const
|
||||
{
|
||||
return std::any_of(m_handles.begin(), m_handles.end(), [&path](const Handle& handle) {
|
||||
return std::ranges::any_of(m_handles, [&path](const Handle& handle) {
|
||||
return handle.opened && handle.wii_path == path;
|
||||
});
|
||||
}
|
||||
|
||||
bool HostFileSystem::IsDirectoryInUse(const std::string& path) const
|
||||
{
|
||||
return std::any_of(m_handles.begin(), m_handles.end(), [&path](const Handle& handle) {
|
||||
return std::ranges::any_of(m_handles, [&path](const Handle& handle) {
|
||||
return handle.opened && handle.wii_path.starts_with(path);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -58,8 +58,8 @@ bool WC24FriendList::CheckFriendList() const
|
|||
|
||||
bool WC24FriendList::DoesFriendExist(u64 friend_id) const
|
||||
{
|
||||
return std::any_of(m_data.friend_codes.cbegin(), m_data.friend_codes.cend(),
|
||||
[&friend_id](const u64 v) { return v == friend_id; });
|
||||
return std::ranges::any_of(m_data.friend_codes,
|
||||
[&friend_id](const u64 v) { return v == friend_id; });
|
||||
}
|
||||
|
||||
std::vector<u64> WC24FriendList::GetUnconfirmedFriends() const
|
||||
|
|
|
@ -1085,10 +1085,10 @@ void WiiSockMan::UpdatePollCommands()
|
|||
std::vector<int> original_order(pfds.size());
|
||||
std::iota(original_order.begin(), original_order.end(), 0);
|
||||
// Select indices with valid fds
|
||||
auto mid = std::partition(original_order.begin(), original_order.end(), [&](auto i) {
|
||||
const auto partition_result = std::ranges::partition(original_order, [&](auto i) {
|
||||
return GetHostSocket(memory.Read_U32(pcmd.buffer_out + 0xc * i)) >= 0;
|
||||
});
|
||||
const auto n_valid = std::distance(original_order.begin(), mid);
|
||||
const auto n_valid = std::distance(original_order.begin(), partition_result.begin());
|
||||
|
||||
// Move all the valid pollfds to the front of the vector
|
||||
for (auto i = 0; i < n_valid; ++i)
|
||||
|
|
|
@ -74,7 +74,7 @@ bool Device::HasClass(const u8 device_class) const
|
|||
if (GetDeviceDescriptor().bDeviceClass == device_class)
|
||||
return true;
|
||||
const auto interfaces = GetInterfaces(0);
|
||||
return std::any_of(interfaces.begin(), interfaces.end(), [device_class](const auto& interface) {
|
||||
return std::ranges::any_of(interfaces, [device_class](const auto& interface) {
|
||||
return interface.bInterfaceClass == device_class;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -234,7 +234,7 @@ std::optional<IPCReply> OH0::RegisterClassChangeHook(const IOCtlVRequest& reques
|
|||
|
||||
bool OH0::HasDeviceWithVidPid(const u16 vid, const u16 pid) const
|
||||
{
|
||||
return std::any_of(m_devices.begin(), m_devices.end(), [=](const auto& device) {
|
||||
return std::ranges::any_of(m_devices, [=](const auto& device) {
|
||||
return device.second->GetVid() == vid && device.second->GetPid() == pid;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -380,9 +380,9 @@ bool IsEmulated(u32 major_version)
|
|||
if (major_version == static_cast<u32>(Titles::BC & 0xffffffff))
|
||||
return true;
|
||||
|
||||
return std::any_of(
|
||||
ios_memory_values.begin(), ios_memory_values.end(),
|
||||
[major_version](const MemoryValues& values) { return values.ios_number == major_version; });
|
||||
return std::ranges::any_of(ios_memory_values, [major_version](const MemoryValues& values) {
|
||||
return values.ios_number == major_version;
|
||||
});
|
||||
}
|
||||
|
||||
bool IsEmulated(u64 title_id)
|
||||
|
|
|
@ -375,8 +375,8 @@ s32 WFSSRVDevice::Rename(std::string source, std::string dest) const
|
|||
|
||||
INFO_LOG_FMT(IOS_WFS, "IOCTL_WFS_RENAME: {} to {}", source, dest);
|
||||
|
||||
const bool opened = std::any_of(m_fds.begin(), m_fds.end(),
|
||||
[&](const auto& fd) { return fd.in_use && fd.path == source; });
|
||||
const bool opened =
|
||||
std::ranges::any_of(m_fds, [&](const auto& fd) { return fd.in_use && fd.path == source; });
|
||||
|
||||
if (opened)
|
||||
return WFS_FILE_IS_OPENED;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "Common/MsgHandler.h"
|
||||
#include "Common/Thread.h"
|
||||
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/MachineContext.h"
|
||||
#include "Core/PowerPC/JitInterface.h"
|
||||
#include "Core/System.h"
|
||||
|
@ -24,6 +25,7 @@
|
|||
#ifndef _WIN32
|
||||
#include <unistd.h> // Needed for _POSIX_VERSION
|
||||
#endif
|
||||
#include <Common/MemoryUtil.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#ifdef _M_X86_64
|
||||
|
@ -60,8 +62,12 @@ static LONG NTAPI Handler(PEXCEPTION_POINTERS pPtrs)
|
|||
// virtual address of the inaccessible data
|
||||
uintptr_t fault_address = (uintptr_t)pPtrs->ExceptionRecord->ExceptionInformation[1];
|
||||
SContext* ctx = pPtrs->ContextRecord;
|
||||
|
||||
if (Core::System::GetInstance().GetJitInterface().HandleFault(fault_address, ctx))
|
||||
Core::System& system = Core::System::GetInstance();
|
||||
if (system.GetMemory().HandleFault(fault_address))
|
||||
{
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
else if (system.GetJitInterface().HandleFault(fault_address, ctx))
|
||||
{
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ static std::array<u8, 20> ConvertGitRevisionToBytes(const std::string& revision)
|
|||
{
|
||||
std::array<u8, 20> revision_bytes{};
|
||||
|
||||
if (revision.size() % 2 == 0 && std::all_of(revision.begin(), revision.end(), ::isxdigit))
|
||||
if (revision.size() % 2 == 0 && std::ranges::all_of(revision, Common::IsXDigit))
|
||||
{
|
||||
// The revision string normally contains a git commit hash,
|
||||
// which is 40 hexadecimal digits long. In DTM files, each pair of
|
||||
|
@ -1081,11 +1081,11 @@ void MovieManager::LoadInput(const std::string& movie_path)
|
|||
std::vector<u8> movInput(m_current_byte);
|
||||
t_record.ReadArray(movInput.data(), movInput.size());
|
||||
|
||||
const auto result = std::mismatch(movInput.begin(), movInput.end(), m_temp_input.begin());
|
||||
const auto mismatch_result = std::ranges::mismatch(movInput, m_temp_input);
|
||||
|
||||
if (result.first != movInput.end())
|
||||
if (mismatch_result.in1 != movInput.end())
|
||||
{
|
||||
const ptrdiff_t mismatch_index = std::distance(movInput.begin(), result.first);
|
||||
const ptrdiff_t mismatch_index = std::distance(movInput.begin(), mismatch_result.in1);
|
||||
|
||||
// this is a "you did something wrong" alert for the user's benefit.
|
||||
// we'll try to say what's going on in excruciating detail, otherwise the user might not
|
||||
|
|
|
@ -132,7 +132,8 @@ struct DTMHeader
|
|||
bool bUseFMA;
|
||||
u8 GBAControllers; // GBA Controllers plugged in (the bits are ports 1-4)
|
||||
bool bWidescreen; // true indicates SYSCONF aspect ratio is 16:9, false for 4:3
|
||||
std::array<u8, 6> reserved; // Padding for any new config options
|
||||
u8 countryCode; // SYSCONF country code
|
||||
std::array<u8, 5> reserved; // Padding for any new config options
|
||||
std::array<char, 40> discChange; // Name of iso file to switch to, for two disc games.
|
||||
std::array<u8, 20> revision; // Git hash
|
||||
u32 DSPiromHash;
|
||||
|
|
|
@ -2486,8 +2486,8 @@ bool NetPlayClient::PlayerHasControllerMapped(const PlayerId pid) const
|
|||
{
|
||||
const auto mapping_matches_player_id = [pid](const PlayerId& mapping) { return mapping == pid; };
|
||||
|
||||
return std::any_of(m_pad_map.begin(), m_pad_map.end(), mapping_matches_player_id) ||
|
||||
std::any_of(m_wiimote_map.begin(), m_wiimote_map.end(), mapping_matches_player_id);
|
||||
return std::ranges::any_of(m_pad_map, mapping_matches_player_id) ||
|
||||
std::ranges::any_of(m_wiimote_map, mapping_matches_player_id);
|
||||
}
|
||||
|
||||
bool NetPlayClient::IsLocalPlayer(const PlayerId pid) const
|
||||
|
@ -2543,7 +2543,7 @@ bool NetPlayClient::DoAllPlayersHaveGame()
|
|||
{
|
||||
std::lock_guard lkp(m_crit.players);
|
||||
|
||||
return std::all_of(std::begin(m_players), std::end(m_players), [](auto entry) {
|
||||
return std::ranges::all_of(m_players, [](const auto& entry) {
|
||||
return entry.second.game_status == SyncIdentifierComparison::SameGame;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -237,7 +237,7 @@ static bool DecompressPacketIntoFolderInternal(sf::Packet& packet, const std::st
|
|||
if (name.find('\\') != std::string::npos)
|
||||
return false;
|
||||
#endif
|
||||
if (std::all_of(name.begin(), name.end(), [](char c) { return c == '.'; }))
|
||||
if (std::ranges::all_of(name, [](char c) { return c == '.'; }))
|
||||
return false;
|
||||
|
||||
bool is_folder;
|
||||
|
|
|
@ -1060,14 +1060,14 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player)
|
|||
{
|
||||
// we have all records for this frame
|
||||
|
||||
if (!std::all_of(timebases.begin(), timebases.end(), [&](std::pair<PlayerId, u64> pair) {
|
||||
if (!std::ranges::all_of(timebases, [&](std::pair<PlayerId, u64> pair) {
|
||||
return pair.second == timebases[0].second;
|
||||
}))
|
||||
{
|
||||
int pid_to_blame = 0;
|
||||
for (auto pair : timebases)
|
||||
{
|
||||
if (std::all_of(timebases.begin(), timebases.end(), [&](std::pair<PlayerId, u64> other) {
|
||||
if (std::ranges::all_of(timebases, [&](std::pair<PlayerId, u64> other) {
|
||||
return other.first == pair.first || other.second != pair.second;
|
||||
}))
|
||||
{
|
||||
|
@ -1467,14 +1467,12 @@ bool NetPlayServer::SetupNetSettings()
|
|||
|
||||
bool NetPlayServer::DoAllPlayersHaveIPLDump() const
|
||||
{
|
||||
return std::all_of(m_players.begin(), m_players.end(),
|
||||
[](const auto& p) { return p.second.has_ipl_dump; });
|
||||
return std::ranges::all_of(m_players, [](const auto& p) { return p.second.has_ipl_dump; });
|
||||
}
|
||||
|
||||
bool NetPlayServer::DoAllPlayersHaveHardwareFMA() const
|
||||
{
|
||||
return std::all_of(m_players.begin(), m_players.end(),
|
||||
[](const auto& p) { return p.second.has_hardware_fma; });
|
||||
return std::ranges::all_of(m_players, [](const auto& p) { return p.second.has_hardware_fma; });
|
||||
}
|
||||
|
||||
struct SaveSyncInfo
|
||||
|
@ -2234,8 +2232,8 @@ bool NetPlayServer::PlayerHasControllerMapped(const PlayerId pid) const
|
|||
{
|
||||
const auto mapping_matches_player_id = [pid](const PlayerId& mapping) { return mapping == pid; };
|
||||
|
||||
return std::any_of(m_pad_map.begin(), m_pad_map.end(), mapping_matches_player_id) ||
|
||||
std::any_of(m_wiimote_map.begin(), m_wiimote_map.end(), mapping_matches_player_id);
|
||||
return std::ranges::any_of(m_pad_map, mapping_matches_player_id) ||
|
||||
std::ranges::any_of(m_wiimote_map, mapping_matches_player_id);
|
||||
}
|
||||
|
||||
void NetPlayServer::AssignNewUserAPad(const Client& player)
|
||||
|
|
|
@ -371,7 +371,7 @@ bool MemChecks::OverlapsMemcheck(u32 address, u32 length) const
|
|||
const u32 page_end_suffix = length - 1;
|
||||
const u32 page_end_address = address | page_end_suffix;
|
||||
|
||||
return std::any_of(m_mem_checks.cbegin(), m_mem_checks.cend(), [&](const auto& mc) {
|
||||
return std::ranges::any_of(m_mem_checks, [&](const auto& mc) {
|
||||
return ((mc.start_address | page_end_suffix) == page_end_address ||
|
||||
(mc.end_address | page_end_suffix) == page_end_address) ||
|
||||
((mc.start_address | page_end_suffix) < page_end_address &&
|
||||
|
|
|
@ -136,15 +136,14 @@ static double CallstackFunc(expr_func* f, vec_expr_t* args, void* c)
|
|||
if (!std::isnan(num))
|
||||
{
|
||||
u32 address = static_cast<u32>(num);
|
||||
return std::any_of(stack.begin(), stack.end(),
|
||||
[address](const auto& s) { return s.vAddress == address; });
|
||||
return std::ranges::any_of(stack, [address](const auto& s) { return s.vAddress == address; });
|
||||
}
|
||||
|
||||
const char* cstr = expr_get_str(&vec_nth(args, 0));
|
||||
if (cstr != nullptr)
|
||||
{
|
||||
return std::any_of(stack.begin(), stack.end(),
|
||||
[cstr](const auto& s) { return s.Name.find(cstr) != std::string::npos; });
|
||||
return std::ranges::any_of(
|
||||
stack, [cstr](const auto& s) { return s.Name.find(cstr) != std::string::npos; });
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -644,6 +644,20 @@ void Jit64::fselx(UGeckoInstruction inst)
|
|||
|
||||
if (cpu_info.bAVX)
|
||||
{
|
||||
// Prefer BLENDVPD over VBLENDVPD if the latter doesn't save any
|
||||
// instructions.
|
||||
//
|
||||
// VBLENDVPD allows separate source and destination registers, which can
|
||||
// eliminate a MOVAPD/MOVSD. However, on Intel since Skylake, VBLENDVPD
|
||||
// takes additional uops to execute compared to BLENDVPD (according to
|
||||
// https://uops.info). On AMD and older Intel microarchitectures there is no
|
||||
// difference.
|
||||
if (d == c)
|
||||
{
|
||||
BLENDVPD(Rd, Rb);
|
||||
return;
|
||||
}
|
||||
|
||||
X64Reg src1 = XMM1;
|
||||
if (Rc.IsSimpleReg())
|
||||
{
|
||||
|
@ -654,7 +668,7 @@ void Jit64::fselx(UGeckoInstruction inst)
|
|||
MOVAPD(XMM1, Rc);
|
||||
}
|
||||
|
||||
if (d == c || packed)
|
||||
if (packed)
|
||||
{
|
||||
VBLENDVPD(Rd, src1, Rb, XMM0);
|
||||
return;
|
||||
|
|
|
@ -369,10 +369,8 @@ RCForkGuard RegCache::Fork()
|
|||
|
||||
void RegCache::Discard(BitSet32 pregs)
|
||||
{
|
||||
ASSERT_MSG(
|
||||
DYNA_REC,
|
||||
std::none_of(m_xregs.begin(), m_xregs.end(), [](const auto& x) { return x.IsLocked(); }),
|
||||
"Someone forgot to unlock a X64 reg");
|
||||
ASSERT_MSG(DYNA_REC, std::ranges::none_of(m_xregs, &X64CachedReg::IsLocked),
|
||||
"Someone forgot to unlock a X64 reg");
|
||||
|
||||
for (preg_t i : pregs)
|
||||
{
|
||||
|
@ -393,10 +391,8 @@ void RegCache::Discard(BitSet32 pregs)
|
|||
|
||||
void RegCache::Flush(BitSet32 pregs, IgnoreDiscardedRegisters ignore_discarded_registers)
|
||||
{
|
||||
ASSERT_MSG(
|
||||
DYNA_REC,
|
||||
std::none_of(m_xregs.begin(), m_xregs.end(), [](const auto& x) { return x.IsLocked(); }),
|
||||
"Someone forgot to unlock a X64 reg");
|
||||
ASSERT_MSG(DYNA_REC, std::ranges::none_of(m_xregs, &X64CachedReg::IsLocked),
|
||||
"Someone forgot to unlock a X64 reg");
|
||||
|
||||
for (preg_t i : pregs)
|
||||
{
|
||||
|
@ -459,9 +455,8 @@ void RegCache::Commit()
|
|||
|
||||
bool RegCache::IsAllUnlocked() const
|
||||
{
|
||||
return std::none_of(m_regs.begin(), m_regs.end(), [](const auto& r) { return r.IsLocked(); }) &&
|
||||
std::none_of(m_xregs.begin(), m_xregs.end(), [](const auto& x) { return x.IsLocked(); }) &&
|
||||
!IsAnyConstraintActive();
|
||||
return std::ranges::none_of(m_regs, &PPCCachedReg::IsLocked) &&
|
||||
std::ranges::none_of(m_xregs, &X64CachedReg::IsLocked) && !IsAnyConstraintActive();
|
||||
}
|
||||
|
||||
void RegCache::PreloadRegisters(BitSet32 to_preload)
|
||||
|
@ -754,6 +749,5 @@ void RegCache::Realize(preg_t preg)
|
|||
|
||||
bool RegCache::IsAnyConstraintActive() const
|
||||
{
|
||||
return std::any_of(m_constraints.begin(), m_constraints.end(),
|
||||
[](const auto& c) { return c.IsActive(); });
|
||||
return std::ranges::any_of(m_constraints, &RCConstraint::IsActive);
|
||||
}
|
||||
|
|
|
@ -355,8 +355,13 @@ protected:
|
|||
Arm64Gen::ARM64Reg exit_address_after_return_reg = Arm64Gen::ARM64Reg::INVALID_REG);
|
||||
void WriteBLRExit(Arm64Gen::ARM64Reg dest);
|
||||
|
||||
Arm64Gen::FixupBranch JumpIfCRFieldBit(int field, int bit, bool jump_if_set);
|
||||
void GetCRFieldBit(int field, int bit, Arm64Gen::ARM64Reg out);
|
||||
void SetCRFieldBit(int field, int bit, Arm64Gen::ARM64Reg in, bool negate = false);
|
||||
void ClearCRFieldBit(int field, int bit);
|
||||
void SetCRFieldBit(int field, int bit);
|
||||
void FixGTBeforeSettingCRFieldBit(Arm64Gen::ARM64Reg reg);
|
||||
Arm64Gen::FixupBranch JumpIfCRFieldBit(int field, int bit, bool jump_if_set);
|
||||
|
||||
void UpdateFPExceptionSummary(Arm64Gen::ARM64Reg fpscr);
|
||||
void UpdateRoundingMode();
|
||||
|
||||
|
|
|
@ -765,6 +765,14 @@ void JitArm64::rlwinmx_internal(UGeckoInstruction inst, u32 sh)
|
|||
return;
|
||||
}
|
||||
|
||||
if (mask == 0)
|
||||
{
|
||||
gpr.SetImmediate(a, 0);
|
||||
if (inst.Rc)
|
||||
ComputeRC0(0);
|
||||
return;
|
||||
}
|
||||
|
||||
gpr.BindToRegister(a, a == s);
|
||||
|
||||
if (sh == 0 && mask == 0xFFFFFFFF)
|
||||
|
@ -1128,47 +1136,85 @@ void JitArm64::addzex(UGeckoInstruction inst)
|
|||
|
||||
int a = inst.RA, d = inst.RD;
|
||||
|
||||
switch (js.carryFlag)
|
||||
if (gpr.IsImm(a) && (gpr.GetImm(a) == 0 || HasConstantCarry()))
|
||||
{
|
||||
case CarryFlag::InPPCState:
|
||||
{
|
||||
const bool allocate_reg = d == a;
|
||||
gpr.BindToRegister(d, allocate_reg);
|
||||
const u32 imm = gpr.GetImm(a);
|
||||
const bool is_all_ones = imm == 0xFFFFFFFF;
|
||||
|
||||
switch (js.carryFlag)
|
||||
{
|
||||
auto WA = allocate_reg ? gpr.GetScopedReg() : Arm64GPRCache::ScopedARM64Reg(gpr.R(d));
|
||||
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
||||
CARRY_IF_NEEDED(ADD, ADDS, gpr.R(d), gpr.R(a), WA);
|
||||
}
|
||||
|
||||
ComputeCarry();
|
||||
break;
|
||||
}
|
||||
case CarryFlag::InHostCarry:
|
||||
{
|
||||
gpr.BindToRegister(d, d == a);
|
||||
CARRY_IF_NEEDED(ADC, ADCS, gpr.R(d), gpr.R(a), ARM64Reg::WZR);
|
||||
ComputeCarry();
|
||||
break;
|
||||
}
|
||||
case CarryFlag::ConstantTrue:
|
||||
{
|
||||
gpr.BindToRegister(d, d == a);
|
||||
CARRY_IF_NEEDED(ADD, ADDS, gpr.R(d), gpr.R(a), 1);
|
||||
ComputeCarry();
|
||||
break;
|
||||
}
|
||||
case CarryFlag::ConstantFalse:
|
||||
{
|
||||
if (d != a)
|
||||
case CarryFlag::InPPCState:
|
||||
{
|
||||
gpr.BindToRegister(d, false);
|
||||
MOV(gpr.R(d), gpr.R(a));
|
||||
LDRB(IndexType::Unsigned, gpr.R(d), PPC_REG, PPCSTATE_OFF(xer_ca));
|
||||
ComputeCarry(false);
|
||||
break;
|
||||
}
|
||||
case CarryFlag::InHostCarry:
|
||||
{
|
||||
gpr.BindToRegister(d, false);
|
||||
CSET(gpr.R(d), CCFlags::CC_CS);
|
||||
ComputeCarry(false);
|
||||
break;
|
||||
}
|
||||
case CarryFlag::ConstantTrue:
|
||||
{
|
||||
gpr.SetImmediate(d, imm + 1);
|
||||
ComputeCarry(is_all_ones);
|
||||
break;
|
||||
}
|
||||
case CarryFlag::ConstantFalse:
|
||||
{
|
||||
gpr.SetImmediate(d, imm);
|
||||
ComputeCarry(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ComputeCarry(false);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (js.carryFlag)
|
||||
{
|
||||
case CarryFlag::InPPCState:
|
||||
{
|
||||
const bool allocate_reg = d == a;
|
||||
gpr.BindToRegister(d, allocate_reg);
|
||||
|
||||
{
|
||||
auto WA = allocate_reg ? gpr.GetScopedReg() : Arm64GPRCache::ScopedARM64Reg(gpr.R(d));
|
||||
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
||||
CARRY_IF_NEEDED(ADD, ADDS, gpr.R(d), gpr.R(a), WA);
|
||||
}
|
||||
|
||||
ComputeCarry();
|
||||
break;
|
||||
}
|
||||
case CarryFlag::InHostCarry:
|
||||
{
|
||||
gpr.BindToRegister(d, d == a);
|
||||
CARRY_IF_NEEDED(ADC, ADCS, gpr.R(d), gpr.R(a), ARM64Reg::WZR);
|
||||
ComputeCarry();
|
||||
break;
|
||||
}
|
||||
case CarryFlag::ConstantTrue:
|
||||
{
|
||||
gpr.BindToRegister(d, d == a);
|
||||
CARRY_IF_NEEDED(ADD, ADDS, gpr.R(d), gpr.R(a), 1);
|
||||
ComputeCarry();
|
||||
break;
|
||||
}
|
||||
case CarryFlag::ConstantFalse:
|
||||
{
|
||||
if (d != a)
|
||||
{
|
||||
gpr.BindToRegister(d, false);
|
||||
MOV(gpr.R(d), gpr.R(a));
|
||||
}
|
||||
|
||||
ComputeCarry(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inst.Rc)
|
||||
|
@ -1216,40 +1262,62 @@ void JitArm64::subfex(UGeckoInstruction inst)
|
|||
|
||||
if (gpr.IsImm(a) && (mex || gpr.IsImm(b)))
|
||||
{
|
||||
u32 i = gpr.GetImm(a), j = mex ? -1 : gpr.GetImm(b);
|
||||
|
||||
gpr.BindToRegister(d, false);
|
||||
const u32 i = gpr.GetImm(a);
|
||||
const u32 j = mex ? -1 : gpr.GetImm(b);
|
||||
const u32 imm = ~i + j;
|
||||
const bool is_zero = imm == 0;
|
||||
const bool is_all_ones = imm == 0xFFFFFFFF;
|
||||
|
||||
switch (js.carryFlag)
|
||||
{
|
||||
case CarryFlag::InPPCState:
|
||||
{
|
||||
auto WA = gpr.GetScopedReg();
|
||||
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
||||
ADDI2R(gpr.R(d), WA, ~i + j, gpr.R(d));
|
||||
gpr.BindToRegister(d, false);
|
||||
ARM64Reg RD = gpr.R(d);
|
||||
if (is_zero)
|
||||
{
|
||||
LDRB(IndexType::Unsigned, RD, PPC_REG, PPCSTATE_OFF(xer_ca));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto WA = gpr.GetScopedReg();
|
||||
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
||||
ADDI2R(RD, WA, imm, RD);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CarryFlag::InHostCarry:
|
||||
{
|
||||
auto WA = gpr.GetScopedReg();
|
||||
MOVI2R(WA, ~i + j);
|
||||
ADC(gpr.R(d), WA, ARM64Reg::WZR);
|
||||
gpr.BindToRegister(d, false);
|
||||
ARM64Reg RD = gpr.R(d);
|
||||
if (is_all_ones)
|
||||
{
|
||||
// RD = -1 + carry = carry ? 0 : -1
|
||||
// CSETM sets the destination to -1 if the condition is true, 0
|
||||
// otherwise. Hence, the condition must be carry clear.
|
||||
CSETM(RD, CC_CC);
|
||||
}
|
||||
else
|
||||
{
|
||||
MOVI2R(RD, imm);
|
||||
ADC(RD, RD, ARM64Reg::WZR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CarryFlag::ConstantTrue:
|
||||
{
|
||||
gpr.SetImmediate(d, ~i + j + 1);
|
||||
gpr.SetImmediate(d, imm + 1);
|
||||
break;
|
||||
}
|
||||
case CarryFlag::ConstantFalse:
|
||||
{
|
||||
gpr.SetImmediate(d, ~i + j);
|
||||
gpr.SetImmediate(d, imm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const bool must_have_carry = Interpreter::Helper_Carry(~i, j);
|
||||
const bool might_have_carry = (~i + j) == 0xFFFFFFFF;
|
||||
const bool might_have_carry = is_all_ones;
|
||||
|
||||
if (must_have_carry)
|
||||
{
|
||||
|
@ -1337,39 +1405,49 @@ void JitArm64::subfzex(UGeckoInstruction inst)
|
|||
|
||||
int a = inst.RA, d = inst.RD;
|
||||
|
||||
gpr.BindToRegister(d, d == a);
|
||||
if (gpr.IsImm(a) && HasConstantCarry())
|
||||
{
|
||||
const u32 imm = ~gpr.GetImm(a);
|
||||
const u32 carry = js.carryFlag == CarryFlag::ConstantTrue;
|
||||
gpr.SetImmediate(d, imm + carry);
|
||||
ComputeCarry(Interpreter::Helper_Carry(imm, carry));
|
||||
}
|
||||
else
|
||||
{
|
||||
gpr.BindToRegister(d, d == a);
|
||||
|
||||
switch (js.carryFlag)
|
||||
{
|
||||
case CarryFlag::InPPCState:
|
||||
{
|
||||
switch (js.carryFlag)
|
||||
{
|
||||
auto WA = gpr.GetScopedReg();
|
||||
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
||||
MVN(gpr.R(d), gpr.R(a));
|
||||
CARRY_IF_NEEDED(ADD, ADDS, gpr.R(d), gpr.R(d), WA);
|
||||
case CarryFlag::InPPCState:
|
||||
{
|
||||
{
|
||||
auto WA = gpr.GetScopedReg();
|
||||
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
||||
MVN(gpr.R(d), gpr.R(a));
|
||||
CARRY_IF_NEEDED(ADD, ADDS, gpr.R(d), gpr.R(d), WA);
|
||||
}
|
||||
ComputeCarry();
|
||||
break;
|
||||
}
|
||||
case CarryFlag::InHostCarry:
|
||||
{
|
||||
CARRY_IF_NEEDED(SBC, SBCS, gpr.R(d), ARM64Reg::WZR, gpr.R(a));
|
||||
ComputeCarry();
|
||||
break;
|
||||
}
|
||||
case CarryFlag::ConstantTrue:
|
||||
{
|
||||
CARRY_IF_NEEDED(NEG, NEGS, gpr.R(d), gpr.R(a));
|
||||
ComputeCarry();
|
||||
break;
|
||||
}
|
||||
case CarryFlag::ConstantFalse:
|
||||
{
|
||||
MVN(gpr.R(d), gpr.R(a));
|
||||
ComputeCarry(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ComputeCarry();
|
||||
break;
|
||||
}
|
||||
case CarryFlag::InHostCarry:
|
||||
{
|
||||
CARRY_IF_NEEDED(SBC, SBCS, gpr.R(d), ARM64Reg::WZR, gpr.R(a));
|
||||
ComputeCarry();
|
||||
break;
|
||||
}
|
||||
case CarryFlag::ConstantTrue:
|
||||
{
|
||||
CARRY_IF_NEEDED(NEG, NEGS, gpr.R(d), gpr.R(a));
|
||||
ComputeCarry();
|
||||
break;
|
||||
}
|
||||
case CarryFlag::ConstantFalse:
|
||||
{
|
||||
MVN(gpr.R(d), gpr.R(a));
|
||||
ComputeCarry(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (inst.Rc)
|
||||
|
@ -1394,23 +1472,34 @@ void JitArm64::subfic(UGeckoInstruction inst)
|
|||
else
|
||||
{
|
||||
const bool will_read = d == a;
|
||||
const bool is_zero = imm == 0;
|
||||
gpr.BindToRegister(d, will_read);
|
||||
|
||||
// d = imm - a
|
||||
ARM64Reg RD = gpr.R(d);
|
||||
|
||||
if (imm == -1)
|
||||
{
|
||||
Arm64GPRCache::ScopedARM64Reg WA(ARM64Reg::WZR);
|
||||
if (!is_zero)
|
||||
// d = -1 - a = ~a
|
||||
MVN(RD, gpr.R(a));
|
||||
// CA is always set in this case
|
||||
ComputeCarry(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool is_zero = imm == 0;
|
||||
|
||||
// d = imm - a
|
||||
{
|
||||
WA = will_read ? gpr.GetScopedReg() : Arm64GPRCache::ScopedARM64Reg(RD);
|
||||
MOVI2R(WA, imm);
|
||||
Arm64GPRCache::ScopedARM64Reg WA(ARM64Reg::WZR);
|
||||
if (!is_zero)
|
||||
{
|
||||
WA = will_read ? gpr.GetScopedReg() : Arm64GPRCache::ScopedARM64Reg(RD);
|
||||
MOVI2R(WA, imm);
|
||||
}
|
||||
|
||||
CARRY_IF_NEEDED(SUB, SUBS, RD, WA, gpr.R(a));
|
||||
}
|
||||
|
||||
CARRY_IF_NEEDED(SUB, SUBS, RD, WA, gpr.R(a));
|
||||
ComputeCarry();
|
||||
}
|
||||
|
||||
ComputeCarry();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1425,40 +1514,66 @@ void JitArm64::addex(UGeckoInstruction inst)
|
|||
|
||||
if (gpr.IsImm(a) && (mex || gpr.IsImm(b)))
|
||||
{
|
||||
u32 i = gpr.GetImm(a), j = mex ? -1 : gpr.GetImm(b);
|
||||
|
||||
gpr.BindToRegister(d, false);
|
||||
const u32 i = gpr.GetImm(a), j = mex ? -1 : gpr.GetImm(b);
|
||||
const u32 imm = i + j;
|
||||
const bool is_zero = imm == 0;
|
||||
const bool is_all_ones = imm == 0xFFFFFFFF;
|
||||
|
||||
switch (js.carryFlag)
|
||||
{
|
||||
case CarryFlag::InPPCState:
|
||||
{
|
||||
auto WA = gpr.GetScopedReg();
|
||||
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
||||
ADDI2R(gpr.R(d), WA, i + j, gpr.R(d));
|
||||
gpr.BindToRegister(d, false);
|
||||
ARM64Reg RD = gpr.R(d);
|
||||
if (is_zero)
|
||||
{
|
||||
LDRB(IndexType::Unsigned, RD, PPC_REG, PPCSTATE_OFF(xer_ca));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto WA = gpr.GetScopedReg();
|
||||
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
|
||||
ADDI2R(RD, WA, imm, RD);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CarryFlag::InHostCarry:
|
||||
{
|
||||
gpr.BindToRegister(d, false);
|
||||
ARM64Reg RD = gpr.R(d);
|
||||
MOVI2R(RD, i + j);
|
||||
ADC(RD, RD, ARM64Reg::WZR);
|
||||
if (is_zero)
|
||||
{
|
||||
// RD = 0 + carry = carry ? 1 : 0
|
||||
CSET(RD, CC_CS);
|
||||
}
|
||||
else if (is_all_ones)
|
||||
{
|
||||
// RD = -1 + carry = carry ? 0 : -1
|
||||
// Note that CSETM sets the destination to -1 if the condition is true,
|
||||
// and 0 otherwise. Hence, the condition must be carry clear.
|
||||
CSETM(RD, CC_CC);
|
||||
}
|
||||
else
|
||||
{
|
||||
MOVI2R(RD, imm);
|
||||
ADC(RD, RD, ARM64Reg::WZR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CarryFlag::ConstantTrue:
|
||||
{
|
||||
gpr.SetImmediate(d, i + j + 1);
|
||||
gpr.SetImmediate(d, imm + 1);
|
||||
break;
|
||||
}
|
||||
case CarryFlag::ConstantFalse:
|
||||
{
|
||||
gpr.SetImmediate(d, i + j);
|
||||
gpr.SetImmediate(d, imm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const bool must_have_carry = Interpreter::Helper_Carry(i, j);
|
||||
const bool might_have_carry = (i + j) == 0xFFFFFFFF;
|
||||
const bool might_have_carry = is_all_ones;
|
||||
|
||||
if (must_have_carry)
|
||||
{
|
||||
|
|
|
@ -817,9 +817,8 @@ void JitArm64::dcbx(UGeckoInstruction inst)
|
|||
STR(IndexType::Unsigned, loop_counter, PPC_REG, PPCSTATE_OFF_SPR(SPR_CTR));
|
||||
|
||||
// downcount -= (WA * reg_cycle_count)
|
||||
MUL(WB, WA, reg_cycle_count);
|
||||
MSUB(reg_downcount, WA, reg_cycle_count, reg_downcount);
|
||||
// ^ Note that this cannot overflow because it's limited by (downcount/cycle_count).
|
||||
SUB(reg_downcount, reg_downcount, WB);
|
||||
STR(IndexType::Unsigned, reg_downcount, PPC_REG, PPCSTATE_OFF(downcount));
|
||||
|
||||
SetJumpTarget(downcount_is_zero_or_negative);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Config/Config.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/Unreachable.h"
|
||||
|
||||
#include "Core/Config/SessionSettings.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
|
@ -337,8 +338,12 @@ void JitArm64::ps_sel(UGeckoInstruction inst)
|
|||
const auto V0Q = fpr.GetScopedReg();
|
||||
const ARM64Reg V0 = reg_encoder(V0Q);
|
||||
m_float_emit.FCMGE(size, V0, VA);
|
||||
m_float_emit.BSL(V0, VC, VB);
|
||||
m_float_emit.MOV(VD, V0);
|
||||
if (d == b)
|
||||
m_float_emit.BIT(VD, VC, V0);
|
||||
else if (d == c)
|
||||
m_float_emit.BIF(VD, VB, V0);
|
||||
else
|
||||
Common::Unreachable();
|
||||
}
|
||||
|
||||
ASSERT_MSG(DYNA_REC, singles == (fpr.IsSingle(a) && fpr.IsSingle(b) && fpr.IsSingle(c)),
|
||||
|
|
|
@ -20,6 +20,144 @@
|
|||
|
||||
using namespace Arm64Gen;
|
||||
|
||||
void JitArm64::GetCRFieldBit(int field, int bit, ARM64Reg out)
|
||||
{
|
||||
ARM64Reg CR = gpr.CR(field);
|
||||
ARM64Reg WCR = EncodeRegTo32(CR);
|
||||
|
||||
switch (bit)
|
||||
{
|
||||
case PowerPC::CR_SO_BIT: // check bit 59 set
|
||||
UBFX(out, CR, PowerPC::CR_EMU_SO_BIT, 1);
|
||||
break;
|
||||
|
||||
case PowerPC::CR_EQ_BIT: // check bits 31-0 == 0
|
||||
CMP(WCR, ARM64Reg::WZR);
|
||||
CSET(out, CC_EQ);
|
||||
break;
|
||||
|
||||
case PowerPC::CR_GT_BIT: // check val > 0
|
||||
CMP(CR, ARM64Reg::ZR);
|
||||
CSET(out, CC_GT);
|
||||
break;
|
||||
|
||||
case PowerPC::CR_LT_BIT: // check bit 62 set
|
||||
UBFX(out, CR, PowerPC::CR_EMU_LT_BIT, 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
ASSERT_MSG(DYNA_REC, false, "Invalid CR bit");
|
||||
}
|
||||
}
|
||||
|
||||
void JitArm64::SetCRFieldBit(int field, int bit, ARM64Reg in, bool negate)
|
||||
{
|
||||
gpr.BindCRToRegister(field, true);
|
||||
ARM64Reg CR = gpr.CR(field);
|
||||
|
||||
if (bit != PowerPC::CR_GT_BIT)
|
||||
FixGTBeforeSettingCRFieldBit(CR);
|
||||
|
||||
switch (bit)
|
||||
{
|
||||
case PowerPC::CR_SO_BIT: // set bit 59 to input
|
||||
BFI(CR, in, PowerPC::CR_EMU_SO_BIT, 1);
|
||||
if (negate)
|
||||
EOR(CR, CR, LogicalImm(1ULL << PowerPC::CR_EMU_SO_BIT, GPRSize::B64));
|
||||
break;
|
||||
|
||||
case PowerPC::CR_EQ_BIT: // clear low 32 bits, set bit 0 to !input
|
||||
AND(CR, CR, LogicalImm(0xFFFF'FFFF'0000'0000, GPRSize::B64));
|
||||
ORR(CR, CR, in);
|
||||
if (!negate)
|
||||
EOR(CR, CR, LogicalImm(1ULL << 0, GPRSize::B64));
|
||||
break;
|
||||
|
||||
case PowerPC::CR_GT_BIT: // set bit 63 to !input
|
||||
BFI(CR, in, 63, 1);
|
||||
if (!negate)
|
||||
EOR(CR, CR, LogicalImm(1ULL << 63, GPRSize::B64));
|
||||
break;
|
||||
|
||||
case PowerPC::CR_LT_BIT: // set bit 62 to input
|
||||
BFI(CR, in, PowerPC::CR_EMU_LT_BIT, 1);
|
||||
if (negate)
|
||||
EOR(CR, CR, LogicalImm(1ULL << PowerPC::CR_EMU_LT_BIT, GPRSize::B64));
|
||||
break;
|
||||
}
|
||||
|
||||
ORR(CR, CR, LogicalImm(1ULL << 32, GPRSize::B64));
|
||||
}
|
||||
|
||||
void JitArm64::ClearCRFieldBit(int field, int bit)
|
||||
{
|
||||
gpr.BindCRToRegister(field, true);
|
||||
ARM64Reg XA = gpr.CR(field);
|
||||
|
||||
switch (bit)
|
||||
{
|
||||
case PowerPC::CR_SO_BIT:
|
||||
AND(XA, XA, LogicalImm(~(u64(1) << PowerPC::CR_EMU_SO_BIT), GPRSize::B64));
|
||||
break;
|
||||
|
||||
case PowerPC::CR_EQ_BIT:
|
||||
FixGTBeforeSettingCRFieldBit(XA);
|
||||
ORR(XA, XA, LogicalImm(1, GPRSize::B64));
|
||||
break;
|
||||
|
||||
case PowerPC::CR_GT_BIT:
|
||||
ORR(XA, XA, LogicalImm(u64(1) << 63, GPRSize::B64));
|
||||
break;
|
||||
|
||||
case PowerPC::CR_LT_BIT:
|
||||
AND(XA, XA, LogicalImm(~(u64(1) << PowerPC::CR_EMU_LT_BIT), GPRSize::B64));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void JitArm64::SetCRFieldBit(int field, int bit)
|
||||
{
|
||||
gpr.BindCRToRegister(field, true);
|
||||
ARM64Reg XA = gpr.CR(field);
|
||||
|
||||
if (bit != PowerPC::CR_GT_BIT)
|
||||
FixGTBeforeSettingCRFieldBit(XA);
|
||||
|
||||
switch (bit)
|
||||
{
|
||||
case PowerPC::CR_SO_BIT:
|
||||
ORR(XA, XA, LogicalImm(u64(1) << PowerPC::CR_EMU_SO_BIT, GPRSize::B64));
|
||||
break;
|
||||
|
||||
case PowerPC::CR_EQ_BIT:
|
||||
AND(XA, XA, LogicalImm(0xFFFF'FFFF'0000'0000, GPRSize::B64));
|
||||
break;
|
||||
|
||||
case PowerPC::CR_GT_BIT:
|
||||
AND(XA, XA, LogicalImm(~(u64(1) << 63), GPRSize::B64));
|
||||
break;
|
||||
|
||||
case PowerPC::CR_LT_BIT:
|
||||
ORR(XA, XA, LogicalImm(u64(1) << PowerPC::CR_EMU_LT_BIT, GPRSize::B64));
|
||||
break;
|
||||
}
|
||||
|
||||
ORR(XA, XA, LogicalImm(u64(1) << 32, GPRSize::B64));
|
||||
}
|
||||
|
||||
void JitArm64::FixGTBeforeSettingCRFieldBit(ARM64Reg reg)
|
||||
{
|
||||
// GT is considered unset if the internal representation is <= 0, or in other words,
|
||||
// if the internal representation either has bit 63 set or has all bits set to zero.
|
||||
// If all bits are zero and we set some bit that's unrelated to GT, we need to set bit 63 so GT
|
||||
// doesn't accidentally become considered set. Gross but necessary; this can break actual games.
|
||||
auto WA = gpr.GetScopedReg();
|
||||
ARM64Reg XA = EncodeRegTo64(WA);
|
||||
ORR(XA, reg, LogicalImm(1ULL << 63, GPRSize::B64));
|
||||
CMP(reg, ARM64Reg::ZR);
|
||||
CSEL(reg, reg, XA, CC_NEQ);
|
||||
}
|
||||
|
||||
FixupBranch JitArm64::JumpIfCRFieldBit(int field, int bit, bool jump_if_set)
|
||||
{
|
||||
ARM64Reg XA = gpr.CR(field);
|
||||
|
@ -42,19 +180,6 @@ FixupBranch JitArm64::JumpIfCRFieldBit(int field, int bit, bool jump_if_set)
|
|||
}
|
||||
}
|
||||
|
||||
void JitArm64::FixGTBeforeSettingCRFieldBit(Arm64Gen::ARM64Reg reg)
|
||||
{
|
||||
// GT is considered unset if the internal representation is <= 0, or in other words,
|
||||
// if the internal representation either has bit 63 set or has all bits set to zero.
|
||||
// If all bits are zero and we set some bit that's unrelated to GT, we need to set bit 63 so GT
|
||||
// doesn't accidentally become considered set. Gross but necessary; this can break actual games.
|
||||
auto WA = gpr.GetScopedReg();
|
||||
ARM64Reg XA = EncodeRegTo64(WA);
|
||||
ORR(XA, reg, LogicalImm(1ULL << 63, GPRSize::B64));
|
||||
CMP(reg, ARM64Reg::ZR);
|
||||
CSEL(reg, reg, XA, CC_NEQ);
|
||||
}
|
||||
|
||||
void JitArm64::UpdateFPExceptionSummary(ARM64Reg fpscr)
|
||||
{
|
||||
auto WA = gpr.GetScopedReg();
|
||||
|
@ -468,72 +593,47 @@ void JitArm64::crXXX(UGeckoInstruction inst)
|
|||
INSTRUCTION_START
|
||||
JITDISABLE(bJITSystemRegistersOff);
|
||||
|
||||
// Special case: crclr
|
||||
if (inst.CRBA == inst.CRBB && inst.CRBA == inst.CRBD && inst.SUBOP10 == 193)
|
||||
if (inst.CRBA == inst.CRBB)
|
||||
{
|
||||
// Clear CR field bit
|
||||
int field = inst.CRBD >> 2;
|
||||
int bit = 3 - (inst.CRBD & 3);
|
||||
|
||||
gpr.BindCRToRegister(field, true);
|
||||
ARM64Reg XA = gpr.CR(field);
|
||||
switch (bit)
|
||||
switch (inst.SUBOP10)
|
||||
{
|
||||
case PowerPC::CR_SO_BIT:
|
||||
AND(XA, XA, LogicalImm(~(u64(1) << PowerPC::CR_EMU_SO_BIT), GPRSize::B64));
|
||||
break;
|
||||
|
||||
case PowerPC::CR_EQ_BIT:
|
||||
FixGTBeforeSettingCRFieldBit(XA);
|
||||
ORR(XA, XA, LogicalImm(1, GPRSize::B64));
|
||||
break;
|
||||
|
||||
case PowerPC::CR_GT_BIT:
|
||||
ORR(XA, XA, LogicalImm(u64(1) << 63, GPRSize::B64));
|
||||
break;
|
||||
|
||||
case PowerPC::CR_LT_BIT:
|
||||
AND(XA, XA, LogicalImm(~(u64(1) << PowerPC::CR_EMU_LT_BIT), GPRSize::B64));
|
||||
break;
|
||||
// crclr
|
||||
case 129: // crandc: A && ~B => 0
|
||||
case 193: // crxor: A ^ B => 0
|
||||
{
|
||||
ClearCRFieldBit(inst.CRBD >> 2, 3 - (inst.CRBD & 3));
|
||||
return;
|
||||
}
|
||||
// crset
|
||||
case 289: // creqv: ~(A ^ B) => 1
|
||||
case 417: // crorc: A || ~B => 1
|
||||
{
|
||||
SetCRFieldBit(inst.CRBD >> 2, 3 - (inst.CRBD & 3));
|
||||
return;
|
||||
}
|
||||
case 257: // crand: A && B => A
|
||||
case 449: // cror: A || B => A
|
||||
{
|
||||
auto WA = gpr.GetScopedReg();
|
||||
ARM64Reg XA = EncodeRegTo64(WA);
|
||||
GetCRFieldBit(inst.CRBA >> 2, 3 - (inst.CRBA & 3), XA);
|
||||
SetCRFieldBit(inst.CRBD >> 2, 3 - (inst.CRBD & 3), XA, false);
|
||||
return;
|
||||
}
|
||||
case 33: // crnor: ~(A || B) => ~A
|
||||
case 225: // crnand: ~(A && B) => ~A
|
||||
{
|
||||
auto WA = gpr.GetScopedReg();
|
||||
ARM64Reg XA = EncodeRegTo64(WA);
|
||||
GetCRFieldBit(inst.CRBA >> 2, 3 - (inst.CRBA & 3), XA);
|
||||
SetCRFieldBit(inst.CRBD >> 2, 3 - (inst.CRBD & 3), XA, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Special case: crset
|
||||
if (inst.CRBA == inst.CRBB && inst.CRBA == inst.CRBD && inst.SUBOP10 == 289)
|
||||
{
|
||||
// SetCRFieldBit
|
||||
int field = inst.CRBD >> 2;
|
||||
int bit = 3 - (inst.CRBD & 3);
|
||||
|
||||
gpr.BindCRToRegister(field, true);
|
||||
ARM64Reg XA = gpr.CR(field);
|
||||
|
||||
if (bit != PowerPC::CR_GT_BIT)
|
||||
FixGTBeforeSettingCRFieldBit(XA);
|
||||
|
||||
switch (bit)
|
||||
{
|
||||
case PowerPC::CR_SO_BIT:
|
||||
ORR(XA, XA, LogicalImm(u64(1) << PowerPC::CR_EMU_SO_BIT, GPRSize::B64));
|
||||
break;
|
||||
|
||||
case PowerPC::CR_EQ_BIT:
|
||||
AND(XA, XA, LogicalImm(0xFFFF'FFFF'0000'0000, GPRSize::B64));
|
||||
break;
|
||||
|
||||
case PowerPC::CR_GT_BIT:
|
||||
AND(XA, XA, LogicalImm(~(u64(1) << 63), GPRSize::B64));
|
||||
break;
|
||||
|
||||
case PowerPC::CR_LT_BIT:
|
||||
ORR(XA, XA, LogicalImm(u64(1) << PowerPC::CR_EMU_LT_BIT, GPRSize::B64));
|
||||
break;
|
||||
}
|
||||
|
||||
ORR(XA, XA, LogicalImm(u64(1) << 32, GPRSize::B64));
|
||||
return;
|
||||
}
|
||||
// crnor or crnand
|
||||
const bool negate_result = inst.SUBOP10 == 33 || inst.SUBOP10 == 225;
|
||||
|
||||
auto WA = gpr.GetScopedReg();
|
||||
ARM64Reg XA = EncodeRegTo64(WA);
|
||||
|
@ -541,106 +641,42 @@ void JitArm64::crXXX(UGeckoInstruction inst)
|
|||
auto WB = gpr.GetScopedReg();
|
||||
ARM64Reg XB = EncodeRegTo64(WB);
|
||||
|
||||
// creqv or crnand or crnor
|
||||
bool negateA = inst.SUBOP10 == 289 || inst.SUBOP10 == 225 || inst.SUBOP10 == 33;
|
||||
// crandc or crorc or crnand or crnor
|
||||
bool negateB =
|
||||
inst.SUBOP10 == 129 || inst.SUBOP10 == 417 || inst.SUBOP10 == 225 || inst.SUBOP10 == 33;
|
||||
|
||||
// GetCRFieldBit
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
int field = i ? inst.CRBB >> 2 : inst.CRBA >> 2;
|
||||
int bit = i ? 3 - (inst.CRBB & 3) : 3 - (inst.CRBA & 3);
|
||||
ARM64Reg out = i ? XB : XA;
|
||||
bool negate = i ? negateB : negateA;
|
||||
|
||||
ARM64Reg XC = gpr.CR(field);
|
||||
ARM64Reg WC = EncodeRegTo32(XC);
|
||||
switch (bit)
|
||||
{
|
||||
case PowerPC::CR_SO_BIT: // check bit 59 set
|
||||
UBFX(out, XC, PowerPC::CR_EMU_SO_BIT, 1);
|
||||
if (negate)
|
||||
EOR(out, out, LogicalImm(1, GPRSize::B64));
|
||||
break;
|
||||
|
||||
case PowerPC::CR_EQ_BIT: // check bits 31-0 == 0
|
||||
CMP(WC, ARM64Reg::WZR);
|
||||
CSET(out, negate ? CC_NEQ : CC_EQ);
|
||||
break;
|
||||
|
||||
case PowerPC::CR_GT_BIT: // check val > 0
|
||||
CMP(XC, ARM64Reg::ZR);
|
||||
CSET(out, negate ? CC_LE : CC_GT);
|
||||
break;
|
||||
|
||||
case PowerPC::CR_LT_BIT: // check bit 62 set
|
||||
UBFX(out, XC, PowerPC::CR_EMU_LT_BIT, 1);
|
||||
if (negate)
|
||||
EOR(out, out, LogicalImm(1, GPRSize::B64));
|
||||
break;
|
||||
|
||||
default:
|
||||
ASSERT_MSG(DYNA_REC, false, "Invalid CR bit");
|
||||
}
|
||||
}
|
||||
GetCRFieldBit(inst.CRBA >> 2, 3 - (inst.CRBA & 3), XA);
|
||||
GetCRFieldBit(inst.CRBB >> 2, 3 - (inst.CRBB & 3), XB);
|
||||
|
||||
// Compute combined bit
|
||||
switch (inst.SUBOP10)
|
||||
{
|
||||
case 33: // crnor: ~(A || B) == (~A && ~B)
|
||||
case 129: // crandc: A && ~B
|
||||
case 225: // crnand: ~(A && B)
|
||||
case 257: // crand: A && B
|
||||
AND(XA, XA, XB);
|
||||
break;
|
||||
|
||||
case 129: // crandc: A && ~B
|
||||
BIC(XA, XA, XB);
|
||||
break;
|
||||
|
||||
case 193: // crxor: A ^ B
|
||||
case 289: // creqv: ~(A ^ B) = ~A ^ B
|
||||
EOR(XA, XA, XB);
|
||||
break;
|
||||
|
||||
case 225: // crnand: ~(A && B) == (~A || ~B)
|
||||
case 417: // crorc: A || ~B
|
||||
case 289: // creqv: ~(A ^ B) = A ^ ~B
|
||||
EON(XA, XA, XB);
|
||||
break;
|
||||
|
||||
case 33: // crnor: ~(A || B)
|
||||
case 449: // cror: A || B
|
||||
ORR(XA, XA, XB);
|
||||
break;
|
||||
|
||||
case 417: // crorc: A || ~B
|
||||
ORN(XA, XA, XB);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Store result bit in CRBD
|
||||
int field = inst.CRBD >> 2;
|
||||
int bit = 3 - (inst.CRBD & 3);
|
||||
|
||||
gpr.BindCRToRegister(field, true);
|
||||
ARM64Reg CR = gpr.CR(field);
|
||||
|
||||
if (bit != PowerPC::CR_GT_BIT)
|
||||
FixGTBeforeSettingCRFieldBit(CR);
|
||||
|
||||
switch (bit)
|
||||
{
|
||||
case PowerPC::CR_SO_BIT: // set bit 59 to input
|
||||
BFI(CR, XA, PowerPC::CR_EMU_SO_BIT, 1);
|
||||
break;
|
||||
|
||||
case PowerPC::CR_EQ_BIT: // clear low 32 bits, set bit 0 to !input
|
||||
AND(CR, CR, LogicalImm(0xFFFF'FFFF'0000'0000, GPRSize::B64));
|
||||
EOR(XA, XA, LogicalImm(1, GPRSize::B64));
|
||||
ORR(CR, CR, XA);
|
||||
break;
|
||||
|
||||
case PowerPC::CR_GT_BIT: // set bit 63 to !input
|
||||
EOR(XA, XA, LogicalImm(1, GPRSize::B64));
|
||||
BFI(CR, XA, 63, 1);
|
||||
break;
|
||||
|
||||
case PowerPC::CR_LT_BIT: // set bit 62 to input
|
||||
BFI(CR, XA, PowerPC::CR_EMU_LT_BIT, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
ORR(CR, CR, LogicalImm(1ULL << 32, GPRSize::B64));
|
||||
SetCRFieldBit(inst.CRBD >> 2, 3 - (inst.CRBD & 3), XA, negate_result);
|
||||
}
|
||||
|
||||
void JitArm64::mfcr(UGeckoInstruction inst)
|
||||
|
|
|
@ -110,9 +110,9 @@ JitBase::~JitBase()
|
|||
CPUThreadConfigCallback::RemoveConfigChangedCallback(m_registered_config_callback_id);
|
||||
}
|
||||
|
||||
bool JitBase::DoesConfigNeedRefresh()
|
||||
bool JitBase::DoesConfigNeedRefresh() const
|
||||
{
|
||||
return std::any_of(JIT_SETTINGS.begin(), JIT_SETTINGS.end(), [this](const auto& pair) {
|
||||
return std::ranges::any_of(JIT_SETTINGS, [this](const auto& pair) {
|
||||
return this->*pair.first != Config::Get(*pair.second);
|
||||
});
|
||||
}
|
||||
|
@ -276,7 +276,7 @@ bool JitBase::CanMergeNextInstructions(int count) const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool JitBase::ShouldHandleFPExceptionForInstruction(const PPCAnalyst::CodeOp* op)
|
||||
bool JitBase::ShouldHandleFPExceptionForInstruction(const PPCAnalyst::CodeOp* op) const
|
||||
{
|
||||
if (jo.fp_exceptions)
|
||||
return (op->opinfo->flags & FL_FLOAT_EXCEPTION) != 0;
|
||||
|
|
|
@ -167,7 +167,7 @@ protected:
|
|||
|
||||
static const std::array<std::pair<bool JitBase::*, const Config::Info<bool>*>, 23> JIT_SETTINGS;
|
||||
|
||||
bool DoesConfigNeedRefresh();
|
||||
bool DoesConfigNeedRefresh() const;
|
||||
void RefreshConfig();
|
||||
|
||||
void InitFastmemArena();
|
||||
|
@ -178,8 +178,16 @@ protected:
|
|||
void CleanUpAfterStackFault();
|
||||
|
||||
bool CanMergeNextInstructions(int count) const;
|
||||
bool HasConstantCarry() const
|
||||
{
|
||||
#ifdef _M_ARM_64
|
||||
return js.carryFlag == CarryFlag::ConstantTrue || js.carryFlag == CarryFlag::ConstantFalse;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ShouldHandleFPExceptionForInstruction(const PPCAnalyst::CodeOp* op);
|
||||
bool ShouldHandleFPExceptionForInstruction(const PPCAnalyst::CodeOp* op) const;
|
||||
|
||||
public:
|
||||
explicit JitBase(Core::System& system);
|
||||
|
|
|
@ -191,7 +191,7 @@ static void AnalyzeFunction2(PPCSymbolDB* func_db, Common::Symbol* func)
|
|||
{
|
||||
u32 flags = func->flags;
|
||||
|
||||
bool nonleafcall = std::any_of(func->calls.begin(), func->calls.end(), [&](const auto& call) {
|
||||
bool nonleafcall = std::ranges::any_of(func->calls, [&](const auto& call) {
|
||||
const Common::Symbol* const called_func = func_db->GetSymbolFromAddr(call.function);
|
||||
return called_func && (called_func->flags & Common::FFLAG_LEAF) == 0;
|
||||
});
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <ranges>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
|||
#include "Common/IOFile.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/Unreachable.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/Debugger/DebugInterface.h"
|
||||
#include "Core/PowerPC/MMU.h"
|
||||
|
@ -251,7 +252,7 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
|
|||
continue;
|
||||
|
||||
// Support CodeWarrior and Dolphin map
|
||||
if (std::string_view{line}.ends_with(" section layout\n") || strcmp(temp, ".text") == 0 ||
|
||||
if (StripWhitespace(line).ends_with(" section layout") || strcmp(temp, ".text") == 0 ||
|
||||
strcmp(temp, ".init") == 0)
|
||||
{
|
||||
section_name = temp;
|
||||
|
@ -310,7 +311,7 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
|
|||
continue;
|
||||
column_count = 2;
|
||||
|
||||
// Three columns format:
|
||||
// Three columns format (with optional alignment):
|
||||
// Starting Virtual
|
||||
// address Size address
|
||||
// -----------------------
|
||||
|
@ -319,7 +320,7 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
|
|||
else
|
||||
iss.str("");
|
||||
|
||||
// Four columns format:
|
||||
// Four columns format (with optional alignment):
|
||||
// Starting Virtual File
|
||||
// address Size address offset
|
||||
// ---------------------------------
|
||||
|
@ -327,85 +328,77 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
|
|||
column_count = 4;
|
||||
}
|
||||
|
||||
u32 address, vaddress, size, offset, alignment;
|
||||
char name[512], container[512];
|
||||
if (column_count == 4)
|
||||
{
|
||||
// sometimes there is no alignment value, and sometimes it is because it is an entry of
|
||||
// something else
|
||||
if (length > 37 && line[37] == ' ')
|
||||
u32 address;
|
||||
u32 vaddress;
|
||||
u32 size = 0;
|
||||
u32 offset = 0;
|
||||
u32 alignment = 0;
|
||||
char name[512]{};
|
||||
static constexpr char ENTRY_OF_STRING[] = " (entry of ";
|
||||
static constexpr std::string_view ENTRY_OF_VIEW(ENTRY_OF_STRING);
|
||||
auto parse_entry_of = [](char* name) {
|
||||
if (char* s1 = strstr(name, ENTRY_OF_STRING); s1 != nullptr)
|
||||
{
|
||||
alignment = 0;
|
||||
sscanf(line, "%08x %08x %08x %08x %511s", &address, &size, &vaddress, &offset, name);
|
||||
char* s = strstr(line, "(entry of ");
|
||||
if (s)
|
||||
char container[512];
|
||||
char* ptr = s1 + ENTRY_OF_VIEW.size();
|
||||
sscanf(ptr, "%511s", container);
|
||||
// Skip sections, those start with a dot, e.g. (entry of .text)
|
||||
if (char* s2 = strchr(container, ')'); s2 != nullptr && *container != '.')
|
||||
{
|
||||
sscanf(s + 10, "%511s", container);
|
||||
char* s2 = (strchr(container, ')'));
|
||||
if (s2 && container[0] != '.')
|
||||
{
|
||||
s2[0] = '\0';
|
||||
strcat(container, "::");
|
||||
strcat(container, name);
|
||||
strcpy(name, container);
|
||||
}
|
||||
ptr += strlen(container);
|
||||
// Preserve data after the entry part, usually it contains object names
|
||||
strcpy(s1, ptr);
|
||||
*s2 = '\0';
|
||||
strcat(container, "::");
|
||||
strcat(container, name);
|
||||
strcpy(name, container);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sscanf(line, "%08x %08x %08x %08x %i %511s", &address, &size, &vaddress, &offset,
|
||||
&alignment, name);
|
||||
}
|
||||
}
|
||||
else if (column_count == 3)
|
||||
};
|
||||
auto was_alignment = [](const char* name) {
|
||||
return *name == ' ' || (*name >= '0' && *name <= '9');
|
||||
};
|
||||
auto parse_alignment = [](char* name, u32* alignment) {
|
||||
const std::string buffer(StripWhitespace(name));
|
||||
return sscanf(buffer.c_str(), "%i %511[^\r\n]", alignment, name);
|
||||
};
|
||||
switch (column_count)
|
||||
{
|
||||
case 4:
|
||||
// sometimes there is no alignment value, and sometimes it is because it is an entry of
|
||||
// something else
|
||||
sscanf(line, "%08x %08x %08x %08x %511[^\r\n]", &address, &size, &vaddress, &offset, name);
|
||||
if (was_alignment(name))
|
||||
parse_alignment(name, &alignment);
|
||||
// The `else` statement was omitted to handle symbol already saved in Dolphin symbol map
|
||||
// since it doesn't omit the alignment on save for such case.
|
||||
parse_entry_of(name);
|
||||
break;
|
||||
case 3:
|
||||
// some entries in the table have a function name followed by " (entry of " followed by a
|
||||
// container name, followed by ")"
|
||||
// instead of a space followed by a number followed by a space followed by a name
|
||||
if (length > 27 && line[27] != ' ' && strstr(line, "(entry of "))
|
||||
{
|
||||
alignment = 0;
|
||||
sscanf(line, "%08x %08x %08x %511s", &address, &size, &vaddress, name);
|
||||
char* s = strstr(line, "(entry of ");
|
||||
if (s)
|
||||
{
|
||||
sscanf(s + 10, "%511s", container);
|
||||
char* s2 = (strchr(container, ')'));
|
||||
if (s2 && container[0] != '.')
|
||||
{
|
||||
s2[0] = '\0';
|
||||
strcat(container, "::");
|
||||
strcat(container, name);
|
||||
strcpy(name, container);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sscanf(line, "%08x %08x %08x %i %511s", &address, &size, &vaddress, &alignment, name);
|
||||
}
|
||||
}
|
||||
else if (column_count == 2)
|
||||
{
|
||||
sscanf(line, "%08x %511s", &address, name);
|
||||
sscanf(line, "%08x %08x %08x %511[^\r\n]", &address, &size, &vaddress, name);
|
||||
if (was_alignment(name))
|
||||
parse_alignment(name, &alignment);
|
||||
// The `else` statement was omitted to handle symbol already saved in Dolphin symbol map
|
||||
// since it doesn't omit the alignment on save for such case.
|
||||
parse_entry_of(name);
|
||||
break;
|
||||
case 2:
|
||||
sscanf(line, "%08x %511[^\r\n]", &address, name);
|
||||
vaddress = address;
|
||||
size = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
default:
|
||||
// Should never happen
|
||||
Common::Unreachable();
|
||||
break;
|
||||
}
|
||||
const char* namepos = strstr(line, name);
|
||||
if (namepos != nullptr) // would be odd if not :P
|
||||
strcpy(name, namepos);
|
||||
name[strlen(name) - 1] = 0;
|
||||
if (name[strlen(name) - 1] == '\r')
|
||||
name[strlen(name) - 1] = 0;
|
||||
|
||||
// Split the current name string into separate parts, and get the object name
|
||||
// if it exists.
|
||||
const std::vector<std::string> parts = SplitString(name, '\t');
|
||||
const std::string name_string(StripWhitespace(parts[0]));
|
||||
const std::string name_string(StripWhitespace(parts.size() > 0 ? parts[0] : name));
|
||||
const std::string object_filename_string =
|
||||
parts.size() > 1 ? std::string(StripWhitespace(parts[1])) : "";
|
||||
|
||||
|
@ -466,42 +459,38 @@ bool PPCSymbolDB::SaveSymbolMap(const std::string& filename) const
|
|||
if (!f)
|
||||
return false;
|
||||
|
||||
std::vector<const Common::Symbol*> function_symbols;
|
||||
std::vector<const Common::Symbol*> data_symbols;
|
||||
|
||||
for (const auto& function : m_functions)
|
||||
{
|
||||
const Common::Symbol& symbol = function.second;
|
||||
if (symbol.type == Common::Symbol::Type::Function)
|
||||
function_symbols.push_back(&symbol);
|
||||
else
|
||||
data_symbols.push_back(&symbol);
|
||||
}
|
||||
|
||||
// Write .text section
|
||||
auto function_symbols =
|
||||
m_functions |
|
||||
std::views::filter([](auto f) { return f.second.type == Common::Symbol::Type::Function; }) |
|
||||
std::views::transform([](auto f) { return f.second; });
|
||||
f.WriteString(".text section layout\n");
|
||||
for (const auto& symbol : function_symbols)
|
||||
{
|
||||
// Write symbol address, size, virtual address, alignment, name
|
||||
std::string line = fmt::format("{0:08x} {1:06x} {2:08x} {3} {4}", symbol->address, symbol->size,
|
||||
symbol->address, 0, symbol->name);
|
||||
std::string line = fmt::format("{:08x} {:06x} {:08x} {} {}", symbol.address, symbol.size,
|
||||
symbol.address, 0, symbol.name);
|
||||
// Also write the object name if it exists
|
||||
if (!symbol->object_name.empty())
|
||||
line += fmt::format(" \t{0}", symbol->object_name);
|
||||
if (!symbol.object_name.empty())
|
||||
line += fmt::format(" \t{0}", symbol.object_name);
|
||||
line += "\n";
|
||||
f.WriteString(line);
|
||||
}
|
||||
|
||||
// Write .data section
|
||||
auto data_symbols =
|
||||
m_functions |
|
||||
std::views::filter([](auto f) { return f.second.type == Common::Symbol::Type::Data; }) |
|
||||
std::views::transform([](auto f) { return f.second; });
|
||||
f.WriteString("\n.data section layout\n");
|
||||
for (const auto& symbol : data_symbols)
|
||||
{
|
||||
// Write symbol address, size, virtual address, alignment, name
|
||||
std::string line = fmt::format("{0:08x} {1:06x} {2:08x} {3} {4}", symbol->address, symbol->size,
|
||||
symbol->address, 0, symbol->name);
|
||||
std::string line = fmt::format("{:08x} {:06x} {:08x} {} {}", symbol.address, symbol.size,
|
||||
symbol.address, 0, symbol.name);
|
||||
// Also write the object name if it exists
|
||||
if (!symbol->object_name.empty())
|
||||
line += fmt::format(" \t{0}", symbol->object_name);
|
||||
if (!symbol.object_name.empty())
|
||||
line += fmt::format(" \t{0}", symbol.object_name);
|
||||
line += "\n";
|
||||
f.WriteString(line);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Contains.h"
|
||||
#include "Common/Event.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/IOFile.h"
|
||||
|
@ -138,7 +139,7 @@ void EnableCompression(bool compression)
|
|||
s_use_compression = compression;
|
||||
}
|
||||
|
||||
static void DoState(Core::System& system, PointerWrap& p)
|
||||
static void DoState(Core::System& system, PointerWrap& p, bool delta)
|
||||
{
|
||||
bool is_wii = system.IsWii() || system.IsMIOS();
|
||||
const bool is_wii_currently = is_wii;
|
||||
|
@ -187,7 +188,7 @@ static void DoState(Core::System& system, PointerWrap& p)
|
|||
p.DoMarker("CoreTiming");
|
||||
|
||||
// HW needs to be restored before PowerPC because the data cache might need to be flushed.
|
||||
HW::DoState(system, p);
|
||||
HW::DoState(system, p, delta);
|
||||
p.DoMarker("HW");
|
||||
|
||||
system.GetPowerPC().DoState(p);
|
||||
|
@ -223,7 +224,7 @@ void LoadFromBuffer(Core::System& system, std::vector<u8>& buffer)
|
|||
[&] {
|
||||
u8* ptr = buffer.data();
|
||||
PointerWrap p(&ptr, buffer.size(), PointerWrap::Mode::Read);
|
||||
DoState(system, p);
|
||||
DoState(system, p, false);
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
@ -236,13 +237,13 @@ void SaveToBuffer(Core::System& system, std::vector<u8>& buffer)
|
|||
u8* ptr = nullptr;
|
||||
PointerWrap p_measure(&ptr, 0, PointerWrap::Mode::Measure);
|
||||
|
||||
DoState(system, p_measure);
|
||||
DoState(system, p_measure, false);
|
||||
const size_t buffer_size = reinterpret_cast<size_t>(ptr);
|
||||
buffer.resize(buffer_size);
|
||||
|
||||
ptr = buffer.data();
|
||||
PointerWrap p(&ptr, buffer_size, PointerWrap::Mode::Write);
|
||||
DoState(system, p);
|
||||
DoState(system, p, false);
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
@ -261,9 +262,7 @@ static int GetEmptySlot(const std::vector<SlotWithTimestamp>& used_slots)
|
|||
{
|
||||
for (int i = 1; i <= (int)NUM_STATES; i++)
|
||||
{
|
||||
const auto it = std::find_if(used_slots.begin(), used_slots.end(),
|
||||
[i](const SlotWithTimestamp& slot) { return slot.slot == i; });
|
||||
if (it == used_slots.end())
|
||||
if (!Common::Contains(used_slots, i, &SlotWithTimestamp::slot))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
|
@ -487,7 +486,7 @@ void SaveAs(Core::System& system, const std::string& filename, bool wait)
|
|||
// Measure the size of the buffer.
|
||||
u8* ptr = nullptr;
|
||||
PointerWrap p_measure(&ptr, 0, PointerWrap::Mode::Measure);
|
||||
DoState(system, p_measure);
|
||||
DoState(system, p_measure, false);
|
||||
const size_t buffer_size = reinterpret_cast<size_t>(ptr);
|
||||
|
||||
// Then actually do the write.
|
||||
|
@ -495,7 +494,7 @@ void SaveAs(Core::System& system, const std::string& filename, bool wait)
|
|||
current_buffer.resize(buffer_size);
|
||||
ptr = current_buffer.data();
|
||||
PointerWrap p(&ptr, buffer_size, PointerWrap::Mode::Write);
|
||||
DoState(system, p);
|
||||
DoState(system, p, true);
|
||||
|
||||
if (p.IsWriteMode())
|
||||
{
|
||||
|
@ -901,7 +900,7 @@ void LoadAs(Core::System& system, const std::string& filename)
|
|||
{
|
||||
u8* ptr = buffer.data();
|
||||
PointerWrap p(&ptr, buffer.size(), PointerWrap::Mode::Read);
|
||||
DoState(system, p);
|
||||
DoState(system, p, true);
|
||||
loaded = true;
|
||||
loadedSuccessfully = p.IsReadMode();
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <array>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "Common/Contains.h"
|
||||
#include "Common/IniFile.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
|
@ -86,10 +87,7 @@ static void LoadPatchSection(const Common::IniFile& ini)
|
|||
static bool IsWC24Channel()
|
||||
{
|
||||
const auto& sconfig = SConfig::GetInstance();
|
||||
const auto found =
|
||||
std::find(s_wc24_channels.begin(), s_wc24_channels.end(), sconfig.GetTitleID());
|
||||
|
||||
return found != s_wc24_channels.end();
|
||||
return Common::Contains(s_wc24_channels, sconfig.GetTitleID());
|
||||
}
|
||||
|
||||
static void LoadPatches()
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "Common/Align.h"
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Contains.h"
|
||||
#include "Common/EnumUtils.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/HttpRequest.h"
|
||||
|
@ -223,15 +224,14 @@ bool IsTitleInstalled(u64 title_id)
|
|||
// Since this isn't IOS and we only need a simple way to figure out if a title is installed,
|
||||
// we make the (reasonable) assumption that having more than just the TMD in the content
|
||||
// directory means that the title is installed.
|
||||
return std::any_of(entries->begin(), entries->end(),
|
||||
[](const std::string& file) { return file != "title.tmd"; });
|
||||
return std::ranges::any_of(*entries, [](const std::string& file) { return file != "title.tmd"; });
|
||||
}
|
||||
|
||||
bool IsTMDImported(IOS::HLE::FS::FileSystem& fs, u64 title_id)
|
||||
{
|
||||
const auto entries = fs.ReadDirectory(0, 0, Common::GetTitleContentPath(title_id));
|
||||
return entries && std::any_of(entries->begin(), entries->end(),
|
||||
[](const std::string& file) { return file == "title.tmd"; });
|
||||
return entries &&
|
||||
std::ranges::any_of(*entries, [](const std::string& file) { return file == "title.tmd"; });
|
||||
}
|
||||
|
||||
IOS::ES::TMDReader FindBackupTMD(IOS::HLE::FS::FileSystem& fs, u64 title_id)
|
||||
|
@ -467,12 +467,7 @@ OnlineSystemUpdater::Response OnlineSystemUpdater::GetSystemTitles()
|
|||
// but the backing data CDN is still active and accessible from other URLs. We take advantage
|
||||
// of this by hosting our own NetUpdateSOAP endpoint which serves the correct list of titles to
|
||||
// install along with URLs for the Wii U CDN.
|
||||
#ifdef ANDROID
|
||||
// HTTPS is unsupported on Android (https://bugs.dolphin-emu.org/issues/11772).
|
||||
base_url = "http://fakenus.dolphin-emu.org";
|
||||
#else
|
||||
base_url = "https://fakenus.dolphin-emu.org";
|
||||
#endif
|
||||
}
|
||||
|
||||
const std::string url = fmt::format("{}/nus/services/NetUpdateSOAP", base_url);
|
||||
|
@ -591,10 +586,8 @@ UpdateResult OnlineSystemUpdater::InstallTitleFromNUS(const std::string& prefix_
|
|||
const UpdateResult import_result = [&]() {
|
||||
for (const IOS::ES::Content& content : tmd.first.GetContents())
|
||||
{
|
||||
const bool is_already_installed = std::find_if(stored_contents.begin(), stored_contents.end(),
|
||||
[&content](const auto& stored_content) {
|
||||
return stored_content.id == content.id;
|
||||
}) != stored_contents.end();
|
||||
const bool is_already_installed =
|
||||
Common::Contains(stored_contents, content.id, &IOS::ES::Content::id);
|
||||
|
||||
// Do skip what is already installed on the NAND.
|
||||
if (is_already_installed)
|
||||
|
@ -947,8 +940,8 @@ static NANDCheckResult CheckNAND(IOS::HLE::Kernel& ios, bool repair)
|
|||
}
|
||||
|
||||
const auto installed_contents = es.GetStoredContentsFromTMD(tmd);
|
||||
const bool is_installed = std::any_of(installed_contents.begin(), installed_contents.end(),
|
||||
[](const auto& content) { return !content.IsShared(); });
|
||||
const bool is_installed = std::ranges::any_of(
|
||||
installed_contents, [](const auto& content) { return !content.IsShared(); });
|
||||
|
||||
if (is_installed && installed_contents != tmd.GetContents() &&
|
||||
(tmd.GetTitleFlags() & IOS::ES::TitleFlags::TITLE_TYPE_DATA) == 0)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "DiscIO/Blob.h"
|
||||
#include "DiscIO/Filesystem.h"
|
||||
#include "DiscIO/Volume.h"
|
||||
|
@ -39,8 +40,7 @@ std::string NameForPartitionType(u32 partition_type, bool include_prefix)
|
|||
static_cast<char>((partition_type >> 16) & 0xFF),
|
||||
static_cast<char>((partition_type >> 8) & 0xFF),
|
||||
static_cast<char>(partition_type & 0xFF)};
|
||||
if (std::all_of(type_as_game_id.cbegin(), type_as_game_id.cend(),
|
||||
[](char c) { return std::isalnum(c, std::locale::classic()); }))
|
||||
if (std::ranges::all_of(type_as_game_id, Common::IsAlnum))
|
||||
{
|
||||
return include_prefix ? "P-" + type_as_game_id : type_as_game_id;
|
||||
}
|
||||
|
|
|
@ -240,11 +240,9 @@ bool NANDImporter::ExtractCertificates()
|
|||
|
||||
for (const PEMCertificate& certificate : certificates)
|
||||
{
|
||||
const auto search_result =
|
||||
std::search(content_bytes.begin(), content_bytes.end(), certificate.search_bytes.begin(),
|
||||
certificate.search_bytes.end());
|
||||
const auto search_result = std::ranges::search(content_bytes, certificate.search_bytes);
|
||||
|
||||
if (search_result == content_bytes.end())
|
||||
if (search_result.empty())
|
||||
{
|
||||
ERROR_LOG_FMT(DISCIO, "ExtractCertificates: Could not find offset for certficate '{}'",
|
||||
certificate.filename);
|
||||
|
@ -252,7 +250,8 @@ bool NANDImporter::ExtractCertificates()
|
|||
}
|
||||
|
||||
const std::string pem_file_path = m_nand_root + std::string(certificate.filename);
|
||||
const ptrdiff_t certificate_offset = std::distance(content_bytes.begin(), search_result);
|
||||
const ptrdiff_t certificate_offset =
|
||||
std::distance(content_bytes.begin(), search_result.begin());
|
||||
constexpr int min_offset = 2;
|
||||
if (certificate_offset < min_offset)
|
||||
{
|
||||
|
|
|
@ -104,7 +104,7 @@ FileDataLoaderHostFS::MakeAbsoluteFromRelative(std::string_view external_relativ
|
|||
result.pop_back();
|
||||
result.pop_back();
|
||||
}
|
||||
else if (std::all_of(element.begin(), element.end(), [](char c) { return c == '.'; }))
|
||||
else if (std::ranges::all_of(element, [](char c) { return c == '.'; }))
|
||||
{
|
||||
// This is a triple, quadruple, etc. dot.
|
||||
// Some file systems treat this as several 'up' path traversals, but Riivolution does not.
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "Common/CPUDetect.h"
|
||||
#include "Common/CommonPaths.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Contains.h"
|
||||
#include "Common/Crypto/SHA1.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/Hash.h"
|
||||
|
@ -143,11 +144,8 @@ RedumpVerifier::DownloadStatus RedumpVerifier::DownloadDatfile(const std::string
|
|||
if (File::Exists(output_path))
|
||||
return DownloadStatus::FailButOldCacheAvailable;
|
||||
|
||||
const std::string system_not_available_message = "System \"" + system + "\" doesn't exist.";
|
||||
const bool system_not_available_match =
|
||||
result->end() != std::search(result->begin(), result->end(),
|
||||
system_not_available_message.begin(),
|
||||
system_not_available_message.end());
|
||||
Common::ContainsSubrange(*result, "System \"" + system + "\" doesn't exist.");
|
||||
return system_not_available_match ? DownloadStatus::SystemNotAvailable : DownloadStatus::Fail;
|
||||
}
|
||||
|
||||
|
@ -453,21 +451,18 @@ std::vector<Partition> VolumeVerifier::CheckPartitions()
|
|||
types.emplace_back(*type);
|
||||
}
|
||||
|
||||
if (std::find(types.cbegin(), types.cend(), PARTITION_UPDATE) == types.cend())
|
||||
if (!Common::Contains(types, PARTITION_UPDATE))
|
||||
AddProblem(Severity::Low, Common::GetStringT("The update partition is missing."));
|
||||
|
||||
const bool has_data_partition =
|
||||
std::find(types.cbegin(), types.cend(), PARTITION_DATA) != types.cend();
|
||||
const bool has_data_partition = Common::Contains(types, PARTITION_DATA);
|
||||
if (!m_is_datel && !has_data_partition)
|
||||
AddProblem(Severity::High, Common::GetStringT("The data partition is missing."));
|
||||
|
||||
const bool has_channel_partition =
|
||||
std::find(types.cbegin(), types.cend(), PARTITION_CHANNEL) != types.cend();
|
||||
const bool has_channel_partition = Common::Contains(types, PARTITION_CHANNEL);
|
||||
if (ShouldHaveChannelPartition() && !has_channel_partition)
|
||||
AddProblem(Severity::Medium, Common::GetStringT("The channel partition is missing."));
|
||||
|
||||
const bool has_install_partition =
|
||||
std::find(types.cbegin(), types.cend(), PARTITION_INSTALL) != types.cend();
|
||||
const bool has_install_partition = Common::Contains(types, PARTITION_INSTALL);
|
||||
if (ShouldHaveInstallPartition() && !has_install_partition)
|
||||
AddProblem(Severity::High, Common::GetStringT("The install partition is missing."));
|
||||
|
||||
|
@ -729,16 +724,15 @@ bool VolumeVerifier::ShouldHaveInstallPartition() const
|
|||
static constexpr std::array<std::string_view, 4> dragon_quest_x = {"S4MJGD", "S4SJGD", "S6TJGD",
|
||||
"SDQJGD"};
|
||||
const std::string& game_id = m_volume.GetGameID();
|
||||
return std::any_of(dragon_quest_x.cbegin(), dragon_quest_x.cend(),
|
||||
[&game_id](std::string_view x) { return x == game_id; });
|
||||
return std::ranges::any_of(dragon_quest_x,
|
||||
[&game_id](std::string_view x) { return x == game_id; });
|
||||
}
|
||||
|
||||
bool VolumeVerifier::ShouldHaveMasterpiecePartitions() const
|
||||
{
|
||||
static constexpr std::array<std::string_view, 4> ssbb = {"RSBE01", "RSBJ01", "RSBK01", "RSBP01"};
|
||||
const std::string& game_id = m_volume.GetGameID();
|
||||
return std::any_of(ssbb.cbegin(), ssbb.cend(),
|
||||
[&game_id](std::string_view x) { return x == game_id; });
|
||||
return std::ranges::any_of(ssbb, [&game_id](std::string_view x) { return x == game_id; });
|
||||
}
|
||||
|
||||
bool VolumeVerifier::ShouldBeDualLayer() const
|
||||
|
@ -1039,7 +1033,7 @@ void VolumeVerifier::CheckSuperPaperMario()
|
|||
if (!m_volume.Read(offset, length, data.data(), partition))
|
||||
return;
|
||||
|
||||
if (std::any_of(data.cbegin(), data.cend(), [](u8 x) { return x != 0; }))
|
||||
if (std::ranges::any_of(data, [](u8 x) { return x != 0; }))
|
||||
{
|
||||
AddProblem(Severity::High,
|
||||
Common::GetStringT("Some padding data that should be zero is not zero. "
|
||||
|
|
|
@ -176,7 +176,7 @@ IOS::ES::TicketReader VolumeWAD::GetTicketWithFixedCommonKey() const
|
|||
return m_ticket;
|
||||
|
||||
const std::vector<u8> sig = m_ticket.GetSignatureData();
|
||||
if (!std::all_of(sig.cbegin(), sig.cend(), [](u8 a) { return a == 0; }))
|
||||
if (!std::ranges::all_of(sig, [](u8 a) { return a == 0; }))
|
||||
{
|
||||
// This does not look like a typical "invalid common key index" ticket, so let's assume
|
||||
// the index is correct. This saves some time when reading properly signed titles.
|
||||
|
|
|
@ -1126,7 +1126,7 @@ bool WIARVZFileReader<RVZ>::TryReuse(std::map<ReuseID, GroupEntry>* reusable_gro
|
|||
|
||||
static bool AllAre(const std::vector<u8>& data, u8 x)
|
||||
{
|
||||
return std::all_of(data.begin(), data.end(), [x](u8 y) { return x == y; });
|
||||
return std::ranges::all_of(data, [x](u8 y) { return x == y; });
|
||||
}
|
||||
|
||||
static bool AllAre(const u8* begin, const u8* end, u8 x)
|
||||
|
@ -1379,8 +1379,8 @@ WIARVZFileReader<RVZ>::ProcessAndCompress(CompressThreadState* state, CompressPa
|
|||
}
|
||||
}
|
||||
|
||||
if (!std::all_of(output_entries.begin(), output_entries.end(),
|
||||
[](const OutputParametersEntry& entry) { return entry.reused_group; }))
|
||||
if (!std::ranges::all_of(output_entries,
|
||||
[](const auto& entry) { return entry.reused_group.has_value(); }))
|
||||
{
|
||||
const u64 number_of_exception_lists =
|
||||
chunks_per_wii_group == 1 ? exception_lists_per_chunk : chunks;
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
<ClInclude Include="Common\Config\ConfigInfo.h" />
|
||||
<ClInclude Include="Common\Config\Enums.h" />
|
||||
<ClInclude Include="Common\Config\Layer.h" />
|
||||
<ClInclude Include="Common\Contains.h" />
|
||||
<ClInclude Include="Common\CPUDetect.h" />
|
||||
<ClInclude Include="Common\Crypto\AES.h" />
|
||||
<ClInclude Include="Common\Crypto\bn.h" />
|
||||
|
|
|
@ -52,6 +52,7 @@ add_executable(dolphin-emu
|
|||
Config/ConfigControls/ConfigBool.h
|
||||
Config/ConfigControls/ConfigChoice.cpp
|
||||
Config/ConfigControls/ConfigChoice.h
|
||||
Config/ConfigControls/ConfigControl.h
|
||||
Config/ConfigControls/ConfigInteger.cpp
|
||||
Config/ConfigControls/ConfigInteger.h
|
||||
Config/ConfigControls/ConfigRadio.cpp
|
||||
|
@ -572,12 +573,11 @@ endif()
|
|||
|
||||
if(APPLE)
|
||||
include(BundleUtilities)
|
||||
set(BUNDLE_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/DolphinQt.app)
|
||||
|
||||
# Ask for an application bundle.
|
||||
set_target_properties(dolphin-emu PROPERTIES
|
||||
MACOSX_BUNDLE true
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/Info.plist"
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ""
|
||||
OUTPUT_NAME DolphinQt
|
||||
)
|
||||
|
@ -614,6 +614,9 @@ if(APPLE)
|
|||
source_group("Resources" FILES "${CMAKE_SOURCE_DIR}/Data/${res}")
|
||||
endforeach()
|
||||
|
||||
include(DolphinInjectVersionInfo)
|
||||
dolphin_inject_version_info(dolphin-emu)
|
||||
|
||||
# Copy MoltenVK into the bundle
|
||||
if(ENABLE_VULKAN)
|
||||
if(USE_BUNDLED_MOLTENVK)
|
||||
|
@ -648,7 +651,7 @@ if(APPLE)
|
|||
COMMAND "${CMAKE_SOURCE_DIR}/Tools/mac-codesign.sh"
|
||||
"-e" "${CMAKE_CURRENT_SOURCE_DIR}/DolphinEmu$<$<CONFIG:Debug>:Debug>.entitlements"
|
||||
"${MACOS_CODE_SIGNING_IDENTITY}"
|
||||
"${BUNDLE_PATH}"
|
||||
"$<TARGET_BUNDLE_DIR:dolphin-emu>"
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
|
|
|
@ -3,31 +3,28 @@
|
|||
|
||||
#include "DolphinQt/Config/ConfigControls/ConfigBool.h"
|
||||
|
||||
#include <QEvent>
|
||||
#include <QFont>
|
||||
#include <QSignalBlocker>
|
||||
|
||||
#include "Common/Config/Config.h"
|
||||
|
||||
#include "DolphinQt/Settings.h"
|
||||
|
||||
ConfigBool::ConfigBool(const QString& label, const Config::Info<bool>& setting, bool reverse)
|
||||
: ToolTipCheckBox(label), m_setting(setting), m_reverse(reverse)
|
||||
: ConfigBool(label, setting, nullptr, reverse)
|
||||
{
|
||||
}
|
||||
|
||||
ConfigBool::ConfigBool(const QString& label, const Config::Info<bool>& setting,
|
||||
Config::Layer* layer, bool reverse)
|
||||
: ConfigControl(label, setting.GetLocation(), layer), m_setting(setting), m_reverse(reverse)
|
||||
{
|
||||
setChecked(ReadValue(setting) ^ reverse);
|
||||
|
||||
connect(this, &QCheckBox::toggled, this, &ConfigBool::Update);
|
||||
setChecked(Config::Get(m_setting) ^ reverse);
|
||||
|
||||
connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] {
|
||||
QFont bf = font();
|
||||
bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base);
|
||||
setFont(bf);
|
||||
|
||||
const QSignalBlocker blocker(this);
|
||||
setChecked(Config::Get(m_setting) ^ m_reverse);
|
||||
});
|
||||
}
|
||||
|
||||
void ConfigBool::Update()
|
||||
{
|
||||
Config::SetBaseOrCurrent(m_setting, static_cast<bool>(isChecked() ^ m_reverse));
|
||||
const bool value = static_cast<bool>(isChecked() ^ m_reverse);
|
||||
|
||||
SaveValue(m_setting, value);
|
||||
}
|
||||
|
||||
void ConfigBool::OnConfigChanged()
|
||||
{
|
||||
setChecked(ReadValue(m_setting) ^ m_reverse);
|
||||
}
|
||||
|
|
|
@ -3,23 +3,25 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "DolphinQt/Config/ConfigControls/ConfigControl.h"
|
||||
#include "DolphinQt/Config/ToolTipControls/ToolTipCheckBox.h"
|
||||
|
||||
namespace Config
|
||||
{
|
||||
template <typename T>
|
||||
class Info;
|
||||
}
|
||||
#include "Common/Config/ConfigInfo.h"
|
||||
|
||||
class ConfigBool : public ToolTipCheckBox
|
||||
class ConfigBool final : public ConfigControl<ToolTipCheckBox>
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ConfigBool(const QString& label, const Config::Info<bool>& setting, bool reverse = false);
|
||||
ConfigBool(const QString& label, const Config::Info<bool>& setting, Config::Layer* layer,
|
||||
bool reverse = false);
|
||||
|
||||
protected:
|
||||
void OnConfigChanged() override;
|
||||
|
||||
private:
|
||||
void Update();
|
||||
|
||||
const Config::Info<bool>& m_setting;
|
||||
const Config::Info<bool> m_setting;
|
||||
bool m_reverse;
|
||||
};
|
||||
|
|
|
@ -5,85 +5,168 @@
|
|||
|
||||
#include <QSignalBlocker>
|
||||
|
||||
#include "Common/Config/Config.h"
|
||||
|
||||
#include "DolphinQt/Settings.h"
|
||||
|
||||
ConfigChoice::ConfigChoice(const QStringList& options, const Config::Info<int>& setting)
|
||||
: m_setting(setting)
|
||||
ConfigChoice::ConfigChoice(const QStringList& options, const Config::Info<int>& setting,
|
||||
Config::Layer* layer)
|
||||
: ConfigControl(setting.GetLocation(), layer), m_setting(setting)
|
||||
{
|
||||
addItems(options);
|
||||
setCurrentIndex(ReadValue(setting));
|
||||
|
||||
connect(this, &QComboBox::currentIndexChanged, this, &ConfigChoice::Update);
|
||||
setCurrentIndex(Config::Get(m_setting));
|
||||
|
||||
connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] {
|
||||
QFont bf = font();
|
||||
bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base);
|
||||
setFont(bf);
|
||||
|
||||
const QSignalBlocker blocker(this);
|
||||
setCurrentIndex(Config::Get(m_setting));
|
||||
});
|
||||
}
|
||||
|
||||
void ConfigChoice::Update(int choice)
|
||||
{
|
||||
Config::SetBaseOrCurrent(m_setting, choice);
|
||||
SaveValue(m_setting, choice);
|
||||
}
|
||||
|
||||
void ConfigChoice::OnConfigChanged()
|
||||
{
|
||||
setCurrentIndex(ReadValue(m_setting));
|
||||
}
|
||||
|
||||
ConfigStringChoice::ConfigStringChoice(const std::vector<std::string>& options,
|
||||
const Config::Info<std::string>& setting)
|
||||
: m_setting(setting), m_text_is_data(true)
|
||||
const Config::Info<std::string>& setting,
|
||||
Config::Layer* layer)
|
||||
: ConfigControl(setting.GetLocation(), layer), m_setting(setting), m_text_is_data(true)
|
||||
{
|
||||
for (const auto& op : options)
|
||||
addItem(QString::fromStdString(op));
|
||||
|
||||
Connect();
|
||||
Load();
|
||||
connect(this, &QComboBox::currentIndexChanged, this, &ConfigStringChoice::Update);
|
||||
}
|
||||
|
||||
ConfigStringChoice::ConfigStringChoice(const std::vector<std::pair<QString, QString>>& options,
|
||||
const Config::Info<std::string>& setting)
|
||||
: m_setting(setting), m_text_is_data(false)
|
||||
const Config::Info<std::string>& setting,
|
||||
Config::Layer* layer)
|
||||
: ConfigControl(setting.GetLocation(), layer), m_setting(setting), m_text_is_data(false)
|
||||
{
|
||||
for (const auto& [option_text, option_data] : options)
|
||||
addItem(option_text, option_data);
|
||||
|
||||
Connect();
|
||||
Load();
|
||||
}
|
||||
|
||||
void ConfigStringChoice::Connect()
|
||||
{
|
||||
const auto on_config_changed = [this]() {
|
||||
QFont bf = font();
|
||||
bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base);
|
||||
setFont(bf);
|
||||
|
||||
Load();
|
||||
};
|
||||
|
||||
connect(&Settings::Instance(), &Settings::ConfigChanged, this, on_config_changed);
|
||||
connect(this, &QComboBox::currentIndexChanged, this, &ConfigStringChoice::Update);
|
||||
Load();
|
||||
}
|
||||
|
||||
void ConfigStringChoice::Update(int index)
|
||||
{
|
||||
if (m_text_is_data)
|
||||
{
|
||||
Config::SetBaseOrCurrent(m_setting, itemText(index).toStdString());
|
||||
}
|
||||
SaveValue(m_setting, itemText(index).toStdString());
|
||||
else
|
||||
{
|
||||
Config::SetBaseOrCurrent(m_setting, itemData(index).toString().toStdString());
|
||||
}
|
||||
SaveValue(m_setting, itemData(index).toString().toStdString());
|
||||
}
|
||||
|
||||
void ConfigStringChoice::Load()
|
||||
{
|
||||
const QString setting_value = QString::fromStdString(Config::Get(m_setting));
|
||||
|
||||
const QString setting_value = QString::fromStdString(ReadValue(m_setting));
|
||||
const int index = m_text_is_data ? findText(setting_value) : findData(setting_value);
|
||||
|
||||
// This can be called publicly.
|
||||
const QSignalBlocker block(this);
|
||||
setCurrentIndex(index);
|
||||
}
|
||||
|
||||
void ConfigStringChoice::OnConfigChanged()
|
||||
{
|
||||
Load();
|
||||
}
|
||||
|
||||
ConfigComplexChoice::ConfigComplexChoice(const InfoVariant setting1, const InfoVariant setting2,
|
||||
Config::Layer* layer)
|
||||
: m_layer(layer), m_setting1(setting1), m_setting2(setting2)
|
||||
{
|
||||
connect(&Settings::Instance(), &Settings::ConfigChanged, this, &ConfigComplexChoice::Refresh);
|
||||
connect(this, &QComboBox::currentIndexChanged, this, &ConfigComplexChoice::SaveValue);
|
||||
}
|
||||
|
||||
void ConfigComplexChoice::Refresh()
|
||||
{
|
||||
auto& location = GetLocation();
|
||||
|
||||
QFont bf = font();
|
||||
if (m_layer != nullptr)
|
||||
{
|
||||
bf.setBold(m_layer->Exists(location.first) || m_layer->Exists(location.second));
|
||||
}
|
||||
else
|
||||
{
|
||||
bf.setBold(Config::GetActiveLayerForConfig(location.first) != Config::LayerType::Base ||
|
||||
Config::GetActiveLayerForConfig(location.second) != Config::LayerType::Base);
|
||||
}
|
||||
|
||||
setFont(bf);
|
||||
UpdateComboIndex();
|
||||
}
|
||||
|
||||
void ConfigComplexChoice::Add(const QString& name, const OptionVariant option1,
|
||||
const OptionVariant option2)
|
||||
{
|
||||
const QSignalBlocker blocker(this);
|
||||
addItem(name);
|
||||
m_options.push_back(std::make_pair(option1, option2));
|
||||
}
|
||||
|
||||
void ConfigComplexChoice::Reset()
|
||||
{
|
||||
clear();
|
||||
m_options.clear();
|
||||
}
|
||||
|
||||
void ConfigComplexChoice::SaveValue(int choice)
|
||||
{
|
||||
auto Set = [this](auto& setting, auto& value) {
|
||||
if (m_layer != nullptr)
|
||||
{
|
||||
m_layer->Set(setting.GetLocation(), value);
|
||||
Config::OnConfigChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
Config::SetBaseOrCurrent(setting, value);
|
||||
};
|
||||
|
||||
std::visit(Set, m_setting1, m_options[choice].first);
|
||||
std::visit(Set, m_setting2, m_options[choice].second);
|
||||
}
|
||||
|
||||
void ConfigComplexChoice::UpdateComboIndex()
|
||||
{
|
||||
auto Get = [this](auto& setting) {
|
||||
if (m_layer != nullptr)
|
||||
return static_cast<OptionVariant>(m_layer->Get(setting));
|
||||
|
||||
return static_cast<OptionVariant>(Config::Get(setting));
|
||||
};
|
||||
|
||||
std::pair<OptionVariant, OptionVariant> values =
|
||||
std::make_pair(std::visit(Get, m_setting1), std::visit(Get, m_setting2));
|
||||
|
||||
auto it = std::find(m_options.begin(), m_options.end(), values);
|
||||
int index = static_cast<int>(std::distance(m_options.begin(), it));
|
||||
|
||||
const QSignalBlocker blocker(this);
|
||||
setCurrentIndex(index);
|
||||
}
|
||||
|
||||
const std::pair<Config::Location, Config::Location> ConfigComplexChoice::GetLocation() const
|
||||
{
|
||||
auto visit = [](auto& v) { return v.GetLocation(); };
|
||||
|
||||
return {std::visit(visit, m_setting1), std::visit(visit, m_setting2)};
|
||||
}
|
||||
|
||||
void ConfigComplexChoice::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
if (event->button() == Qt::RightButton && m_layer != nullptr)
|
||||
{
|
||||
auto& location = GetLocation();
|
||||
m_layer->DeleteKey(location.first);
|
||||
m_layer->DeleteKey(location.second);
|
||||
Config::OnConfigChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
QComboBox::mousePressEvent(event);
|
||||
}
|
||||
};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue