diff --git a/.github/ISSUE_TEMPLATE/app-bug-report.yaml b/.github/ISSUE_TEMPLATE/app-bug-report.yaml
index cd540e06e..8959221a0 100644
--- a/.github/ISSUE_TEMPLATE/app-bug-report.yaml
+++ b/.github/ISSUE_TEMPLATE/app-bug-report.yaml
@@ -53,3 +53,24 @@ body:
placeholder: "Example: Windows 11, Arch Linux, MacOS 15"
validations:
required: true
+ - type: input
+ id: cpu
+ attributes:
+ label: CPU
+ placeholder: "Example: Intel Core i7-8700"
+ validations:
+ required: true
+ - type: input
+ id: gpu
+ attributes:
+ label: GPU
+ placeholder: "Example: nVidia GTX 1650"
+ validations:
+ required: true
+ - type: input
+ id: ram
+ attributes:
+ label: Amount of RAM in GB
+ placeholder: "Example: 16 GB"
+ validations:
+ required: true
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 86ca81c38..ceb915f6a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -30,7 +30,7 @@ jobs:
- name: Install
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
- sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 main'
+ sudo add-apt-repository 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main'
sudo apt update
sudo apt install clang-format-19
- name: Build
@@ -205,12 +205,12 @@ jobs:
run: |
mkdir upload
mv ${{github.workspace}}/build/shadps4 upload
- cp ${{github.workspace}}/build/externals/MoltenVK/libMoltenVK.dylib upload
- tar cf shadps4-macos-sdl.tar.gz -C upload .
+ mv ${{github.workspace}}/build/MoltenVK_icd.json upload
+ mv ${{github.workspace}}/build/libMoltenVK.dylib upload
- uses: actions/upload-artifact@v4
with:
name: shadps4-macos-sdl-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
- path: shadps4-macos-sdl.tar.gz
+ path: upload/
macos-qt:
runs-on: macos-15
@@ -281,8 +281,13 @@ jobs:
with:
submodules: recursive
+ - name: Add LLVM repository
+ run: |
+ wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
+ sudo add-apt-repository 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main'
+
- name: Install dependencies
- run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev
+ run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang-19 mold build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev
- name: Cache CMake Configuration
uses: actions/cache@v4
@@ -304,7 +309,7 @@ jobs:
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- name: Configure CMake
- run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
+ run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19 -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
@@ -337,8 +342,13 @@ jobs:
with:
submodules: recursive
+ - name: Add LLVM repository
+ run: |
+ wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
+ sudo add-apt-repository 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main'
+
- name: Install dependencies
- run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev
+ run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang-19 mold build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev
- name: Cache CMake Configuration
uses: actions/cache@v4
@@ -360,7 +370,7 @@ jobs:
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- name: Configure CMake
- run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
+ run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19 -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=mold" -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
@@ -385,7 +395,7 @@ jobs:
submodules: recursive
- name: Install dependencies
- run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev
+ run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 mold build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev
- name: Cache CMake Configuration
uses: actions/cache@v4
@@ -407,7 +417,7 @@ jobs:
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- name: Configure CMake
- run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
+ run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
@@ -421,7 +431,7 @@ jobs:
submodules: recursive
- name: Install dependencies
- run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev
+ run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 mold build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev
- name: Cache CMake Configuration
uses: actions/cache@v4
@@ -443,7 +453,7 @@ jobs:
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- name: Configure CMake
- run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
+ run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=mold" -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
diff --git a/.gitmodules b/.gitmodules
index 3d0d21c5b..065a4570f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,15 +1,3 @@
-[submodule "externals/cryptopp-cmake"]
- path = externals/cryptopp-cmake
- url = https://github.com/shadps4-emu/ext-cryptopp-cmake.git
- shallow = true
-[submodule "externals/cryptopp"]
- path = externals/cryptopp
- url = https://github.com/shadps4-emu/ext-cryptopp.git
- shallow = true
-[submodule "externals/cryptoppwin"]
- path = externals/cryptoppwin
- url = https://github.com/shadps4-emu/ext-cryptoppwin.git
- shallow = true
[submodule "externals/zlib-ng"]
path = externals/zlib-ng
url = https://github.com/shadps4-emu/ext-zlib-ng.git
@@ -119,3 +107,6 @@
path = externals/MoltenVK/cereal
url = https://github.com/USCiLab/cereal
shallow = true
+[submodule "externals/libusb"]
+ path = externals/libusb
+ url = https://github.com/libusb/libusb-cmake.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b05a175bb..e36c1f280 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,7 +9,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
if(APPLE)
list(APPEND ADDITIONAL_LANGUAGES OBJC)
- set(CMAKE_OSX_DEPLOYMENT_TARGET 14)
+ # Starting with 15.4, Rosetta 2 has support for all the necessary instruction sets.
+ set(CMAKE_OSX_DEPLOYMENT_TARGET 15.4 CACHE STRING "")
endif()
if (NOT CMAKE_BUILD_TYPE)
@@ -37,8 +38,10 @@ option(ENABLE_UPDATER "Enables the options to updater" ON)
# First, determine whether to use CMAKE_OSX_ARCHITECTURES or CMAKE_SYSTEM_PROCESSOR.
if (APPLE AND CMAKE_OSX_ARCHITECTURES)
set(BASE_ARCHITECTURE "${CMAKE_OSX_ARCHITECTURES}")
-else()
+elseif (CMAKE_SYSTEM_PROCESSOR)
set(BASE_ARCHITECTURE "${CMAKE_SYSTEM_PROCESSOR}")
+else()
+ set(BASE_ARCHITECTURE "${CMAKE_HOST_SYSTEM_PROCESSOR}")
endif()
# Next, match common architecture strings down to a known common value.
@@ -50,7 +53,13 @@ else()
message(FATAL_ERROR "Unsupported CPU architecture: ${BASE_ARCHITECTURE}")
endif()
-if (APPLE AND ARCHITECTURE STREQUAL "x86_64" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
+if (ARCHITECTURE STREQUAL "x86_64")
+ # Target the same CPU architecture as the PS4, to maintain the same level of compatibility.
+ # Exclude SSE4a as it is only available on AMD CPUs.
+ add_compile_options(-march=btver2 -mtune=generic -mno-sse4a)
+endif()
+
+if (APPLE AND ARCHITECTURE STREQUAL "x86_64" AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64")
# Exclude ARM homebrew path to avoid conflicts when cross compiling.
list(APPEND CMAKE_IGNORE_PREFIX_PATH "/opt/homebrew")
@@ -96,38 +105,81 @@ if (CLANG_FORMAT)
unset(CCOMMENT)
endif()
-list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
-
# generate git revision information
-list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules/")
-include(GetGitRevisionDescription)
+include("${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules/GetGitRevisionDescription.cmake")
get_git_head_revision(GIT_REF_SPEC GIT_REV)
git_describe(GIT_DESC --always --long --dirty)
git_branch_name(GIT_BRANCH)
string(TIMESTAMP BUILD_DATE "%Y-%m-%d %H:%M:%S")
+message("start git things")
+
# Try to get the upstream remote and branch
+message("check for remote and branch")
execute_process(
COMMAND git rev-parse --abbrev-ref --symbolic-full-name @{u}
OUTPUT_VARIABLE GIT_REMOTE_NAME
- RESULT_VARIABLE GIT_BRANCH_RESULT
+ RESULT_VARIABLE GIT_REMOTE_RESULT
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# If there's no upstream set or the command failed, check remote.pushDefault
-if (GIT_BRANCH_RESULT OR GIT_REMOTE_NAME STREQUAL "")
+if (GIT_REMOTE_RESULT OR GIT_REMOTE_NAME STREQUAL "")
+ message("check default push")
execute_process(
COMMAND git config --get remote.pushDefault
OUTPUT_VARIABLE GIT_REMOTE_NAME
- RESULT_VARIABLE GIT_PUSH_DEFAULT_RESULT
+ RESULT_VARIABLE GIT_REMOTE_RESULT
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE
)
-
- # If remote.pushDefault is not set or fails, default to origin
- if (GIT_PUSH_DEFAULT_RESULT OR GIT_REMOTE_NAME STREQUAL "")
- set(GIT_REMOTE_NAME "origin")
+endif()
+
+# If running in GitHub Actions and the above fails
+if (GIT_REMOTE_RESULT OR GIT_REMOTE_NAME STREQUAL "")
+ message("check github")
+ set(GIT_REMOTE_NAME "origin")
+
+ # Retrieve environment variables
+ if (DEFINED ENV{GITHUB_HEAD_REF} AND NOT "$ENV{GITHUB_HEAD_REF}" STREQUAL "")
+ message("github head ref: $ENV{GITHUB_HEAD_REF}")
+ set(GITHUB_HEAD_REF "$ENV{GITHUB_HEAD_REF}")
+ else()
+ set(GITHUB_HEAD_REF "")
+ endif()
+
+ if (DEFINED ENV{GITHUB_REF} AND NOT "$ENV{GITHUB_REF}" STREQUAL "")
+ message("github ref: $ENV{GITHUB_REF}")
+ string(REGEX REPLACE "^refs/[^/]*/" "" GITHUB_BRANCH "$ENV{GITHUB_REF}")
+ string(REGEX MATCH "refs/pull/([0-9]+)/merge" MATCHED_REF "$ENV{GITHUB_REF}")
+ if (MATCHED_REF)
+ set(PR_NUMBER "${CMAKE_MATCH_1}")
+ set(GITHUB_BRANCH "")
+ message("PR number: ${PR_NUMBER}")
+ else()
+ set(PR_NUMBER "")
+ endif()
+ else()
+ set(GITHUB_BRANCH "")
+ set(PR_NUMBER "")
+ endif()
+
+ if (NOT "${PR_NUMBER}" STREQUAL "" AND NOT "${GITHUB_HEAD_REF}" STREQUAL "")
+ set(GIT_BRANCH "pr-${PR_NUMBER}-${GITHUB_HEAD_REF}")
+ elseif (NOT "${PR_NUMBER}" STREQUAL "" AND NOT "${GITHUB_BRANCH}" STREQUAL "")
+ set(GIT_BRANCH "pr-${PR_NUMBER}-${GITHUB_BRANCH}")
+ elseif (NOT "${PR_NUMBER}" STREQUAL "")
+ set(GIT_BRANCH "pr-${PR_NUMBER}")
+ elseif ("${PR_NUMBER}" STREQUAL "" AND NOT "${GITHUB_HEAD_REF}" STREQUAL "")
+ set(GIT_BRANCH "${GITHUB_HEAD_REF}")
+ elseif ("${PR_NUMBER}" STREQUAL "" AND NOT "${GITHUB_BRANCH}" STREQUAL "")
+ set(GIT_BRANCH "${GITHUB_BRANCH}")
+ elseif ("${PR_NUMBER}" STREQUAL "" AND NOT "${GITHUB_REF}" STREQUAL "")
+ set(GIT_BRANCH "${GITHUB_REF}")
+ else()
+ message("couldn't find branch")
+ set(GIT_BRANCH "detached-head")
endif()
else()
# Extract remote name if the output contains a remote/branch format
@@ -141,14 +193,27 @@ else()
endif()
# Get remote link
+message("getting remote link")
execute_process(
COMMAND git config --get remote.${GIT_REMOTE_NAME}.url
OUTPUT_VARIABLE GIT_REMOTE_URL
OUTPUT_STRIP_TRAILING_WHITESPACE
)
+# Set Version
+set(EMULATOR_VERSION_MAJOR "0")
+set(EMULATOR_VERSION_MINOR "8")
+set(EMULATOR_VERSION_PATCH "1")
+
+set_source_files_properties(src/shadps4.rc PROPERTIES COMPILE_DEFINITIONS "EMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR};EMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR};EMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH}")
+
+set(APP_VERSION "${EMULATOR_VERSION_MAJOR}.${EMULATOR_VERSION_MINOR}.${EMULATOR_VERSION_PATCH} WIP")
+set(APP_IS_RELEASE false)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/common/scm_rev.cpp" @ONLY)
+message("end git things, remote: ${GIT_REMOTE_NAME}, branch: ${GIT_BRANCH}")
+
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
find_package(Boost 1.84.0 CONFIG)
find_package(FFmpeg 5.1.2 MODULE)
find_package(fmt 10.2.0 CONFIG)
@@ -161,21 +226,18 @@ find_package(SDL3 3.1.2 CONFIG)
find_package(stb MODULE)
find_package(toml11 4.2.0 CONFIG)
find_package(tsl-robin-map 1.3.0 CONFIG)
-find_package(VulkanHeaders 1.4.305 CONFIG)
+find_package(VulkanHeaders 1.4.309 CONFIG)
find_package(VulkanMemoryAllocator 3.1.0 CONFIG)
find_package(xbyak 7.07 CONFIG)
find_package(xxHash 0.8.2 MODULE)
find_package(ZLIB 1.3 MODULE)
find_package(Zydis 5.0.0 CONFIG)
find_package(pugixml 1.14 CONFIG)
-
-if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR NOT MSVC)
- find_package(cryptopp 8.9.0 MODULE)
-endif()
-
+find_package(libusb 1.0.27 MODULE)
if (APPLE)
find_package(date 3.0.1 CONFIG)
endif()
+list(POP_BACK CMAKE_MODULE_PATH)
# Note: Windows always has these functions through winpthreads
include(CheckSymbolExists)
@@ -278,6 +340,8 @@ set(KERNEL_LIB src/core/libraries/kernel/sync/mutex.cpp
src/core/libraries/kernel/threads/thread_state.h
src/core/libraries/kernel/process.cpp
src/core/libraries/kernel/process.h
+ src/core/libraries/kernel/debug.cpp
+ src/core/libraries/kernel/debug.h
src/core/libraries/kernel/equeue.cpp
src/core/libraries/kernel/equeue.h
src/core/libraries/kernel/file_system.cpp
@@ -298,6 +362,7 @@ set(KERNEL_LIB src/core/libraries/kernel/sync/mutex.cpp
set(NETWORK_LIBS src/core/libraries/network/http.cpp
src/core/libraries/network/http.h
+ src/core/libraries/network/http_error.h
src/core/libraries/network/http2.cpp
src/core/libraries/network/http2.h
src/core/libraries/network/net.cpp
@@ -370,12 +435,28 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
src/core/libraries/ngs2/ngs2_error.h
src/core/libraries/ngs2/ngs2_impl.cpp
src/core/libraries/ngs2/ngs2_impl.h
+ src/core/libraries/ngs2/ngs2_custom.cpp
+ src/core/libraries/ngs2/ngs2_custom.h
+ src/core/libraries/ngs2/ngs2_reverb.cpp
+ src/core/libraries/ngs2/ngs2_reverb.h
+ src/core/libraries/ngs2/ngs2_geom.cpp
+ src/core/libraries/ngs2/ngs2_geom.h
+ src/core/libraries/ngs2/ngs2_pan.cpp
+ src/core/libraries/ngs2/ngs2_pan.h
+ src/core/libraries/ngs2/ngs2_report.cpp
+ src/core/libraries/ngs2/ngs2_report.h
+ src/core/libraries/ngs2/ngs2_eq.cpp
+ src/core/libraries/ngs2/ngs2_eq.h
+ src/core/libraries/ngs2/ngs2_mastering.cpp
+ src/core/libraries/ngs2/ngs2_mastering.h
+ src/core/libraries/ngs2/ngs2_sampler.cpp
+ src/core/libraries/ngs2/ngs2_sampler.h
+ src/core/libraries/ngs2/ngs2_submixer.cpp
+ src/core/libraries/ngs2/ngs2_submixer.h
src/core/libraries/ajm/ajm_error.h
src/core/libraries/audio3d/audio3d.cpp
src/core/libraries/audio3d/audio3d.h
src/core/libraries/audio3d/audio3d_error.h
- src/core/libraries/audio3d/audio3d_impl.cpp
- src/core/libraries/audio3d/audio3d_impl.h
src/core/libraries/game_live_streaming/gamelivestreaming.cpp
src/core/libraries/game_live_streaming/gamelivestreaming.h
src/core/libraries/remote_play/remoteplay.cpp
@@ -489,6 +570,8 @@ set(NP_LIBS src/core/libraries/np_common/np_common.cpp
src/core/libraries/np_web_api/np_web_api.h
src/core/libraries/np_party/np_party.cpp
src/core/libraries/np_party/np_party.h
+ src/core/libraries/np_auth/np_auth.cpp
+ src/core/libraries/np_auth/np_auth.h
)
set(ZLIB_LIB src/core/libraries/zlib/zlib.cpp
@@ -504,6 +587,8 @@ set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp
src/core/libraries/screenshot/screenshot.h
src/core/libraries/move/move.cpp
src/core/libraries/move/move.h
+ src/core/libraries/ulobjmgr/ulobjmgr.cpp
+ src/core/libraries/ulobjmgr/ulobjmgr.h
)
set(DEV_TOOLS src/core/devtools/layer.cpp
@@ -543,6 +628,7 @@ set(COMMON src/common/logging/backend.cpp
src/common/logging/text_formatter.cpp
src/common/logging/text_formatter.h
src/common/logging/types.h
+ src/common/aes.h
src/common/alignment.h
src/common/arch.h
src/common/assert.cpp
@@ -574,6 +660,7 @@ set(COMMON src/common/logging/backend.cpp
src/common/polyfill_thread.h
src/common/rdtsc.cpp
src/common/rdtsc.h
+ src/common/sha1.h
src/common/signal_context.h
src/common/signal_context.cpp
src/common/singleton.h
@@ -583,6 +670,7 @@ set(COMMON src/common/logging/backend.cpp
src/common/spin_lock.h
src/common/stb.cpp
src/common/stb.h
+ src/common/string_literal.h
src/common/string_util.cpp
src/common/string_util.h
src/common/thread.cpp
@@ -591,7 +679,6 @@ set(COMMON src/common/logging/backend.cpp
src/common/uint128.h
src/common/unique_function.h
src/common/va_ctx.h
- src/common/version.h
src/common/ntapi.h
src/common/ntapi.cpp
src/common/number_utils.h
@@ -612,9 +699,6 @@ set(CORE src/core/aerolib/stubs.cpp
src/core/aerolib/aerolib.h
src/core/address_space.cpp
src/core/address_space.h
- src/core/crypto/crypto.cpp
- src/core/crypto/crypto.h
- src/core/crypto/keys.h
src/core/devices/base_device.cpp
src/core/devices/base_device.h
src/core/devices/ioccom.h
@@ -632,22 +716,14 @@ set(CORE src/core/aerolib/stubs.cpp
src/core/devices/srandom_device.cpp
src/core/devices/srandom_device.h
src/core/file_format/pfs.h
- src/core/file_format/pkg.cpp
- src/core/file_format/pkg.h
- src/core/file_format/pkg_type.cpp
- src/core/file_format/pkg_type.h
src/core/file_format/psf.cpp
src/core/file_format/psf.h
src/core/file_format/playgo_chunk.cpp
src/core/file_format/playgo_chunk.h
src/core/file_format/trp.cpp
src/core/file_format/trp.h
- src/core/file_format/splash.h
- src/core/file_format/splash.cpp
src/core/file_sys/fs.cpp
src/core/file_sys/fs.h
- src/core/loader.cpp
- src/core/loader.h
src/core/loader/dwarf.cpp
src/core/loader/dwarf.h
src/core/loader/elf.cpp
@@ -764,6 +840,8 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h
src/shader_recompiler/ir/passes/identity_removal_pass.cpp
src/shader_recompiler/ir/passes/ir_passes.h
src/shader_recompiler/ir/passes/lower_buffer_format_to_raw.cpp
+ src/shader_recompiler/ir/passes/lower_fp64_to_fp32.cpp
+ src/shader_recompiler/ir/passes/readlane_elimination_pass.cpp
src/shader_recompiler/ir/passes/resource_tracking_pass.cpp
src/shader_recompiler/ir/passes/ring_access_elimination.cpp
src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp
@@ -844,6 +922,10 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
src/video_core/renderer_vulkan/vk_shader_util.h
src/video_core/renderer_vulkan/vk_swapchain.cpp
src/video_core/renderer_vulkan/vk_swapchain.h
+ src/video_core/renderer_vulkan/host_passes/fsr_pass.cpp
+ src/video_core/renderer_vulkan/host_passes/fsr_pass.h
+ src/video_core/renderer_vulkan/host_passes/pp_pass.cpp
+ src/video_core/renderer_vulkan/host_passes/pp_pass.h
src/video_core/texture_cache/image.cpp
src/video_core/texture_cache/image.h
src/video_core/texture_cache/image_info.cpp
@@ -916,6 +998,9 @@ set(QT_GUI src/qt_gui/about_dialog.cpp
src/qt_gui/control_settings.cpp
src/qt_gui/control_settings.h
src/qt_gui/control_settings.ui
+ src/qt_gui/kbm_gui.cpp
+ src/qt_gui/kbm_gui.h
+ src/qt_gui/kbm_gui.ui
src/qt_gui/main_window_ui.h
src/qt_gui/main_window.cpp
src/qt_gui/main_window.h
@@ -929,10 +1014,6 @@ set(QT_GUI src/qt_gui/about_dialog.cpp
src/qt_gui/game_grid_frame.h
src/qt_gui/game_install_dialog.cpp
src/qt_gui/game_install_dialog.h
- src/qt_gui/install_dir_select.cpp
- src/qt_gui/install_dir_select.h
- src/qt_gui/pkg_viewer.cpp
- src/qt_gui/pkg_viewer.h
src/qt_gui/trophy_viewer.cpp
src/qt_gui/trophy_viewer.h
src/qt_gui/elf_viewer.cpp
@@ -988,7 +1069,7 @@ endif()
create_target_directory_groups(shadps4)
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG)
-target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers)
+target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers libusb::usb)
target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h")
target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h")
@@ -1003,46 +1084,49 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND ENABLE_USERFAULTFD)
endif()
if (APPLE)
- if (ENABLE_QT_GUI)
- # Include MoltenVK in the app bundle, along with an ICD file so it can be found by the system Vulkan loader if used for loading layers.
- set(MVK_ICD ${CMAKE_CURRENT_SOURCE_DIR}/externals/MoltenVK/MoltenVK_icd.json)
- target_sources(shadps4 PRIVATE ${MVK_ICD})
- set_source_files_properties(${MVK_ICD} PROPERTIES MACOSX_PACKAGE_LOCATION Resources/vulkan/icd.d)
+ # Include MoltenVK, along with an ICD file so it can be found by the system Vulkan loader if used for loading layers.
+ if (ENABLE_QT_GUI)
+ set(MVK_BUNDLE_PATH "Resources/vulkan/icd.d")
+ set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path/../${MVK_BUNDLE_PATH}")
+ set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR}/shadps4.app/Contents/${MVK_BUNDLE_PATH})
+ else()
+ set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path")
+ set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR})
+ endif()
- set(MVK_DYLIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/externals/MoltenVK/libMoltenVK.dylib)
- set(MVK_DYLIB_DST ${CMAKE_CURRENT_BINARY_DIR}/shadps4.app/Contents/Frameworks/libMoltenVK.dylib)
- add_custom_command(
- OUTPUT ${MVK_DYLIB_DST}
- DEPENDS ${MVK_DYLIB_SRC}
- COMMAND cmake -E copy ${MVK_DYLIB_SRC} ${MVK_DYLIB_DST})
- add_custom_target(CopyMoltenVK DEPENDS ${MVK_DYLIB_DST})
- add_dependencies(CopyMoltenVK MoltenVK)
- add_dependencies(shadps4 CopyMoltenVK)
- set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path/../Frameworks")
- else()
- # For non-bundled SDL build, just do a normal library link.
- target_link_libraries(shadps4 PRIVATE MoltenVK)
- endif()
+ set(MVK_DYLIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/externals/MoltenVK/libMoltenVK.dylib)
+ set(MVK_DYLIB_DST ${MVK_DST}/libMoltenVK.dylib)
+ set(MVK_ICD_SRC ${CMAKE_CURRENT_SOURCE_DIR}/externals/MoltenVK/MoltenVK/MoltenVK/icd/MoltenVK_icd.json)
+ set(MVK_ICD_DST ${MVK_DST}/MoltenVK_icd.json)
- if (ARCHITECTURE STREQUAL "x86_64")
- # Reserve system-managed memory space.
- target_link_options(shadps4 PRIVATE -Wl,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,SYSTEM_MANAGED,0x400000,-segaddr,SYSTEM_RESERVED,0x7FFFFC000,-image_base,0x20000000000)
- endif()
+ add_custom_command(
+ OUTPUT ${MVK_DST}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${MVK_DST})
+ add_custom_command(
+ OUTPUT ${MVK_ICD_DST}
+ DEPENDS ${MVK_ICD_SRC} ${MVK_DST}
+ COMMAND ${CMAKE_COMMAND} -E copy ${MVK_ICD_SRC} ${MVK_ICD_DST})
+ add_custom_command(
+ OUTPUT ${MVK_DYLIB_DST}
+ DEPENDS ${MVK_DYLIB_SRC} ${MVK_DST}
+ COMMAND ${CMAKE_COMMAND} -E copy ${MVK_DYLIB_SRC} ${MVK_DYLIB_DST})
+ add_custom_target(CopyMoltenVK DEPENDS ${MVK_ICD_DST} ${MVK_DYLIB_DST})
+ add_dependencies(CopyMoltenVK MoltenVK)
+ add_dependencies(shadps4 CopyMoltenVK)
- # Replacement for std::chrono::time_zone
- target_link_libraries(shadps4 PRIVATE date::date-tz)
+ if (ARCHITECTURE STREQUAL "x86_64")
+ # Reserve system-managed memory space.
+ target_link_options(shadps4 PRIVATE -Wl,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,SYSTEM_MANAGED,0x400000,-segaddr,SYSTEM_RESERVED,0x7FFFFC000,-image_base,0x20000000000)
+ endif()
+
+ # Replacement for std::chrono::time_zone
+ target_link_libraries(shadps4 PRIVATE date::date-tz)
endif()
if (NOT ENABLE_QT_GUI)
target_link_libraries(shadps4 PRIVATE SDL3::SDL3)
endif()
-if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND MSVC)
- target_link_libraries(shadps4 PRIVATE cryptoppwin)
-else()
- target_link_libraries(shadps4 PRIVATE cryptopp::cryptopp)
-endif()
-
if (ENABLE_QT_GUI)
target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network Qt6::Multimedia)
add_definitions(-DENABLE_QT_GUI)
@@ -1105,7 +1189,7 @@ target_include_directories(shadps4 PRIVATE ${HOST_SHADERS_INCLUDE})
# embed resources
-include(CMakeRC)
+include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CMakeRC.cmake")
cmrc_add_resource_library(embedded-resources
ALIAS res::embedded
NAMESPACE res
@@ -1113,7 +1197,6 @@ cmrc_add_resource_library(embedded-resources
src/images/gold.png
src/images/platinum.png
src/images/silver.png)
-
target_link_libraries(shadps4 PRIVATE res::embedded)
# ImGui resources
@@ -1127,8 +1210,8 @@ if (ENABLE_QT_GUI)
MACOSX_BUNDLE ON
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/dist/MacOSBundleInfo.plist.in"
MACOSX_BUNDLE_ICON_FILE "shadPS4.icns"
- MACOSX_BUNDLE_SHORT_VERSION_STRING "0.4.1"
- )
+ MACOSX_BUNDLE_SHORT_VERSION_STRING "${APP_VERSION}"
+ )
set_source_files_properties(src/images/shadPS4.icns PROPERTIES
MACOSX_PACKAGE_LOCATION Resources)
diff --git a/README.md b/README.md
index 9259ab875..985bba586 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
-
+
@@ -71,7 +71,7 @@ Check the build instructions for [**Linux**](https://github.com/shadps4-emu/shad
Check the build instructions for [**macOS**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/building-macos.md).
> [!IMPORTANT]
-> macOS users need at least macOS 15 on Apple Silicon-based Mac devices and at least macOS 14 on Intel-based Mac devices.
+> macOS users need at least macOS 15.4 to run shadPS4. Due to GPU issues there are currently heavy bugs on Intel Macs.
# Debugging and reporting issues
@@ -122,19 +122,37 @@ R3 | M |
Keyboard and mouse inputs can be customized in the settings menu by clicking the Controller button, and further details and help on controls are also found there. Custom bindings are saved per-game. Inputs support up to three keys per binding, mouse buttons, mouse movement mapped to joystick input, and more.
+# Firmware files
+
+shadPS4 can load some PlayStation 4 firmware files, these must be dumped from your legally owned PlayStation 4 console.\
+The following firmware modules are supported and must be placed in shadPS4's `user/sys_modules` folder.
+
+
+
+| Modules | Modules | Modules | Modules |
+|-------------------------|-------------------------|-------------------------|-------------------------|
+| libSceCesCs.sprx | libSceFont.sprx | libSceFontFt.sprx | libSceFreeTypeOt.sprx |
+| libSceJson.sprx | libSceJson2.sprx | libSceLibcInternal.sprx | libSceNgs2.sprx |
+| libSceRtc.sprx | libSceUlt.sprx | | |
+
+
+
+> [!Caution]
+> The above modules are required to run the games properly and must be extracted from your PlayStation 4.\
+> **We do not provide any information or support on how to do this**.
+
+
+
# Main team
- [**georgemoralis**](https://github.com/georgemoralis)
-- [**raphaelthegreat**](https://github.com/raphaelthegreat)
- [**psucien**](https://github.com/psucien)
-- [**skmp**](https://github.com/skmp)
-- [**wheremyfoodat**](https://github.com/wheremyfoodat)
-- [**raziel1000**](https://github.com/raziel1000)
- [**viniciuslrangel**](https://github.com/viniciuslrangel)
- [**roamic**](https://github.com/vladmikhalin)
-- [**poly**](https://github.com/polybiusproxy)
- [**squidbus**](https://github.com/squidbus)
- [**frodo**](https://github.com/baggins183)
+- [**Stephen Miller**](https://github.com/StevenMiller123)
+- [**kalaposfos13**](https://github.com/kalaposfos13)
Logo is done by [**Xphalnos**](https://github.com/Xphalnos)
@@ -145,11 +163,11 @@ Open a PR and we'll check it :)
# Translations
-If you want to translate shadPS4 to your language we use [**crowdin**](https://crowdin.com/project/shadps4-emulator).
+If you want to translate shadPS4 to your language we use [**Crowdin**](https://crowdin.com/project/shadps4-emulator).
# Contributors
-
+
diff --git a/REUSE.toml b/REUSE.toml
index d8f31c2f2..662987611 100644
--- a/REUSE.toml
+++ b/REUSE.toml
@@ -30,6 +30,7 @@ path = [
"src/images/dump_icon.png",
"src/images/exit_icon.png",
"src/images/file_icon.png",
+ "src/images/trophy_icon.png",
"src/images/flag_china.png",
"src/images/flag_eu.png",
"src/images/flag_jp.png",
@@ -41,23 +42,33 @@ path = [
"src/images/grid_icon.png",
"src/images/keyboard_icon.png",
"src/images/iconsize_icon.png",
+ "src/images/KBM.png",
"src/images/ko-fi.png",
"src/images/list_icon.png",
"src/images/list_mode_icon.png",
"src/images/pause_icon.png",
"src/images/play_icon.png",
"src/images/ps4_controller.png",
- "src/images/refresh_icon.png",
+ "src/images/restart_game_icon.png",
+ "src/images/refreshlist_icon.png",
"src/images/settings_icon.png",
+ "src/images/fullscreen_icon.png",
"src/images/stop_icon.png",
"src/images/utils_icon.png",
"src/images/shadPS4.icns",
"src/images/shadps4.ico",
+ "src/images/shadps4.png",
"src/images/net.shadps4.shadPS4.svg",
"src/images/themes_icon.png",
"src/images/update_icon.png",
"src/images/youtube.png",
"src/images/website.png",
+ "src/images/discord.svg",
+ "src/images/github.svg",
+ "src/images/ko-fi.svg",
+ "src/images/shadps4.svg",
+ "src/images/website.svg",
+ "src/images/youtube.svg",
"src/shadps4.qrc",
"src/shadps4.rc",
"src/qt_gui/translations/update_translation.sh",
@@ -110,4 +121,9 @@ SPDX-License-Identifier = "CC0-1.0"
[[annotations]]
path = "cmake/CMakeRC.cmake"
SPDX-FileCopyrightText = "Copyright (c) 2017 vector-of-bool "
-SPDX-License-Identifier = "MIT"
\ No newline at end of file
+SPDX-License-Identifier = "MIT"
+
+[[annotations]]
+path = "src/video_core/host_shaders/fsr/*"
+SPDX-FileCopyrightText = "Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved."
+SPDX-License-Identifier = "MIT"
diff --git a/cmake/Findcryptopp.cmake b/cmake/Findcryptopp.cmake
deleted file mode 100644
index d57c0bc54..000000000
--- a/cmake/Findcryptopp.cmake
+++ /dev/null
@@ -1,15 +0,0 @@
-# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-# SPDX-License-Identifier: GPL-2.0-or-later
-
-find_package(PkgConfig QUIET)
-pkg_search_module(CRYPTOPP QUIET IMPORTED_TARGET libcryptopp)
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(cryptopp
- REQUIRED_VARS CRYPTOPP_LINK_LIBRARIES
- VERSION_VAR CRYPTOPP_VERSION
-)
-
-if (cryptopp_FOUND AND NOT TARGET cryptopp::cryptopp)
- add_library(cryptopp::cryptopp ALIAS PkgConfig::CRYPTOPP)
-endif()
diff --git a/cmake/Findlibusb.cmake b/cmake/Findlibusb.cmake
new file mode 100644
index 000000000..c8b35e979
--- /dev/null
+++ b/cmake/Findlibusb.cmake
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+find_package(PkgConfig QUIET)
+pkg_search_module(LIBUSB QUIET IMPORTED_TARGET libusb-1.0)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(libusb
+ REQUIRED_VARS LIBUSB_LINK_LIBRARIES
+ VERSION_VAR LIBUSB_VERSION
+)
+
+if (libusb_FOUND AND NOT TARGET libusb::usb)
+ add_library(libusb::usb ALIAS PkgConfig::LIBUSB)
+endif()
diff --git a/dist/net.shadps4.shadPS4.metainfo.xml b/dist/net.shadps4.shadPS4.metainfo.xml
index c8c9d5c23..9f7b4f9c5 100644
--- a/dist/net.shadps4.shadPS4.metainfo.xml
+++ b/dist/net.shadps4.shadPS4.metainfo.xml
@@ -37,6 +37,12 @@
Game
+
+ https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.8.0
+
+
+ https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.7.0
+
https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.6.0
diff --git a/documents/Quickstart/2.png b/documents/Quickstart/2.png
deleted file mode 100644
index 7e5bdfb15..000000000
Binary files a/documents/Quickstart/2.png and /dev/null differ
diff --git a/documents/Quickstart/Quickstart.md b/documents/Quickstart/Quickstart.md
index 2f2751887..62df95e71 100644
--- a/documents/Quickstart/Quickstart.md
+++ b/documents/Quickstart/Quickstart.md
@@ -13,7 +13,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
- [**RAM**](#ram)
- [**OS**](#os)
- [**Have the latest WIP version**](#how-to-run-the-latest-work-in-progress-builds-of-shadps4)
-- [**Install PKG files (Games and Updates)**](#install-pkg-files)
- [**Configure the emulator**](#configure-the-emulator)
## Minimum PC requirements
@@ -25,13 +24,13 @@ SPDX-License-Identifier: GPL-2.0-or-later
- A CPU supporting the following instruction sets: MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, F16C, CLMUL, AES, BMI1, MOVBE, XSAVE, ABM
- **Intel**: Haswell generation or newer
- **AMD**: Jaguar generation or newer
- - **Apple**: Rosetta 2 on macOS 15 or newer
+ - **Apple**: Rosetta 2 on macOS 15.4 or newer
### GPU
- A graphics card with at least 1GB of VRAM
-- Keep your graphics drivers up to date
-- Vulkan 1.3 support (required)
+- Up-to-date graphics drivers
+- Vulkan 1.3 with the `VK_KHR_swapchain` and `VK_KHR_push_descriptor` extensions
### RAM
@@ -48,13 +47,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
2. Once downloaded, extract to its own folder, and run shadPS4's executable from the extracted folder.
-3. Upon first launch, shadPS4 will prompt you to select a folder to store your installed games in. Select "Browse" and then select a folder that shadPS4 can use to install your PKG files to.
-
-## Install PKG files
-
-To install PKG files (game and updates), you will need the Qt application (with UI). You will have to go to "File" then to "Install Packages (PKG)", a window will open then you will have to select the files. You can install multiple PKG files at once. Once finished, the game should appear in the application.
-
-
+3. Upon first launch, shadPS4 will prompt you to select a folder to store your installed games in. Select "Browse" and then select a folder that contains your dumped games.
## Configure the emulator
diff --git a/documents/building-linux.md b/documents/building-linux.md
index 18ddab0c6..cdc8ba12f 100644
--- a/documents/building-linux.md
+++ b/documents/building-linux.md
@@ -108,7 +108,7 @@ Now run the emulator. If Qt was enabled at configure time:
./build/shadps4
```
-Otherwise, specify the path to your PKG's boot file:
+Otherwise, specify the path to your game's boot file:
```bash
./build/shadps4 /"PATH"/"TO"/"GAME"/"FOLDER"/eboot.bin
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index bb434677d..b92e13795 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -26,24 +26,7 @@ if (NOT TARGET fmt::fmt)
add_subdirectory(fmt)
endif()
-if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND MSVC)
- # If it is clang and MSVC we will add a static lib
- # CryptoPP
- add_subdirectory(cryptoppwin)
- target_include_directories(cryptoppwin INTERFACE cryptoppwin/include)
-else()
- # CryptoPP
- if (NOT TARGET cryptopp::cryptopp)
- set(CRYPTOPP_INSTALL OFF)
- set(CRYPTOPP_BUILD_TESTING OFF)
- set(CRYPTOPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp)
- add_subdirectory(cryptopp-cmake)
- file(COPY cryptopp DESTINATION cryptopp FILES_MATCHING PATTERN "*.h")
- # remove externals/cryptopp from include directories because it contains a conflicting zlib.h file
- set_target_properties(cryptopp PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/cryptopp")
- endif()
-endif()
-
+# FFmpeg
if (NOT TARGET FFmpeg::ffmpeg)
add_subdirectory(ffmpeg-core)
add_library(FFmpeg::ffmpeg ALIAS ffmpeg)
@@ -218,6 +201,12 @@ if (NOT TARGET pugixml::pugixml)
add_subdirectory(pugixml)
endif()
+# libusb
+if (NOT TARGET libusb::usb)
+ add_subdirectory(libusb)
+ add_library(libusb::usb ALIAS usb-1.0)
+endif()
+
# Discord RPC
if (ENABLE_DISCORD_RPC)
add_subdirectory(discord-rpc)
diff --git a/externals/MoltenVK/MoltenVK b/externals/MoltenVK/MoltenVK
index 2048427e5..87a8e8b13 160000
--- a/externals/MoltenVK/MoltenVK
+++ b/externals/MoltenVK/MoltenVK
@@ -1 +1 @@
-Subproject commit 2048427e50f9eb20f2b8f98d316ecaee398c9b91
+Subproject commit 87a8e8b13d4ad8835367fea1ebad1896d0460946
diff --git a/externals/MoltenVK/MoltenVK_icd.json b/externals/MoltenVK/MoltenVK_icd.json
deleted file mode 100644
index 2c3319263..000000000
--- a/externals/MoltenVK/MoltenVK_icd.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "file_format_version": "1.0.0",
- "ICD": {
- "library_path": "../../../Frameworks/libMoltenVK.dylib",
- "api_version": "1.2.0",
- "is_portability_driver": true
- }
-}
diff --git a/externals/MoltenVK/SPIRV-Cross b/externals/MoltenVK/SPIRV-Cross
index 2c32b6bf8..791877574 160000
--- a/externals/MoltenVK/SPIRV-Cross
+++ b/externals/MoltenVK/SPIRV-Cross
@@ -1 +1 @@
-Subproject commit 2c32b6bf86f3c4a5539aa1f0bacbd59fe61759cf
+Subproject commit 7918775748c5e2f5c40d9918ce68825035b5a1e1
diff --git a/externals/MoltenVK/cereal b/externals/MoltenVK/cereal
index d1fcec807..a56bad8bb 160000
--- a/externals/MoltenVK/cereal
+++ b/externals/MoltenVK/cereal
@@ -1 +1 @@
-Subproject commit d1fcec807b372f04e4c1041b3058e11c12853e6e
+Subproject commit a56bad8bbb770ee266e930c95d37fff2a5be7fea
diff --git a/externals/cryptopp b/externals/cryptopp
deleted file mode 160000
index 60f81a77e..000000000
--- a/externals/cryptopp
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 60f81a77e0c9a0e7ffc1ca1bc438ddfa2e43b78e
diff --git a/externals/cryptopp-cmake b/externals/cryptopp-cmake
deleted file mode 160000
index 2c384c282..000000000
--- a/externals/cryptopp-cmake
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 2c384c28265a93358a2455e610e76393358794df
diff --git a/externals/cryptoppwin b/externals/cryptoppwin
deleted file mode 160000
index bc3441dd2..000000000
--- a/externals/cryptoppwin
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit bc3441dd2d6a9728e747dc0180bc8b9065a2923c
diff --git a/externals/date b/externals/date
index 28b7b2325..a45ea7c17 160000
--- a/externals/date
+++ b/externals/date
@@ -1 +1 @@
-Subproject commit 28b7b232521ace2c8ef3f2ad4126daec3569c14f
+Subproject commit a45ea7c17b4a7f320e199b71436074bd624c9e15
diff --git a/externals/dear_imgui b/externals/dear_imgui
index 636cd4a7d..f4d935909 160000
--- a/externals/dear_imgui
+++ b/externals/dear_imgui
@@ -1 +1 @@
-Subproject commit 636cd4a7d623a2bc9bf59bb3acbb4ca075befba3
+Subproject commit f4d9359095eff3eb03f685921edc1cf0e37b1687
diff --git a/externals/discord-rpc b/externals/discord-rpc
index 51b09d426..19f66e6dc 160000
--- a/externals/discord-rpc
+++ b/externals/discord-rpc
@@ -1 +1 @@
-Subproject commit 51b09d426a4a1bcfa6ee6d4894e57d669f4a2e65
+Subproject commit 19f66e6dcabb2268965f453db9e5774ede43238f
diff --git a/externals/ffmpeg-core b/externals/ffmpeg-core
index 27de97c82..b0de1dcca 160000
--- a/externals/ffmpeg-core
+++ b/externals/ffmpeg-core
@@ -1 +1 @@
-Subproject commit 27de97c826b6b40c255891c37ac046a25836a575
+Subproject commit b0de1dcca26c0ebfb8011b8e59dd17fc399db0ff
diff --git a/externals/fmt b/externals/fmt
index 8ee89546f..64db979e3 160000
--- a/externals/fmt
+++ b/externals/fmt
@@ -1 +1 @@
-Subproject commit 8ee89546ffcf046309d1f0d38c0393f02fde56c8
+Subproject commit 64db979e38ec644b1798e41610b28c8d2c8a2739
diff --git a/externals/glslang b/externals/glslang
index a0995c49e..ba1640446 160000
--- a/externals/glslang
+++ b/externals/glslang
@@ -1 +1 @@
-Subproject commit a0995c49ebcaca2c6d3b03efbabf74f3843decdb
+Subproject commit ba1640446f3826a518721d1f083f3a8cca1120c3
diff --git a/externals/libusb b/externals/libusb
new file mode 160000
index 000000000..a63a7e43e
--- /dev/null
+++ b/externals/libusb
@@ -0,0 +1 @@
+Subproject commit a63a7e43e0950a595cf4b98a0eaf4051749ace5f
diff --git a/externals/magic_enum b/externals/magic_enum
index 1a1824df7..a413fcc9c 160000
--- a/externals/magic_enum
+++ b/externals/magic_enum
@@ -1 +1 @@
-Subproject commit 1a1824df7ac798177a521eed952720681b0bf482
+Subproject commit a413fcc9c46a020a746907136a384c227f3cd095
diff --git a/externals/pugixml b/externals/pugixml
index 4bc14418d..caade5a28 160000
--- a/externals/pugixml
+++ b/externals/pugixml
@@ -1 +1 @@
-Subproject commit 4bc14418d12d289dd9978fdce9490a45deeb653e
+Subproject commit caade5a28aad86b92a4b5337a9dc70c4ba73c5eb
diff --git a/externals/robin-map b/externals/robin-map
index fe845fd78..4ec1bf19c 160000
--- a/externals/robin-map
+++ b/externals/robin-map
@@ -1 +1 @@
-Subproject commit fe845fd7852ef541c5479ae23b3d36b57f8608ee
+Subproject commit 4ec1bf19c6a96125ea22062f38c2cf5b958e448e
diff --git a/externals/sdl3 b/externals/sdl3
index a336b62d8..4093e4a19 160000
--- a/externals/sdl3
+++ b/externals/sdl3
@@ -1 +1 @@
-Subproject commit a336b62d8b0b97b09214e053203e442e2b6e2be5
+Subproject commit 4093e4a193971ef1d4928158e0a1832be42e4599
diff --git a/externals/sirit b/externals/sirit
index 8b9b12c20..427a42c9e 160000
--- a/externals/sirit
+++ b/externals/sirit
@@ -1 +1 @@
-Subproject commit 8b9b12c2089505ac8b10fa56bf56b3ed49d9d7b0
+Subproject commit 427a42c9ed99b38204d9107bc3dc14e92458acf1
diff --git a/externals/toml11 b/externals/toml11
index 7f6c574ff..a01fe3b4c 160000
--- a/externals/toml11
+++ b/externals/toml11
@@ -1 +1 @@
-Subproject commit 7f6c574ff5aa1053534e7e19c0a4f22bf4c6aaca
+Subproject commit a01fe3b4c14c6d7b99ee3f07c9e80058c6403097
diff --git a/externals/vma b/externals/vma
index 5a53a1989..f378e7b3f 160000
--- a/externals/vma
+++ b/externals/vma
@@ -1 +1 @@
-Subproject commit 5a53a198945ba8260fbc58fadb788745ce6aa263
+Subproject commit f378e7b3f18f6e2b06b957f6ba7b1c7207d2a536
diff --git a/externals/vulkan-headers b/externals/vulkan-headers
index a03d2f6d5..5ceb9ed48 160000
--- a/externals/vulkan-headers
+++ b/externals/vulkan-headers
@@ -1 +1 @@
-Subproject commit a03d2f6d5753b365d704d58161825890baad0755
+Subproject commit 5ceb9ed481e58e705d0d9b5326537daedd06b97d
diff --git a/externals/winpthreads b/externals/winpthreads
index f00c973a6..f35b0948d 160000
--- a/externals/winpthreads
+++ b/externals/winpthreads
@@ -1 +1 @@
-Subproject commit f00c973a6ab2a23573708568b8ef4acc20a9d36b
+Subproject commit f35b0948d36a736e6a2d052ae295a3ffde09703f
diff --git a/externals/xbyak b/externals/xbyak
index 4e44f4614..44a72f369 160000
--- a/externals/xbyak
+++ b/externals/xbyak
@@ -1 +1 @@
-Subproject commit 4e44f4614ddbf038f2a6296f5b906d5c72691e0f
+Subproject commit 44a72f369268f7d552650891b296693e91db86bb
diff --git a/externals/xxhash b/externals/xxhash
index 2bf8313b9..953a09abc 160000
--- a/externals/xxhash
+++ b/externals/xxhash
@@ -1 +1 @@
-Subproject commit 2bf8313b934633b2a5b7e8fd239645b85e10c852
+Subproject commit 953a09abc39096da9e216b6eb0002c681cdc1199
diff --git a/externals/zlib-ng b/externals/zlib-ng
index d54e3769b..fd0d263ce 160000
--- a/externals/zlib-ng
+++ b/externals/zlib-ng
@@ -1 +1 @@
-Subproject commit d54e3769be0c522015b784eca2af258b1c026107
+Subproject commit fd0d263cedab1a136f40d65199987e3eaeecfcbd
diff --git a/externals/zydis b/externals/zydis
index bffbb610c..120e0e705 160000
--- a/externals/zydis
+++ b/externals/zydis
@@ -1 +1 @@
-Subproject commit bffbb610cfea643b98e87658b9058382f7522807
+Subproject commit 120e0e705f8e3b507dc49377ac2879979f0d545c
diff --git a/src/common/aes.h b/src/common/aes.h
new file mode 100644
index 000000000..5ca0096bd
--- /dev/null
+++ b/src/common/aes.h
@@ -0,0 +1,1195 @@
+// SPDX-FileCopyrightText: 2015 kkAyataka
+// SPDX-License-Identifier: BSL-1.0
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+/** AES cipher APIs */
+namespace aes {
+namespace detail {
+
+const int kWordSize = 4;
+typedef unsigned int Word;
+
+const int kBlockSize = 4;
+/** @private */
+struct State {
+ Word w[4];
+ Word& operator[](const int index) {
+ return w[index];
+ }
+ const Word& operator[](const int index) const {
+ return w[index];
+ }
+};
+
+const int kStateSize = 16; // Word * BlockSize
+typedef State RoundKey;
+typedef std::vector RoundKeys;
+
+inline void add_round_key(const RoundKey& key, State& state) {
+ for (int i = 0; i < kBlockSize; ++i) {
+ state[i] ^= key[i];
+ }
+}
+
+const unsigned char kSbox[] = {
+ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16};
+
+const unsigned char kInvSbox[] = {
+ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
+ 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
+ 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
+ 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
+ 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
+ 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
+ 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
+ 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
+ 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
+ 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
+ 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
+ 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
+ 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
+ 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
+ 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
+ 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d};
+
+inline Word sub_word(const Word w) {
+ return kSbox[(w >> 0) & 0xFF] << 0 | kSbox[(w >> 8) & 0xFF] << 8 |
+ kSbox[(w >> 16) & 0xFF] << 16 | kSbox[(w >> 24) & 0xFF] << 24;
+}
+
+inline Word inv_sub_word(const Word w) {
+ return kInvSbox[(w >> 0) & 0xFF] << 0 | kInvSbox[(w >> 8) & 0xFF] << 8 |
+ kInvSbox[(w >> 16) & 0xFF] << 16 | kInvSbox[(w >> 24) & 0xFF] << 24;
+}
+
+inline void sub_bytes(State& state) {
+ for (int i = 0; i < kBlockSize; ++i) {
+ state[i] = sub_word(state[i]);
+ }
+}
+
+inline void inv_sub_bytes(State& state) {
+ for (int i = 0; i < kBlockSize; ++i) {
+ state[i] = inv_sub_word(state[i]);
+ }
+}
+
+inline void shift_rows(State& state) {
+ const State ori = {state[0], state[1], state[2], state[3]};
+ for (int r = 1; r < kWordSize; ++r) {
+ const Word m2 = 0xFF << (r * 8);
+ const Word m1 = ~m2;
+ for (int c = 0; c < kBlockSize; ++c) {
+ state[c] = (state[c] & m1) | (ori[(c + r) % kBlockSize] & m2);
+ }
+ }
+}
+
+inline void inv_shift_rows(State& state) {
+ const State ori = {state[0], state[1], state[2], state[3]};
+ for (int r = 1; r < kWordSize; ++r) {
+ const Word m2 = 0xFF << (r * 8);
+ const Word m1 = ~m2;
+ for (int c = 0; c < kBlockSize; ++c) {
+ state[c] = (state[c] & m1) | (ori[(c + kBlockSize - r) % kWordSize] & m2);
+ }
+ }
+}
+
+inline unsigned char mul2(const unsigned char b) {
+ unsigned char m2 = b << 1;
+ if (b & 0x80) {
+ m2 ^= 0x011B;
+ }
+
+ return m2;
+}
+
+inline unsigned char mul(const unsigned char b, const unsigned char m) {
+ unsigned char v = 0;
+ unsigned char t = b;
+ for (int i = 0; i < 8; ++i) { // 8-bits
+ if ((m >> i) & 0x01) {
+ v ^= t;
+ }
+
+ t = mul2(t);
+ }
+
+ return v;
+}
+
+inline void mix_columns(State& state) {
+ for (int i = 0; i < kBlockSize; ++i) {
+ const unsigned char v0_1 = (state[i] >> 0) & 0xFF;
+ const unsigned char v1_1 = (state[i] >> 8) & 0xFF;
+ const unsigned char v2_1 = (state[i] >> 16) & 0xFF;
+ const unsigned char v3_1 = (state[i] >> 24) & 0xFF;
+
+ const unsigned char v0_2 = mul2(v0_1);
+ const unsigned char v1_2 = mul2(v1_1);
+ const unsigned char v2_2 = mul2(v2_1);
+ const unsigned char v3_2 = mul2(v3_1);
+
+ const unsigned char v0_3 = v0_2 ^ v0_1;
+ const unsigned char v1_3 = v1_2 ^ v1_1;
+ const unsigned char v2_3 = v2_2 ^ v2_1;
+ const unsigned char v3_3 = v3_2 ^ v3_1;
+
+ state[i] = (v0_2 ^ v1_3 ^ v2_1 ^ v3_1) << 0 | (v0_1 ^ v1_2 ^ v2_3 ^ v3_1) << 8 |
+ (v0_1 ^ v1_1 ^ v2_2 ^ v3_3) << 16 | (v0_3 ^ v1_1 ^ v2_1 ^ v3_2) << 24;
+ }
+}
+
+inline void inv_mix_columns(State& state) {
+ for (int i = 0; i < kBlockSize; ++i) {
+ const unsigned char v0 = (state[i] >> 0) & 0xFF;
+ const unsigned char v1 = (state[i] >> 8) & 0xFF;
+ const unsigned char v2 = (state[i] >> 16) & 0xFF;
+ const unsigned char v3 = (state[i] >> 24) & 0xFF;
+
+ state[i] = (mul(v0, 0x0E) ^ mul(v1, 0x0B) ^ mul(v2, 0x0D) ^ mul(v3, 0x09)) << 0 |
+ (mul(v0, 0x09) ^ mul(v1, 0x0E) ^ mul(v2, 0x0B) ^ mul(v3, 0x0D)) << 8 |
+ (mul(v0, 0x0D) ^ mul(v1, 0x09) ^ mul(v2, 0x0E) ^ mul(v3, 0x0B)) << 16 |
+ (mul(v0, 0x0B) ^ mul(v1, 0x0D) ^ mul(v2, 0x09) ^ mul(v3, 0x0E)) << 24;
+ }
+}
+
+inline Word rot_word(const Word v) {
+ return ((v >> 8) & 0x00FFFFFF) | ((v & 0xFF) << 24);
+}
+
+/**
+ * @private
+ * @throws std::invalid_argument
+ */
+inline unsigned int get_round_count(const int key_size) {
+ switch (key_size) {
+ case 16:
+ return 10;
+ case 24:
+ return 12;
+ case 32:
+ return 14;
+ default:
+ throw std::invalid_argument("Invalid key size");
+ }
+}
+
+/**
+ * @private
+ * @throws std::invalid_argument
+ */
+inline RoundKeys expand_key(const unsigned char* key, const int key_size) {
+ if (key_size != 16 && key_size != 24 && key_size != 32) {
+ throw std::invalid_argument("Invalid key size");
+ }
+
+ const Word rcon[] = {0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36};
+
+ const int nb = kBlockSize;
+ const int nk = key_size / nb;
+ const int nr = get_round_count(key_size);
+
+ std::vector w(nb * (nr + 1));
+ for (int i = 0; i < nk; ++i) {
+ memcpy(&w[i], key + (i * kWordSize), kWordSize);
+ }
+
+ for (int i = nk; i < nb * (nr + 1); ++i) {
+ Word t = w[i - 1];
+ if (i % nk == 0) {
+ t = sub_word(rot_word(t)) ^ rcon[i / nk];
+ } else if (nk > 6 && i % nk == 4) {
+ t = sub_word(t);
+ }
+
+ w[i] = t ^ w[i - nk];
+ }
+
+ RoundKeys keys(nr + 1);
+ memcpy(&keys[0], &w[0], w.size() * kWordSize);
+
+ return keys;
+}
+
+inline void copy_bytes_to_state(const unsigned char data[16], State& state) {
+ memcpy(&state[0], data + 0, kWordSize);
+ memcpy(&state[1], data + 4, kWordSize);
+ memcpy(&state[2], data + 8, kWordSize);
+ memcpy(&state[3], data + 12, kWordSize);
+}
+
+inline void copy_state_to_bytes(const State& state, unsigned char buf[16]) {
+ memcpy(buf + 0, &state[0], kWordSize);
+ memcpy(buf + 4, &state[1], kWordSize);
+ memcpy(buf + 8, &state[2], kWordSize);
+ memcpy(buf + 12, &state[3], kWordSize);
+}
+
+inline void xor_data(unsigned char data[kStateSize], const unsigned char v[kStateSize]) {
+ for (int i = 0; i < kStateSize; ++i) {
+ data[i] ^= v[i];
+ }
+}
+
+/** increment counter (128-bit int) by 1 */
+inline void incr_counter(unsigned char counter[kStateSize]) {
+ unsigned n = kStateSize, c = 1;
+ do {
+ --n;
+ c += counter[n];
+ counter[n] = c;
+ c >>= 8;
+ } while (n);
+}
+
+inline void encrypt_state(const RoundKeys& rkeys, const unsigned char data[16],
+ unsigned char encrypted[16]) {
+ State s;
+ copy_bytes_to_state(data, s);
+
+ add_round_key(rkeys[0], s);
+
+ for (unsigned int i = 1; i < rkeys.size() - 1; ++i) {
+ sub_bytes(s);
+ shift_rows(s);
+ mix_columns(s);
+ add_round_key(rkeys[i], s);
+ }
+
+ sub_bytes(s);
+ shift_rows(s);
+ add_round_key(rkeys.back(), s);
+
+ copy_state_to_bytes(s, encrypted);
+}
+
+inline void decrypt_state(const RoundKeys& rkeys, const unsigned char data[16],
+ unsigned char decrypted[16]) {
+ State s;
+ copy_bytes_to_state(data, s);
+
+ add_round_key(rkeys.back(), s);
+ inv_shift_rows(s);
+ inv_sub_bytes(s);
+
+ for (std::size_t i = rkeys.size() - 2; i > 0; --i) {
+ add_round_key(rkeys[i], s);
+ inv_mix_columns(s);
+ inv_shift_rows(s);
+ inv_sub_bytes(s);
+ }
+
+ add_round_key(rkeys[0], s);
+
+ copy_state_to_bytes(s, decrypted);
+}
+
+template
+std::vector key_from_string(const char (*key_str)[KeyLen]) {
+ std::vector key(KeyLen - 1);
+ memcpy(&key[0], *key_str, KeyLen - 1);
+ return key;
+}
+
+inline bool is_valid_key_size(const std::size_t key_size) {
+ if (key_size != 16 && key_size != 24 && key_size != 32) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+namespace gcm {
+
+const int kBlockBitSize = 128;
+const int kBlockByteSize = kBlockBitSize / 8;
+
+/**
+ * @private
+ * GCM operation unit as bit.
+ * This library handles 128 bit little endian bit array.
+ * e.g. 0^127 || 1 == "000...0001" (bit string) == 1
+ */
+typedef std::bitset bitset128;
+
+/**
+ * @private
+ * GCM operation unit.
+ * Little endian byte array
+ *
+ * If bitset128 is 1: 0^127 || 1 == "000...0001" (bit string) == 1
+ * byte array is 0x00, 0x00, 0x00 ... 0x01 (low -> high).
+ * Byte array is NOT 0x01, 0x00 ... 0x00.
+ *
+ * This library handles GCM bit string in two ways.
+ * One is an array of bitset, which is a little endian 128-bit array's array.
+ *
+ * <- first byte
+ * bitset128 || bitset128 || bitset128...
+ *
+ * The other one is a byte array.
+ * <- first byte
+ * byte || byte || byte...
+ */
+class Block {
+public:
+ Block() {
+ init_v(0, 0);
+ }
+
+ Block(const unsigned char* bytes, const unsigned long bytes_size) {
+ init_v(bytes, bytes_size);
+ }
+
+ Block(const std::vector& bytes) {
+ init_v(&bytes[0], bytes.size());
+ }
+
+ Block(const std::bitset<128>& bits); // implementation below
+
+ inline unsigned char* data() {
+ return v_;
+ }
+
+ inline const unsigned char* data() const {
+ return v_;
+ }
+
+ inline std::bitset<128> to_bits() const {
+ std::bitset<128> bits;
+ for (int i = 0; i < 16; ++i) {
+ bits <<= 8;
+ bits |= v_[i];
+ }
+
+ return bits;
+ }
+
+ inline Block operator^(const Block& b) const {
+ Block r;
+ for (int i = 0; i < 16; ++i) {
+ r.data()[i] = data()[i] ^ b.data()[i];
+ }
+ return r;
+ }
+
+private:
+ unsigned char v_[16];
+
+ inline void init_v(const unsigned char* bytes, const std::size_t bytes_size) {
+ memset(v_, 0, sizeof(v_));
+
+ const std::size_t cs = (std::min)(bytes_size, static_cast(16));
+ for (std::size_t i = 0; i < cs; ++i) {
+ v_[i] = bytes[i];
+ }
+ }
+};
+
+// Workaround for clang optimization in 32-bit build via Visual Studio producing incorrect results
+// (https://github.com/kkAyataka/plusaes/issues/43)
+#if defined(__clang__) && defined(_WIN32) && !defined(_WIN64)
+#pragma optimize("", off)
+#endif
+inline Block::Block(const std::bitset<128>& bits) {
+ init_v(0, 0);
+ const std::bitset<128> mask(0xFF); // 1 byte mask
+ for (std::size_t i = 0; i < 16; ++i) {
+ v_[15 - i] = static_cast(((bits >> (i * 8)) & mask).to_ulong());
+ }
+}
+#if defined(__clang__) && defined(_WIN32) && !defined(_WIN64)
+#pragma optimize("", on)
+#endif
+
+template
+unsigned long ceil(const T v) {
+ return static_cast(std::ceil(v) + 0.5);
+}
+
+template
+std::bitset operator||(const std::bitset& v1, const std::bitset& v2) {
+ std::bitset ret(v1.to_string() + v2.to_string());
+ return ret;
+}
+
+template
+std::bitset lsb(const std::bitset& X) {
+ std::bitset r;
+ for (std::size_t i = 0; i < S; ++i) {
+ r[i] = X[i];
+ }
+ return r;
+}
+
+template
+std::bitset msb(const std::bitset& X) {
+ std::bitset r;
+ for (std::size_t i = 0; i < S; ++i) {
+ r[S - 1 - i] = X[X.size() - 1 - i];
+ }
+ return r;
+}
+
+template
+std::bitset inc32(const std::bitset X) {
+ const std::size_t S = 32;
+
+ const auto a = msb(X);
+ const std::bitset b(
+ (lsb(X).to_ulong() + 1)); // % (2^32);
+ // lsb<32> is low 32-bit value
+ // Spec.'s "mod 2^S" is not necessary when S is 32 (inc32).
+ // ...and 2^32 is over 32-bit integer.
+
+ return a || b;
+}
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wself-assign"
+#endif // __clang__
+
+/** Algorithm 1 @private */
+inline Block mul_blocks(const Block X, const Block Y) {
+ const bitset128 R = (std::bitset<8>("11100001") || std::bitset<120>());
+
+ bitset128 X_bits = X.to_bits();
+ bitset128 Z;
+ bitset128 V = Y.to_bits();
+ for (int i = 127; i >= 0; --i) {
+ // Z
+ if (X_bits[i] == false) {
+ Z = Z;
+ } else {
+ Z = Z ^ V;
+ }
+
+ // V
+ if (V[0] == false) {
+ V = V >> 1;
+ } else {
+ V = (V >> 1) ^ R;
+ }
+ }
+
+ return Z;
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif // __clang__
+
+/** Algorithm 2 @private */
+inline Block ghash(const Block& H, const std::vector& X) {
+ const std::size_t m = X.size() / kBlockByteSize;
+ Block Ym;
+ for (std::size_t i = 0; i < m; ++i) {
+ const Block Xi(&X[i * kBlockByteSize], kBlockByteSize);
+ Ym = mul_blocks((Ym ^ Xi), H);
+ }
+
+ return Ym;
+}
+
+template
+std::bitset make_bitset(const unsigned char* bytes, const std::size_t bytes_size) {
+ std::bitset bits;
+ for (auto i = 0u; i < bytes_size; ++i) {
+ bits <<= 8;
+ bits |= bytes[i];
+ }
+ return bits;
+}
+
+/** Algorithm 3 @private */
+inline std::vector gctr(const detail::RoundKeys& rkeys, const Block& ICB,
+ const unsigned char* X, const std::size_t X_size) {
+ if (!X || X_size == 0) {
+ return std::vector();
+ } else {
+ const unsigned long n = ceil(X_size * 8.0 / kBlockBitSize);
+ std::vector Y(X_size);
+
+ Block CB;
+ for (std::size_t i = 0; i < n; ++i) {
+ // CB
+ if (i == 0) { // first
+ CB = ICB;
+ } else {
+ CB = inc32(CB.to_bits());
+ }
+
+ // CIPH
+ Block eCB;
+ encrypt_state(rkeys, CB.data(), eCB.data());
+
+ // Y
+ int op_size = 0;
+ if (i < n - 1) {
+ op_size = kBlockByteSize;
+ } else { // last
+ op_size = (X_size % kBlockByteSize) ? (X_size % kBlockByteSize) : kBlockByteSize;
+ }
+ const Block Yi = Block(X + i * kBlockBitSize / 8, op_size) ^ eCB;
+ memcpy(&Y[i * kBlockByteSize], Yi.data(), op_size);
+ }
+
+ return Y;
+ }
+}
+
+inline void push_back(std::vector& bytes, const unsigned char* data,
+ const std::size_t data_size) {
+ bytes.insert(bytes.end(), data, data + data_size);
+}
+
+inline void push_back(std::vector& bytes, const std::bitset<64>& bits) {
+ const std::bitset<64> mask(0xFF); // 1 byte mask
+ for (std::size_t i = 0; i < 8; ++i) {
+ bytes.push_back(static_cast(((bits >> ((7 - i) * 8)) & mask).to_ulong()));
+ }
+}
+
+inline void push_back_zero_bits(std::vector& bytes,
+ const std::size_t zero_bits_size) {
+ const std::vector zero_bytes(zero_bits_size / 8);
+ bytes.insert(bytes.end(), zero_bytes.begin(), zero_bytes.end());
+}
+
+inline Block calc_H(const RoundKeys& rkeys) {
+ std::vector H_raw(gcm::kBlockByteSize);
+ encrypt_state(rkeys, &H_raw[0], &H_raw[0]);
+ return gcm::Block(H_raw);
+}
+
+inline Block calc_J0(const Block& H, const unsigned char* iv, const std::size_t iv_size) {
+ if (iv_size == 12) {
+ const std::bitset<96> iv_bits = gcm::make_bitset<96>(iv, iv_size);
+ return iv_bits || std::bitset<31>() || std::bitset<1>(1);
+ } else {
+ const auto len_iv = iv_size * 8;
+ const auto s = 128 * gcm::ceil(len_iv / 128.0) - len_iv;
+ std::vector ghash_in;
+ gcm::push_back(ghash_in, iv, iv_size);
+ gcm::push_back_zero_bits(ghash_in, s + 64);
+ gcm::push_back(ghash_in, std::bitset<64>(len_iv));
+
+ return gcm::ghash(H, ghash_in);
+ }
+}
+
+inline void calc_gcm_tag(const unsigned char* data, const std::size_t data_size,
+ const unsigned char* aadata, const std::size_t aadata_size,
+ const unsigned char* key, const std::size_t key_size,
+ const unsigned char* iv, const std::size_t iv_size, unsigned char* tag,
+ const std::size_t tag_size) {
+ const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size));
+ const gcm::Block H = gcm::calc_H(rkeys);
+ const gcm::Block J0 = gcm::calc_J0(H, iv, iv_size);
+
+ const auto lenC = data_size * 8;
+ const auto lenA = aadata_size * 8;
+ const std::size_t u = 128 * gcm::ceil(lenC / 128.0) - lenC;
+ const std::size_t v = 128 * gcm::ceil(lenA / 128.0) - lenA;
+
+ std::vector ghash_in;
+ ghash_in.reserve((aadata_size + v / 8) + (data_size + u / 8) + 8 + 8);
+ gcm::push_back(ghash_in, aadata, aadata_size);
+ gcm::push_back_zero_bits(ghash_in, v);
+ gcm::push_back(ghash_in, data, data_size);
+ gcm::push_back_zero_bits(ghash_in, u);
+ gcm::push_back(ghash_in, std::bitset<64>(lenA));
+ gcm::push_back(ghash_in, std::bitset<64>(lenC));
+ const gcm::Block S = gcm::ghash(H, ghash_in);
+ const std::vector T = gcm::gctr(rkeys, J0, S.data(), gcm::kBlockByteSize);
+
+ // return
+ memcpy(tag, &T[0], (std::min)(tag_size, static_cast(16)));
+}
+
+/** Algorithm 4 and 5 @private */
+inline void crypt_gcm(const unsigned char* data, const std::size_t data_size,
+ const unsigned char* key, const std::size_t key_size, const unsigned char* iv,
+ const std::size_t iv_size, unsigned char* crypted) {
+ const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size));
+ const gcm::Block H = gcm::calc_H(rkeys);
+ const gcm::Block J0 = gcm::calc_J0(H, iv, iv_size);
+
+ const std::vector C =
+ gcm::gctr(rkeys, gcm::inc32(J0.to_bits()), data, data_size);
+
+ if (crypted) {
+ memcpy(crypted, &C[0], data_size);
+ }
+}
+
+} // namespace gcm
+
+} // namespace detail
+
+/** @defgroup Base Base
+ * Base definitions and convenient functions
+ * @{ */
+
+/** Create 128-bit key from string. */
+inline std::vector key_from_string(const char (*key_str)[17]) {
+ return detail::key_from_string<17>(key_str);
+}
+
+/** Create 192-bit key from string. */
+inline std::vector key_from_string(const char (*key_str)[25]) {
+ return detail::key_from_string<25>(key_str);
+}
+
+/** Create 256-bit key from string. */
+inline std::vector key_from_string(const char (*key_str)[33]) {
+ return detail::key_from_string<33>(key_str);
+}
+
+/** Calculates encrypted data size when padding is enabled. */
+inline unsigned long get_padded_encrypted_size(const unsigned long data_size) {
+ return data_size + detail::kStateSize - (data_size % detail::kStateSize);
+}
+
+/** Error code */
+typedef enum {
+ kErrorOk = 0,
+ kErrorInvalidDataSize = 1,
+ kErrorInvalidKeySize,
+ kErrorInvalidBufferSize,
+ kErrorInvalidKey,
+ kErrorDeprecated, // kErrorInvalidNonceSize
+ kErrorInvalidIvSize,
+ kErrorInvalidTagSize,
+ kErrorInvalidTag
+} Error;
+
+/** @} */
+
+namespace detail {
+
+inline Error check_encrypt_cond(const unsigned long data_size, const unsigned long key_size,
+ const unsigned long encrypted_size, const bool pads) {
+ // check data size
+ if (!pads && (data_size % kStateSize != 0)) {
+ return kErrorInvalidDataSize;
+ }
+
+ // check key size
+ if (!detail::is_valid_key_size(key_size)) {
+ return kErrorInvalidKeySize;
+ }
+
+ // check encrypted buffer size
+ if (pads) {
+ const unsigned long required_size = get_padded_encrypted_size(data_size);
+ if (encrypted_size < required_size) {
+ return kErrorInvalidBufferSize;
+ }
+ } else {
+ if (encrypted_size < data_size) {
+ return kErrorInvalidBufferSize;
+ }
+ }
+ return kErrorOk;
+}
+
+inline Error check_decrypt_cond(const unsigned long data_size, const unsigned long key_size,
+ const unsigned long decrypted_size,
+ const unsigned long* padded_size) {
+ // check data size
+ if (data_size % 16 != 0) {
+ return kErrorInvalidDataSize;
+ }
+
+ // check key size
+ if (!detail::is_valid_key_size(key_size)) {
+ return kErrorInvalidKeySize;
+ }
+
+ // check decrypted buffer size
+ if (!padded_size) {
+ if (decrypted_size < data_size) {
+ return kErrorInvalidBufferSize;
+ }
+ } else {
+ if (decrypted_size < (data_size - kStateSize)) {
+ return kErrorInvalidBufferSize;
+ }
+ }
+
+ return kErrorOk;
+}
+
+inline bool check_padding(const unsigned long padding, const unsigned char data[kStateSize]) {
+ if (padding > kStateSize) {
+ return false;
+ }
+
+ for (unsigned long i = 0; i < padding; ++i) {
+ if (data[kStateSize - 1 - i] != padding) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+inline Error check_gcm_cond(const std::size_t key_size, const std::size_t iv_size,
+ const std::size_t tag_size) {
+ // check key size
+ if (!detail::is_valid_key_size(key_size)) {
+ return kErrorInvalidKeySize;
+ }
+
+ if (iv_size < 1) {
+ return kErrorInvalidIvSize;
+ }
+
+ // check tag size
+ if ((tag_size < 12 || 16 < tag_size) && (tag_size != 8) && (tag_size != 4)) {
+ return kErrorInvalidTagSize;
+ }
+
+ return kErrorOk;
+}
+
+} // namespace detail
+
+/** @defgroup ECB ECB
+ * ECB mode functions
+ * @{ */
+
+/**
+ * Encrypts data with ECB mode.
+ * @param [in] data Data.
+ * @param [in] data_size Data size.
+ * If the pads is false, data size must be multiple of 16.
+ * @param [in] key key bytes. The key length must be 16 (128-bit), 24 (192-bit) or 32 (256-bit).
+ * @param [in] key_size key size.
+ * @param [out] encrypted Encrypted data buffer.
+ * @param [in] encrypted_size Encrypted data buffer size.
+ * @param [in] pads If this value is true, encrypted data is padded by PKCS.
+ * Encrypted data size must be multiple of 16.
+ * If the pads is true, encrypted data is padded with PKCS.
+ * So the data is multiple of 16, encrypted data size needs additonal 16 bytes.
+ * @since 1.0.0
+ */
+inline Error encrypt_ecb(const unsigned char* data, const unsigned long data_size,
+ const unsigned char* key, const unsigned long key_size,
+ unsigned char* encrypted, const unsigned long encrypted_size,
+ const bool pads) {
+ const Error e = detail::check_encrypt_cond(data_size, key_size, encrypted_size, pads);
+ if (e != kErrorOk) {
+ return e;
+ }
+
+ const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size));
+
+ const unsigned long bc = data_size / detail::kStateSize;
+ for (unsigned long i = 0; i < bc; ++i) {
+ detail::encrypt_state(rkeys, data + (i * detail::kStateSize),
+ encrypted + (i * detail::kStateSize));
+ }
+
+ if (pads) {
+ const int rem = data_size % detail::kStateSize;
+ const char pad_v = detail::kStateSize - rem;
+
+ std::vector ib(detail::kStateSize, pad_v), ob(detail::kStateSize);
+ memcpy(&ib[0], data + data_size - rem, rem);
+
+ detail::encrypt_state(rkeys, &ib[0], &ob[0]);
+ memcpy(encrypted + (data_size - rem), &ob[0], detail::kStateSize);
+ }
+
+ return kErrorOk;
+}
+
+/**
+ * Decrypts data with ECB mode.
+ * @param [in] data Data bytes.
+ * @param [in] data_size Data size.
+ * @param [in] key Key bytes.
+ * @param [in] key_size Key size.
+ * @param [out] decrypted Decrypted data buffer.
+ * @param [in] decrypted_size Decrypted data buffer size.
+ * @param [out] padded_size If this value is NULL, this function does not remove padding.
+ * If this value is not NULL, this function removes padding by PKCS
+ * and returns padded size using padded_size.
+ * @since 1.0.0
+ */
+inline Error decrypt_ecb(const unsigned char* data, const unsigned long data_size,
+ const unsigned char* key, const unsigned long key_size,
+ unsigned char* decrypted, const unsigned long decrypted_size,
+ unsigned long* padded_size) {
+ const Error e = detail::check_decrypt_cond(data_size, key_size, decrypted_size, padded_size);
+ if (e != kErrorOk) {
+ return e;
+ }
+
+ const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size));
+
+ const unsigned long bc = data_size / detail::kStateSize - 1;
+ for (unsigned long i = 0; i < bc; ++i) {
+ detail::decrypt_state(rkeys, data + (i * detail::kStateSize),
+ decrypted + (i * detail::kStateSize));
+ }
+
+ unsigned char last[detail::kStateSize] = {};
+ detail::decrypt_state(rkeys, data + (bc * detail::kStateSize), last);
+
+ if (padded_size) {
+ *padded_size = last[detail::kStateSize - 1];
+ const unsigned long cs = detail::kStateSize - *padded_size;
+
+ if (!detail::check_padding(*padded_size, last)) {
+ return kErrorInvalidKey;
+ } else if (decrypted_size >= (bc * detail::kStateSize) + cs) {
+ memcpy(decrypted + (bc * detail::kStateSize), last, cs);
+ } else {
+ return kErrorInvalidBufferSize;
+ }
+ } else {
+ memcpy(decrypted + (bc * detail::kStateSize), last, sizeof(last));
+ }
+
+ return kErrorOk;
+}
+
+/** @} */
+
+/** @defgroup CBC CBC
+ * CBC mode functions
+ * @{ */
+
+/**
+ * Encrypt data with CBC mode.
+ * @param [in] data Data.
+ * @param [in] data_size Data size.
+ * If the pads is false, data size must be multiple of 16.
+ * @param [in] key key bytes. The key length must be 16 (128-bit), 24 (192-bit) or 32 (256-bit).
+ * @param [in] key_size key size.
+ * @param [in] iv Initialize vector.
+ * @param [out] encrypted Encrypted data buffer.
+ * @param [in] encrypted_size Encrypted data buffer size.
+ * @param [in] pads If this value is true, encrypted data is padded by PKCS.
+ * Encrypted data size must be multiple of 16.
+ * If the pads is true, encrypted data is padded with PKCS.
+ * So the data is multiple of 16, encrypted data size needs additonal 16 bytes.
+ * @since 1.0.0
+ */
+inline Error encrypt_cbc(const unsigned char* data, const unsigned long data_size,
+ const unsigned char* key, const unsigned long key_size,
+ const unsigned char iv[16], unsigned char* encrypted,
+ const unsigned long encrypted_size, const bool pads) {
+ const Error e = detail::check_encrypt_cond(data_size, key_size, encrypted_size, pads);
+ if (e != kErrorOk) {
+ return e;
+ }
+
+ const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size));
+
+ unsigned char s[detail::kStateSize] = {}; // encrypting data
+
+ // calculate padding value
+ const bool ge16 = (data_size >= detail::kStateSize);
+ const int rem = data_size % detail::kStateSize;
+ const unsigned char pad_v = detail::kStateSize - rem;
+
+ // encrypt 1st state
+ if (ge16) {
+ memcpy(s, data, detail::kStateSize);
+ } else {
+ memset(s, pad_v, detail::kStateSize);
+ memcpy(s, data, data_size);
+ }
+ if (iv) {
+ detail::xor_data(s, iv);
+ }
+ detail::encrypt_state(rkeys, s, encrypted);
+
+ // encrypt mid
+ const unsigned long bc = data_size / detail::kStateSize;
+ for (unsigned long i = 1; i < bc; ++i) {
+ const long offset = i * detail::kStateSize;
+ memcpy(s, data + offset, detail::kStateSize);
+ detail::xor_data(s, encrypted + offset - detail::kStateSize);
+
+ detail::encrypt_state(rkeys, s, encrypted + offset);
+ }
+
+ // enctypt last
+ if (pads && ge16) {
+ std::vector ib(detail::kStateSize, pad_v), ob(detail::kStateSize);
+ memcpy(&ib[0], data + data_size - rem, rem);
+
+ detail::xor_data(&ib[0], encrypted + (bc - 1) * detail::kStateSize);
+
+ detail::encrypt_state(rkeys, &ib[0], &ob[0]);
+ memcpy(encrypted + (data_size - rem), &ob[0], detail::kStateSize);
+ }
+
+ return kErrorOk;
+}
+
+/**
+ * Decrypt data with CBC mode.
+ * @param [in] data Data bytes.
+ * @param [in] data_size Data size.
+ * @param [in] key Key bytes.
+ * @param [in] key_size Key size.
+ * @param [in] iv Initialize vector.
+ * @param [out] decrypted Decrypted data buffer.
+ * @param [in] decrypted_size Decrypted data buffer size.
+ * @param [out] padded_size If this value is NULL, this function does not remove padding.
+ * If this value is not NULL, this function removes padding by PKCS
+ * and returns padded size using padded_size.
+ * @since 1.0.0
+ */
+inline Error decrypt_cbc(const unsigned char* data, const unsigned long data_size,
+ const unsigned char* key, const unsigned long key_size,
+ const unsigned char iv[16], unsigned char* decrypted,
+ const unsigned long decrypted_size, unsigned long* padded_size) {
+ const Error e = detail::check_decrypt_cond(data_size, key_size, decrypted_size, padded_size);
+ if (e != kErrorOk) {
+ return e;
+ }
+
+ const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size));
+
+ // decrypt 1st state
+ detail::decrypt_state(rkeys, data, decrypted);
+ if (iv) {
+ detail::xor_data(decrypted, iv);
+ }
+
+ // decrypt mid
+ const unsigned long bc = data_size / detail::kStateSize - 1;
+ for (unsigned long i = 1; i < bc; ++i) {
+ const long offset = i * detail::kStateSize;
+ detail::decrypt_state(rkeys, data + offset, decrypted + offset);
+ detail::xor_data(decrypted + offset, data + offset - detail::kStateSize);
+ }
+
+ // decrypt last
+ unsigned char last[detail::kStateSize] = {};
+ if (data_size > detail::kStateSize) {
+ detail::decrypt_state(rkeys, data + (bc * detail::kStateSize), last);
+ detail::xor_data(last, data + (bc * detail::kStateSize - detail::kStateSize));
+ } else {
+ memcpy(last, decrypted, data_size);
+ memset(decrypted, 0, decrypted_size);
+ }
+
+ if (padded_size) {
+ *padded_size = last[detail::kStateSize - 1];
+ const unsigned long cs = detail::kStateSize - *padded_size;
+
+ if (!detail::check_padding(*padded_size, last)) {
+ return kErrorInvalidKey;
+ } else if (decrypted_size >= (bc * detail::kStateSize) + cs) {
+ memcpy(decrypted + (bc * detail::kStateSize), last, cs);
+ } else {
+ return kErrorInvalidBufferSize;
+ }
+ } else {
+ memcpy(decrypted + (bc * detail::kStateSize), last, sizeof(last));
+ }
+
+ return kErrorOk;
+}
+
+/** @} */
+
+/** @defgroup GCM GCM
+ * GCM mode functions
+ * @{ */
+
+/**
+ * Encrypts data with GCM mode and gets an authentication tag.
+ *
+ * You can specify iv size and tag size.
+ * But usually you should use the other overloaded function whose iv and tag size is fixed.
+ *
+ * @returns kErrorOk
+ * @returns kErrorInvalidKeySize
+ * @returns kErrorInvalidIvSize
+ * @returns kErrorInvalidTagSize
+ */
+inline Error encrypt_gcm(unsigned char* data, const std::size_t data_size,
+ const unsigned char* aadata, const std::size_t aadata_size,
+ const unsigned char* key, const std::size_t key_size,
+ const unsigned char* iv, const std::size_t iv_size, unsigned char* tag,
+ const std::size_t tag_size) {
+ const Error err = detail::check_gcm_cond(key_size, iv_size, tag_size);
+ if (err != kErrorOk) {
+ return err;
+ }
+
+ detail::gcm::crypt_gcm(data, data_size, key, key_size, iv, iv_size, data);
+ detail::gcm::calc_gcm_tag(data, data_size, aadata, aadata_size, key, key_size, iv, iv_size, tag,
+ tag_size);
+
+ return kErrorOk;
+}
+
+/**
+ * Encrypts data with GCM mode and gets an authentication tag.
+ *
+ * @param [in,out] data Input data and output buffer.
+ * This buffer is replaced with encrypted data.
+ * @param [in] data_size data size
+ * @param [in] aadata Additional Authenticated data
+ * @param [in] aadata_size aadata size
+ * @param [in] key Cipher key
+ * @param [in] key_size Cipher key size. This value must be 16 (128-bit), 24 (192-bit), or 32
+ * (256-bit).
+ * @param [in] iv Initialization vector
+ * @param [out] tag Calculated authentication tag data
+ *
+ * @returns kErrorOk
+ * @returns kErrorInvalidKeySize
+ */
+inline Error encrypt_gcm(unsigned char* data, const std::size_t data_size,
+ const unsigned char* aadata, const std::size_t aadata_size,
+ const unsigned char* key, const std::size_t key_size,
+ const unsigned char (*iv)[12], unsigned char (*tag)[16]) {
+ return encrypt_gcm(data, data_size, aadata, aadata_size, key, key_size, *iv, 12, *tag, 16);
+}
+
+/**
+ * Decrypts data with GCM mode and checks an authentication tag.
+ *
+ * You can specify iv size and tag size.
+ * But usually you should use the other overloaded function whose iv and tag size is fixed.
+ *
+ * @returns kErrorOk
+ * @returns kErrorInvalidKeySize
+ * @returns kErrorInvalidIvSize
+ * @returns kErrorInvalidTagSize
+ * @returns kErrorInvalidTag
+ */
+inline Error decrypt_gcm(unsigned char* data, const std::size_t data_size,
+ const unsigned char* aadata, const std::size_t aadata_size,
+ const unsigned char* key, const std::size_t key_size,
+ const unsigned char* iv, const std::size_t iv_size,
+ const unsigned char* tag, const std::size_t tag_size) {
+ const Error err = detail::check_gcm_cond(key_size, iv_size, tag_size);
+ if (err != kErrorOk) {
+ return err;
+ }
+
+ unsigned char* C = data;
+ const auto C_size = data_size;
+ unsigned char tagd[16] = {};
+ detail::gcm::calc_gcm_tag(C, C_size, aadata, aadata_size, key, key_size, iv, iv_size, tagd, 16);
+
+ if (memcmp(tag, tagd, tag_size) != 0) {
+ return kErrorInvalidTag;
+ } else {
+ detail::gcm::crypt_gcm(C, C_size, key, key_size, iv, iv_size, C);
+
+ return kErrorOk;
+ }
+}
+
+/**
+ * Decrypts data with GCM mode and checks an authentication tag.
+ *
+ * @param [in,out] data Input data and output buffer.
+ * This buffer is replaced with decrypted data.
+ * @param [in] data_size data size
+ * @param [in] aadata Additional Authenticated data
+ * @param [in] aadata_size aadata size
+ * @param [in] key Cipher key
+ * @param [in] key_size Cipher key size. This value must be 16 (128-bit), 24 (192-bit), or 32
+ * (256-bit).
+ * @param [in] iv Initialization vector
+ * @param [in] tag Authentication tag data
+ *
+ * @returns kErrorOk
+ * @returns kErrorInvalidKeySize
+ * @returns kErrorInvalidTag
+ */
+inline Error decrypt_gcm(unsigned char* data, const std::size_t data_size,
+ const unsigned char* aadata, const std::size_t aadata_size,
+ const unsigned char* key, const std::size_t key_size,
+ const unsigned char (*iv)[12], const unsigned char (*tag)[16]) {
+ return decrypt_gcm(data, data_size, aadata, aadata_size, key, key_size, *iv, 12, *tag, 16);
+}
+
+/** @} */
+
+/** @defgroup CTR CTR
+ * CTR mode function
+ * @{ */
+
+/**
+ * Encrypts or decrypt data in-place with CTR mode.
+ *
+ * @param [in,out] data Input data and output buffer.
+ * This buffer is replaced with encrypted / decrypted data.
+ * @param [in] data_size Data size.
+ * @param [in] key Cipher key
+ * @param [in] key_size Cipher key size. This value must be 16 (128-bit), 24 (192-bit), or 32
+ * (256-bit).
+ * @param [in] nonce Nonce of the counter initialization.
+ *
+ * @returns kErrorOk
+ * @returns kErrorInvalidKeySize
+ * @since 1.0.0
+ */
+inline Error crypt_ctr(unsigned char* data, const std::size_t data_size, const unsigned char* key,
+ const std::size_t key_size, const unsigned char (*nonce)[16]) {
+ if (!detail::is_valid_key_size(key_size))
+ return kErrorInvalidKeySize;
+ const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size));
+
+ unsigned long pos = 0;
+ unsigned long blkpos = detail::kStateSize;
+ unsigned char blk[detail::kStateSize] = {};
+ unsigned char counter[detail::kStateSize] = {};
+ memcpy(counter, nonce, 16);
+
+ while (pos < data_size) {
+ if (blkpos == detail::kStateSize) {
+ detail::encrypt_state(rkeys, counter, blk);
+ detail::incr_counter(counter);
+ blkpos = 0;
+ }
+ data[pos++] ^= blk[blkpos++];
+ }
+
+ return kErrorOk;
+}
+
+/** @} */
+
+} // namespace aes
diff --git a/src/common/config.cpp b/src/common/config.cpp
index 36566a14c..111c0cfa9 100644
--- a/src/common/config.cpp
+++ b/src/common/config.cpp
@@ -7,10 +7,10 @@
#include // for wstring support
#include
+#include "common/config.h"
+#include "common/logging/formatter.h"
#include "common/path_util.h"
-#include "config.h"
-#include "logging/formatter.h"
-#include "version.h"
+#include "common/scm_rev.h"
namespace toml {
template
@@ -32,6 +32,7 @@ std::filesystem::path find_fs_path_or(const basic_value& v, const K& ky,
namespace Config {
static bool isNeo = false;
+static bool isDevKit = false;
static bool playBGM = false;
static bool isTrophyPopupDisabled = false;
static int BGMvolume = 50;
@@ -40,7 +41,7 @@ static u32 screenWidth = 1280;
static u32 screenHeight = 720;
static s32 gpuId = -1; // Vulkan physical device index. Set to negative for auto select
static std::string logFilter;
-static std::string logType = "async";
+static std::string logType = "sync";
static std::string userName = "shadPS4";
static std::string updateChannel;
static std::string chooseHomeTab;
@@ -53,7 +54,7 @@ static bool isShaderDebug = false;
static bool isShowSplash = false;
static bool isAutoUpdate = false;
static bool isAlwaysShowChangelog = false;
-static bool isLeftSideTrophy = false;
+static std::string isSideTrophy = "right";
static bool isNullGpu = false;
static bool shouldCopyGPUBuffers = false;
static bool shouldDumpShaders = false;
@@ -74,14 +75,14 @@ static double trophyNotificationDuration = 6.0;
static bool useUnifiedInputConfig = true;
static bool overrideControllerColor = false;
static int controllerCustomColorRGB[3] = {0, 0, 255};
-static bool separateupdatefolder = false;
static bool compatibilityData = false;
static bool checkCompatibilityOnStartup = false;
static std::string trophyKey;
// Gui
static bool load_game_size = true;
-std::vector settings_install_dirs = {};
+static std::vector settings_install_dirs = {};
+std::vector install_dirs_enabled = {};
std::filesystem::path settings_addon_install_dir = {};
std::filesystem::path save_data_path = {};
u32 main_window_geometry_x = 400;
@@ -96,7 +97,6 @@ u32 m_slider_pos_grid = 0;
u32 m_table_mode = 0;
u32 m_window_size_W = 1280;
u32 m_window_size_H = 720;
-std::vector m_pkg_viewer;
std::vector m_elf_viewer;
std::vector m_recent_files;
std::string emulator_language = "en_US";
@@ -105,6 +105,7 @@ static bool showBackgroundImage = true;
static bool isFullscreen = false;
static std::string fullscreenMode = "Windowed";
static bool isHDRAllowed = false;
+static bool showLabelsUnderIcons = true;
// Language
u32 m_language = 1; // english
@@ -166,10 +167,22 @@ bool isNeoModeConsole() {
return isNeo;
}
+bool isDevKitConsole() {
+ return isDevKit;
+}
+
bool getIsFullscreen() {
return isFullscreen;
}
+bool getShowLabelsUnderIcons() {
+ return showLabelsUnderIcons;
+}
+
+bool setShowLabelsUnderIcons() {
+ return false;
+}
+
std::string getFullscreenMode() {
return fullscreenMode;
}
@@ -270,8 +283,8 @@ bool alwaysShowChangelog() {
return isAlwaysShowChangelog;
}
-bool leftSideTrophy() {
- return isLeftSideTrophy;
+std::string sideTrophy() {
+ return isSideTrophy;
}
bool nullGpu() {
@@ -338,10 +351,6 @@ void setVkGuestMarkersEnabled(bool enable) {
vkGuestMarkers = enable;
}
-bool getSeparateUpdateEnabled() {
- return separateupdatefolder;
-}
-
bool getCompatibilityEnabled() {
return compatibilityData;
}
@@ -381,8 +390,9 @@ void setAutoUpdate(bool enable) {
void setAlwaysShowChangelog(bool enable) {
isAlwaysShowChangelog = enable;
}
-void setLeftSideTrophy(bool enable) {
- isLeftSideTrophy = enable;
+
+void setSideTrophy(std::string side) {
+ isSideTrophy = side;
}
void setNullGpu(bool enable) {
@@ -420,6 +430,9 @@ void setVblankDiv(u32 value) {
void setIsFullscreen(bool enable) {
isFullscreen = enable;
}
+static void setShowLabelsUnderIcons(bool enable) {
+ showLabelsUnderIcons = enable;
+}
void setFullscreenMode(std::string mode) {
fullscreenMode = mode;
@@ -499,10 +512,6 @@ void setIsMotionControlsEnabled(bool use) {
isMotionControlsEnabled = use;
}
-void setSeparateUpdateEnabled(bool use) {
- separateupdatefolder = use;
-}
-
void setCompatibilityEnabled(bool use) {
compatibilityData = use;
}
@@ -518,22 +527,34 @@ void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) {
main_window_geometry_h = h;
}
-bool addGameInstallDir(const std::filesystem::path& dir) {
- if (std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir) ==
- settings_install_dirs.end()) {
- settings_install_dirs.push_back(dir);
- return true;
+bool addGameInstallDir(const std::filesystem::path& dir, bool enabled) {
+ for (const auto& install_dir : settings_install_dirs) {
+ if (install_dir.path == dir) {
+ return false;
+ }
}
- return false;
+ settings_install_dirs.push_back({dir, enabled});
+ return true;
}
void removeGameInstallDir(const std::filesystem::path& dir) {
- auto iterator = std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir);
+ auto iterator =
+ std::find_if(settings_install_dirs.begin(), settings_install_dirs.end(),
+ [&dir](const GameInstallDir& install_dir) { return install_dir.path == dir; });
if (iterator != settings_install_dirs.end()) {
settings_install_dirs.erase(iterator);
}
}
+void setGameInstallDirEnabled(const std::filesystem::path& dir, bool enabled) {
+ auto iterator =
+ std::find_if(settings_install_dirs.begin(), settings_install_dirs.end(),
+ [&dir](const GameInstallDir& install_dir) { return install_dir.path == dir; });
+ if (iterator != settings_install_dirs.end()) {
+ iterator->enabled = enabled;
+ }
+}
+
void setAddonInstallDir(const std::filesystem::path& dir) {
settings_addon_install_dir = dir;
}
@@ -570,11 +591,6 @@ void setMainWindowHeight(u32 height) {
m_window_size_H = height;
}
-void setPkgViewer(const std::vector& pkgList) {
- m_pkg_viewer.resize(pkgList.size());
- m_pkg_viewer = pkgList;
-}
-
void setElfViewer(const std::vector& elfList) {
m_elf_viewer.resize(elfList.size());
m_elf_viewer = elfList;
@@ -589,8 +605,15 @@ void setEmulatorLanguage(std::string language) {
emulator_language = language;
}
-void setGameInstallDirs(const std::vector& settings_install_dirs_config) {
- settings_install_dirs = settings_install_dirs_config;
+void setGameInstallDirs(const std::vector& dirs_config) {
+ settings_install_dirs.clear();
+ for (const auto& dir : dirs_config) {
+ settings_install_dirs.push_back({dir, true});
+ }
+}
+
+void setAllGameInstallDirs(const std::vector& dirs_config) {
+ settings_install_dirs = dirs_config;
}
void setSaveDataPath(const std::filesystem::path& path) {
@@ -613,8 +636,22 @@ u32 getMainWindowGeometryH() {
return main_window_geometry_h;
}
-const std::vector& getGameInstallDirs() {
- return settings_install_dirs;
+const std::vector getGameInstallDirs() {
+ std::vector enabled_dirs;
+ for (const auto& dir : settings_install_dirs) {
+ if (dir.enabled) {
+ enabled_dirs.push_back(dir.path);
+ }
+ }
+ return enabled_dirs;
+}
+
+const std::vector getGameInstallDirsEnabled() {
+ std::vector enabled_dirs;
+ for (const auto& dir : settings_install_dirs) {
+ enabled_dirs.push_back(dir.enabled);
+ }
+ return enabled_dirs;
}
std::filesystem::path getAddonInstallDir() {
@@ -657,10 +694,6 @@ u32 getMainWindowHeight() {
return m_window_size_H;
}
-std::vector getPkgViewer() {
- return m_pkg_viewer;
-}
-
std::vector getElfViewer() {
return m_elf_viewer;
}
@@ -720,6 +753,7 @@ void load(const std::filesystem::path& path) {
const toml::value& general = data.at("General");
isNeo = toml::find_or(general, "isPS4Pro", false);
+ isDevKit = toml::find_or(general, "isDevKit", false);
playBGM = toml::find_or(general, "playBGM", false);
isTrophyPopupDisabled = toml::find_or(general, "isTrophyPopupDisabled", false);
trophyNotificationDuration =
@@ -729,7 +763,7 @@ void load(const std::filesystem::path& path) {
logFilter = toml::find_or(general, "logFilter", "");
logType = toml::find_or(general, "logType", "sync");
userName = toml::find_or(general, "userName", "shadPS4");
- if (Common::isRelease) {
+ if (Common::g_is_release) {
updateChannel = toml::find_or(general, "updateChannel", "Release");
} else {
updateChannel = toml::find_or(general, "updateChannel", "Nightly");
@@ -737,8 +771,7 @@ void load(const std::filesystem::path& path) {
isShowSplash = toml::find_or(general, "showSplash", true);
isAutoUpdate = toml::find_or(general, "autoUpdate", false);
isAlwaysShowChangelog = toml::find_or(general, "alwaysShowChangelog", false);
- isLeftSideTrophy = toml::find_or(general, "leftSideTrophy", false);
- separateupdatefolder = toml::find_or(general, "separateUpdateEnabled", false);
+ isSideTrophy = toml::find_or(general, "sideTrophy", "right");
compatibilityData = toml::find_or(general, "compatibilityEnabled", false);
checkCompatibilityOnStartup =
toml::find_or(general, "checkCompatibilityOnStartup", false);
@@ -807,9 +840,23 @@ void load(const std::filesystem::path& path) {
m_window_size_H = toml::find_or(gui, "mw_height", 0);
const auto install_dir_array =
- toml::find_or>(gui, "installDirs", {});
- for (const auto& dir : install_dir_array) {
- addGameInstallDir(std::filesystem::path{dir});
+ toml::find_or>(gui, "installDirs", {});
+
+ try {
+ install_dirs_enabled = toml::find>(gui, "installDirsEnabled");
+ } catch (...) {
+ // If it does not exist, assume that all are enabled.
+ install_dirs_enabled.resize(install_dir_array.size(), true);
+ }
+
+ if (install_dirs_enabled.size() < install_dir_array.size()) {
+ install_dirs_enabled.resize(install_dir_array.size(), true);
+ }
+
+ settings_install_dirs.clear();
+ for (size_t i = 0; i < install_dir_array.size(); i++) {
+ settings_install_dirs.push_back(
+ {std::filesystem::path{install_dir_array[i]}, install_dirs_enabled[i]});
}
save_data_path = toml::find_fs_path_or(gui, "saveDataPath", {});
@@ -819,7 +866,6 @@ void load(const std::filesystem::path& path) {
main_window_geometry_y = toml::find_or(gui, "geometry_y", 0);
main_window_geometry_w = toml::find_or(gui, "geometry_w", 0);
main_window_geometry_h = toml::find_or(gui, "geometry_h", 0);
- m_pkg_viewer = toml::find_or>(gui, "pkgDirs", {});
m_elf_viewer = toml::find_or>(gui, "elfDirs", {});
m_recent_files = toml::find_or>(gui, "recentFiles", {});
m_table_mode = toml::find_or(gui, "gameTableMode", 0);
@@ -852,6 +898,37 @@ void load(const std::filesystem::path& path) {
}
}
+void sortTomlSections(toml::ordered_value& data) {
+ toml::ordered_value ordered_data;
+ std::vector section_order = {"General", "Input", "GPU", "Vulkan",
+ "Debug", "Keys", "GUI", "Settings"};
+
+ for (const auto& section : section_order) {
+ if (data.contains(section)) {
+ std::vector keys;
+ for (const auto& item : data.at(section).as_table()) {
+ keys.push_back(item.first);
+ }
+
+ std::sort(keys.begin(), keys.end(), [](const std::string& a, const std::string& b) {
+ return std::lexicographical_compare(
+ a.begin(), a.end(), b.begin(), b.end(), [](char a_char, char b_char) {
+ return std::tolower(a_char) < std::tolower(b_char);
+ });
+ });
+
+ toml::ordered_value ordered_section;
+ for (const auto& key : keys) {
+ ordered_section[key] = data.at(section).at(key);
+ }
+
+ ordered_data[section] = ordered_section;
+ }
+ }
+
+ data = ordered_data;
+}
+
void save(const std::filesystem::path& path) {
toml::ordered_value data;
@@ -875,6 +952,7 @@ void save(const std::filesystem::path& path) {
}
data["General"]["isPS4Pro"] = isNeo;
+ data["General"]["isDevKit"] = isDevKit;
data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled;
data["General"]["trophyNotificationDuration"] = trophyNotificationDuration;
data["General"]["playBGM"] = playBGM;
@@ -888,8 +966,7 @@ void save(const std::filesystem::path& path) {
data["General"]["showSplash"] = isShowSplash;
data["General"]["autoUpdate"] = isAutoUpdate;
data["General"]["alwaysShowChangelog"] = isAlwaysShowChangelog;
- data["General"]["leftSideTrophy"] = isLeftSideTrophy;
- data["General"]["separateUpdateEnabled"] = separateupdatefolder;
+ data["General"]["sideTrophy"] = isSideTrophy;
data["General"]["compatibilityEnabled"] = compatibilityData;
data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup;
data["Input"]["cursorState"] = cursorState;
@@ -921,14 +998,37 @@ void save(const std::filesystem::path& path) {
data["Debug"]["CollectShader"] = isShaderDebug;
data["Debug"]["isSeparateLogFilesEnabled"] = isSeparateLogFilesEnabled;
data["Debug"]["FPSColor"] = isFpsColor;
-
data["Keys"]["TrophyKey"] = trophyKey;
std::vector install_dirs;
- for (const auto& dirString : settings_install_dirs) {
- install_dirs.emplace_back(std::string{fmt::UTF(dirString.u8string()).data});
+ std::vector install_dirs_enabled;
+
+ // temporary structure for ordering
+ struct DirEntry {
+ std::string path_str;
+ bool enabled;
+ };
+
+ std::vector sorted_dirs;
+ for (const auto& dirInfo : settings_install_dirs) {
+ sorted_dirs.push_back(
+ {std::string{fmt::UTF(dirInfo.path.u8string()).data}, dirInfo.enabled});
}
+
+ // Sort directories alphabetically
+ std::sort(sorted_dirs.begin(), sorted_dirs.end(), [](const DirEntry& a, const DirEntry& b) {
+ return std::lexicographical_compare(
+ a.path_str.begin(), a.path_str.end(), b.path_str.begin(), b.path_str.end(),
+ [](char a_char, char b_char) { return std::tolower(a_char) < std::tolower(b_char); });
+ });
+
+ for (const auto& entry : sorted_dirs) {
+ install_dirs.push_back(entry.path_str);
+ install_dirs_enabled.push_back(entry.enabled);
+ }
+
data["GUI"]["installDirs"] = install_dirs;
+ data["GUI"]["installDirsEnabled"] = install_dirs_enabled;
data["GUI"]["saveDataPath"] = std::string{fmt::UTF(save_data_path.u8string()).data};
data["GUI"]["loadGameSizeEnabled"] = load_game_size;
@@ -939,9 +1039,13 @@ void save(const std::filesystem::path& path) {
data["GUI"]["showBackgroundImage"] = showBackgroundImage;
data["Settings"]["consoleLanguage"] = m_language;
+ // Sorting of TOML sections
+ sortTomlSections(data);
+
std::ofstream file(path, std::ios::binary);
file << data;
file.close();
+
saveMainWindow(path);
}
@@ -979,10 +1083,12 @@ void saveMainWindow(const std::filesystem::path& path) {
data["GUI"]["geometry_y"] = main_window_geometry_y;
data["GUI"]["geometry_w"] = main_window_geometry_w;
data["GUI"]["geometry_h"] = main_window_geometry_h;
- data["GUI"]["pkgDirs"] = m_pkg_viewer;
data["GUI"]["elfDirs"] = m_elf_viewer;
data["GUI"]["recentFiles"] = m_recent_files;
+ // Sorting of TOML sections
+ sortTomlSections(data);
+
std::ofstream file(path, std::ios::binary);
file << data;
file.close();
@@ -991,6 +1097,7 @@ void saveMainWindow(const std::filesystem::path& path) {
void setDefaultValues() {
isHDRAllowed = false;
isNeo = false;
+ isDevKit = false;
isFullscreen = false;
isTrophyPopupDisabled = false;
playBGM = false;
@@ -999,9 +1106,9 @@ void setDefaultValues() {
screenWidth = 1280;
screenHeight = 720;
logFilter = "";
- logType = "async";
+ logType = "sync";
userName = "shadPS4";
- if (Common::isRelease) {
+ if (Common::g_is_release) {
updateChannel = "Release";
} else {
updateChannel = "Nightly";
@@ -1018,7 +1125,7 @@ void setDefaultValues() {
isShowSplash = false;
isAutoUpdate = false;
isAlwaysShowChangelog = false;
- isLeftSideTrophy = false;
+ isSideTrophy = "right";
isNullGpu = false;
shouldDumpShaders = false;
vblankDivider = 1;
@@ -1032,7 +1139,6 @@ void setDefaultValues() {
emulator_language = "en_US";
m_language = 1;
gpuId = -1;
- separateupdatefolder = false;
compatibilityData = false;
checkCompatibilityOnStartup = false;
backgroundImageOpacity = 50;
diff --git a/src/common/config.h b/src/common/config.h
index 988734b93..aba23621c 100644
--- a/src/common/config.h
+++ b/src/common/config.h
@@ -9,6 +9,11 @@
namespace Config {
+struct GameInstallDir {
+ std::filesystem::path path;
+ bool enabled;
+};
+
enum HideCursorState : s16 { Never, Idle, Always };
void load(const std::filesystem::path& path);
@@ -21,13 +26,15 @@ bool GetLoadGameSizeEnabled();
std::filesystem::path GetSaveDataPath();
void setLoadGameSizeEnabled(bool enable);
bool getIsFullscreen();
+bool getShowLabelsUnderIcons();
+bool setShowLabelsUnderIcons();
std::string getFullscreenMode();
bool isNeoModeConsole();
+bool isDevKitConsole();
bool getPlayBGM();
int getBGMvolume();
bool getisTrophyPopupDisabled();
bool getEnableDiscordRPC();
-bool getSeparateUpdateEnabled();
bool getCompatibilityEnabled();
bool getCheckCompatibilityOnStartup();
int getBackgroundImageOpacity();
@@ -63,7 +70,7 @@ bool collectShadersForDebug();
bool showSplash();
bool autoUpdate();
bool alwaysShowChangelog();
-bool leftSideTrophy();
+std::string sideTrophy();
bool nullGpu();
bool copyGPUCmdBuffers();
bool dumpShaders();
@@ -77,7 +84,7 @@ void setCollectShaderForDebug(bool enable);
void setShowSplash(bool enable);
void setAutoUpdate(bool enable);
void setAlwaysShowChangelog(bool enable);
-void setLeftSideTrophy(bool enable);
+void setSideTrophy(std::string side);
void setNullGpu(bool enable);
void setAllowHDR(bool enable);
void setCopyGPUCmdBuffers(bool enable);
@@ -97,8 +104,8 @@ void setNeoMode(bool enable);
void setUserName(const std::string& type);
void setUpdateChannel(const std::string& type);
void setChooseHomeTab(const std::string& type);
-void setSeparateUpdateEnabled(bool use);
-void setGameInstallDirs(const std::vector& settings_install_dirs_config);
+void setGameInstallDirs(const std::vector& dirs_config);
+void setAllGameInstallDirs(const std::vector& dirs_config);
void setSaveDataPath(const std::filesystem::path& path);
void setCompatibilityEnabled(bool use);
void setCheckCompatibilityOnStartup(bool use);
@@ -133,8 +140,9 @@ void setVkGuestMarkersEnabled(bool enable);
// Gui
void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h);
-bool addGameInstallDir(const std::filesystem::path& dir);
+bool addGameInstallDir(const std::filesystem::path& dir, bool enabled = true);
void removeGameInstallDir(const std::filesystem::path& dir);
+void setGameInstallDirEnabled(const std::filesystem::path& dir, bool enabled);
void setAddonInstallDir(const std::filesystem::path& dir);
void setMainWindowTheme(u32 theme);
void setIconSize(u32 size);
@@ -144,7 +152,6 @@ void setSliderPositionGrid(u32 pos);
void setTableMode(u32 mode);
void setMainWindowWidth(u32 width);
void setMainWindowHeight(u32 height);
-void setPkgViewer(const std::vector& pkgList);
void setElfViewer(const std::vector& elfList);
void setRecentFiles(const std::vector& recentFiles);
void setEmulatorLanguage(std::string language);
@@ -153,7 +160,8 @@ u32 getMainWindowGeometryX();
u32 getMainWindowGeometryY();
u32 getMainWindowGeometryW();
u32 getMainWindowGeometryH();
-const std::vector& getGameInstallDirs();
+const std::vector getGameInstallDirs();
+const std::vector getGameInstallDirsEnabled();
std::filesystem::path getAddonInstallDir();
u32 getMainWindowTheme();
u32 getIconSize();
@@ -163,7 +171,6 @@ u32 getSliderPositionGrid();
u32 getTableMode();
u32 getMainWindowWidth();
u32 getMainWindowHeight();
-std::vector getPkgViewer();
std::vector getElfViewer();
std::vector getRecentFiles();
std::string getEmulatorLanguage();
diff --git a/src/common/elf_info.h b/src/common/elf_info.h
index d885709cd..062cee012 100644
--- a/src/common/elf_info.h
+++ b/src/common/elf_info.h
@@ -3,6 +3,7 @@
#pragma once
+#include
#include
#include
@@ -69,6 +70,8 @@ class ElfInfo {
u32 raw_firmware_ver = 0;
PSFAttributes psf_attributes{};
+ std::filesystem::path splash_path{};
+
public:
static constexpr u32 FW_15 = 0x1500000;
static constexpr u32 FW_16 = 0x1600000;
@@ -116,6 +119,10 @@ public:
ASSERT(initialized);
return psf_attributes;
}
+
+ [[nodiscard]] const std::filesystem::path& GetSplashPath() const {
+ return splash_path;
+ }
};
} // namespace Common
diff --git a/src/common/io_file.cpp b/src/common/io_file.cpp
index 067010a26..3efadc6ea 100644
--- a/src/common/io_file.cpp
+++ b/src/common/io_file.cpp
@@ -125,12 +125,15 @@ namespace {
[[nodiscard]] constexpr int ToSeekOrigin(SeekOrigin origin) {
switch (origin) {
case SeekOrigin::SetOrigin:
- default:
return SEEK_SET;
case SeekOrigin::CurrentPosition:
return SEEK_CUR;
case SeekOrigin::End:
return SEEK_END;
+ default:
+ LOG_ERROR(Common_Filesystem, "Unsupported origin {}, defaulting to SEEK_SET",
+ static_cast(origin));
+ return SEEK_SET;
}
}
@@ -377,20 +380,6 @@ bool IOFile::Seek(s64 offset, SeekOrigin origin) const {
return false;
}
- if (False(file_access_mode & (FileAccessMode::Write | FileAccessMode::Append))) {
- u64 size = GetSize();
- if (origin == SeekOrigin::CurrentPosition && Tell() + offset > size) {
- LOG_ERROR(Common_Filesystem, "Seeking past the end of the file");
- return false;
- } else if (origin == SeekOrigin::SetOrigin && (u64)offset > size) {
- LOG_ERROR(Common_Filesystem, "Seeking past the end of the file");
- return false;
- } else if (origin == SeekOrigin::End && offset > 0) {
- LOG_ERROR(Common_Filesystem, "Seeking past the end of the file");
- return false;
- }
- }
-
errno = 0;
const auto seek_result = fseeko(file, offset, ToSeekOrigin(origin)) == 0;
diff --git a/src/common/io_file.h b/src/common/io_file.h
index 45787a092..fb20a2bc5 100644
--- a/src/common/io_file.h
+++ b/src/common/io_file.h
@@ -61,6 +61,8 @@ enum class SeekOrigin : u32 {
SetOrigin, // Seeks from the start of the file.
CurrentPosition, // Seeks from the current file pointer position.
End, // Seeks from the end of the file.
+ SeekHole, // Seeks from the start of the next hole in the file.
+ SeekData, // Seeks from the start of the next non-hole region in the file.
};
class IOFile final {
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index bed7802ed..867d62916 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -101,6 +101,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Lib, Ssl2) \
SUB(Lib, SysModule) \
SUB(Lib, Move) \
+ SUB(Lib, NpAuth) \
SUB(Lib, NpCommon) \
SUB(Lib, NpManager) \
SUB(Lib, NpScore) \
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index c07efbc0d..e5714a81a 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -69,6 +69,7 @@ enum class Class : u8 {
Lib_Http2, ///< The LibSceHttp2 implementation.
Lib_SysModule, ///< The LibSceSysModule implementation
Lib_NpCommon, ///< The LibSceNpCommon implementation
+ Lib_NpAuth, ///< The LibSceNpAuth implementation
Lib_NpManager, ///< The LibSceNpManager implementation
Lib_NpScore, ///< The LibSceNpScore implementation
Lib_NpTrophy, ///< The LibSceNpTrophy implementation
diff --git a/src/common/memory_patcher.cpp b/src/common/memory_patcher.cpp
index 2a8b26acb..bb2d23c45 100644
--- a/src/common/memory_patcher.cpp
+++ b/src/common/memory_patcher.cpp
@@ -169,7 +169,8 @@ void OnGameLoaded() {
if (type == "mask_jump32")
patchMask = MemoryPatcher::PatchMask::Mask_Jump32;
- if (type == "mask" || type == "mask_jump32" && !maskOffsetStr.empty()) {
+ if ((type == "mask" || type == "mask_jump32") &&
+ !maskOffsetStr.empty()) {
maskOffsetValue = std::stoi(maskOffsetStr, 0, 10);
}
diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp
index d48e8c3fe..1a6ff9ec8 100644
--- a/src/common/path_util.cpp
+++ b/src/common/path_util.cpp
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include
#include
#include "common/logging/log.h"
#include "common/path_util.h"
@@ -16,6 +17,8 @@
#ifdef _WIN32
// This is the maximum number of UTF-16 code units permissible in Windows file paths
#define MAX_PATH 260
+#include
+#include
#else
// This is the maximum number of UTF-8 code units permissible in all other OSes' file paths
#define MAX_PATH 1024
@@ -57,7 +60,7 @@ static CFURLRef UntranslocateBundlePath(const CFURLRef bundle_path) {
return nullptr;
}
-static std::filesystem::path GetBundleParentDirectory() {
+static std::optional GetBundleParentDirectory() {
if (CFBundleRef bundle_ref = CFBundleGetMainBundle()) {
if (CFURLRef bundle_url_ref = CFBundleCopyBundleURL(bundle_ref)) {
SCOPE_EXIT {
@@ -80,14 +83,16 @@ static std::filesystem::path GetBundleParentDirectory() {
}
}
}
- return std::filesystem::current_path();
+ return std::nullopt;
}
#endif
static auto UserPaths = [] {
-#ifdef __APPLE__
+#if defined(__APPLE__) && defined(ENABLE_QT_GUI)
// Set the current path to the directory containing the app bundle.
- std::filesystem::current_path(GetBundleParentDirectory());
+ if (const auto bundle_dir = GetBundleParentDirectory()) {
+ std::filesystem::current_path(*bundle_dir);
+ }
#endif
// Try the portable user directory first.
@@ -105,6 +110,10 @@ static auto UserPaths = [] {
} else {
user_dir = std::filesystem::path(getenv("HOME")) / ".local" / "share" / "shadPS4";
}
+#elif _WIN32
+ TCHAR appdata[MAX_PATH] = {0};
+ SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, appdata);
+ user_dir = std::filesystem::path(appdata) / "shadPS4";
#endif
}
@@ -130,6 +139,21 @@ static auto UserPaths = [] {
create_path(PathType::MetaDataDir, user_dir / METADATA_DIR);
create_path(PathType::CustomTrophy, user_dir / CUSTOM_TROPHY);
+ std::ofstream notice_file(user_dir / CUSTOM_TROPHY / "Notice.txt");
+ if (notice_file.is_open()) {
+ notice_file
+ << "++++++++++++++++++++++++++++++++\n+ Custom Trophy Images / Sound "
+ "+\n++++++++++++++++++++++++++++++++\n\nYou can add custom images to the "
+ "trophies.\n*We recommend a square resolution image, for example 200x200, 500x500, "
+ "the same size as the height and width.\nIn this folder ('user\\custom_trophy'), "
+ "add the files with the following "
+ "names:\n\nbronze.png\nsilver.png\ngold.png\nplatinum.png\n\nYou can add a custom "
+ "sound for trophy notifications.\n*By default, no audio is played unless it is in "
+ "this folder and you are using the QT version.\nIn this folder "
+ "('user\\custom_trophy'), add the files with the following names:\n\ntrophy.mp3";
+ notice_file.close();
+ }
+
return paths;
}();
@@ -223,4 +247,4 @@ std::filesystem::path PathFromQString(const QString& path) {
}
#endif
-} // namespace Common::FS
\ No newline at end of file
+} // namespace Common::FS
diff --git a/src/common/scm_rev.cpp.in b/src/common/scm_rev.cpp.in
index 2de04e0be..71c4c2d0a 100644
--- a/src/common/scm_rev.cpp.in
+++ b/src/common/scm_rev.cpp.in
@@ -3,21 +3,17 @@
#include "common/scm_rev.h"
-#define GIT_REV "@GIT_REV@"
-#define GIT_BRANCH "@GIT_BRANCH@"
-#define GIT_DESC "@GIT_DESC@"
-#define GIT_REMOTE_NAME "@GIT_REMOTE_NAME@"
-#define GIT_REMOTE_URL "@GIT_REMOTE_URL@"
-#define BUILD_DATE "@BUILD_DATE@"
-
namespace Common {
-const char g_scm_rev[] = GIT_REV;
-const char g_scm_branch[] = GIT_BRANCH;
-const char g_scm_desc[] = GIT_DESC;
-const char g_scm_remote_name[] = GIT_REMOTE_NAME;
-const char g_scm_remote_url[] = GIT_REMOTE_URL;
-const char g_scm_date[] = BUILD_DATE;
+constexpr char g_version[] = "@APP_VERSION@";
+constexpr bool g_is_release = @APP_IS_RELEASE@;
+
+constexpr char g_scm_rev[] = "@GIT_REV@";
+constexpr char g_scm_branch[] = "@GIT_BRANCH@";
+constexpr char g_scm_desc[] = "@GIT_DESC@";
+constexpr char g_scm_remote_name[] = "@GIT_REMOTE_NAME@";
+constexpr char g_scm_remote_url[] = "@GIT_REMOTE_URL@";
+constexpr char g_scm_date[] = "@BUILD_DATE@";
} // namespace
diff --git a/src/common/scm_rev.h b/src/common/scm_rev.h
index f38efff42..36b844e94 100644
--- a/src/common/scm_rev.h
+++ b/src/common/scm_rev.h
@@ -5,6 +5,9 @@
namespace Common {
+extern const char g_version[];
+extern const bool g_is_release;
+
extern const char g_scm_rev[];
extern const char g_scm_branch[];
extern const char g_scm_desc[];
diff --git a/src/common/sha1.h b/src/common/sha1.h
new file mode 100644
index 000000000..fad849dcc
--- /dev/null
+++ b/src/common/sha1.h
@@ -0,0 +1,180 @@
+// SPDX-FileCopyrightText: 2012 SAURAV MOHAPATRA
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include
+#include
+#include
+
+namespace sha1 {
+class SHA1 {
+public:
+ typedef uint32_t digest32_t[5];
+ typedef uint8_t digest8_t[20];
+ inline static uint32_t LeftRotate(uint32_t value, size_t count) {
+ return (value << count) ^ (value >> (32 - count));
+ }
+ SHA1() {
+ reset();
+ }
+ virtual ~SHA1() {}
+ SHA1(const SHA1& s) {
+ *this = s;
+ }
+ const SHA1& operator=(const SHA1& s) {
+ memcpy(m_digest, s.m_digest, 5 * sizeof(uint32_t));
+ memcpy(m_block, s.m_block, 64);
+ m_blockByteIndex = s.m_blockByteIndex;
+ m_byteCount = s.m_byteCount;
+ return *this;
+ }
+ SHA1& reset() {
+ m_digest[0] = 0x67452301;
+ m_digest[1] = 0xEFCDAB89;
+ m_digest[2] = 0x98BADCFE;
+ m_digest[3] = 0x10325476;
+ m_digest[4] = 0xC3D2E1F0;
+ m_blockByteIndex = 0;
+ m_byteCount = 0;
+ return *this;
+ }
+ SHA1& processByte(uint8_t octet) {
+ this->m_block[this->m_blockByteIndex++] = octet;
+ ++this->m_byteCount;
+ if (m_blockByteIndex == 64) {
+ this->m_blockByteIndex = 0;
+ processBlock();
+ }
+ return *this;
+ }
+ SHA1& processBlock(const void* const start, const void* const end) {
+ const uint8_t* begin = static_cast(start);
+ const uint8_t* finish = static_cast(end);
+ while (begin != finish) {
+ processByte(*begin);
+ begin++;
+ }
+ return *this;
+ }
+ SHA1& processBytes(const void* const data, size_t len) {
+ const uint8_t* block = static_cast(data);
+ processBlock(block, block + len);
+ return *this;
+ }
+ const uint32_t* getDigest(digest32_t digest) {
+ size_t bitCount = this->m_byteCount * 8;
+ processByte(0x80);
+ if (this->m_blockByteIndex > 56) {
+ while (m_blockByteIndex != 0) {
+ processByte(0);
+ }
+ while (m_blockByteIndex < 56) {
+ processByte(0);
+ }
+ } else {
+ while (m_blockByteIndex < 56) {
+ processByte(0);
+ }
+ }
+ processByte(0);
+ processByte(0);
+ processByte(0);
+ processByte(0);
+ processByte(static_cast((bitCount >> 24) & 0xFF));
+ processByte(static_cast((bitCount >> 16) & 0xFF));
+ processByte(static_cast((bitCount >> 8) & 0xFF));
+ processByte(static_cast((bitCount) & 0xFF));
+
+ memcpy(digest, m_digest, 5 * sizeof(uint32_t));
+ return digest;
+ }
+ const uint8_t* getDigestBytes(digest8_t digest) {
+ digest32_t d32;
+ getDigest(d32);
+ size_t di = 0;
+ digest[di++] = ((d32[0] >> 24) & 0xFF);
+ digest[di++] = ((d32[0] >> 16) & 0xFF);
+ digest[di++] = ((d32[0] >> 8) & 0xFF);
+ digest[di++] = ((d32[0]) & 0xFF);
+
+ digest[di++] = ((d32[1] >> 24) & 0xFF);
+ digest[di++] = ((d32[1] >> 16) & 0xFF);
+ digest[di++] = ((d32[1] >> 8) & 0xFF);
+ digest[di++] = ((d32[1]) & 0xFF);
+
+ digest[di++] = ((d32[2] >> 24) & 0xFF);
+ digest[di++] = ((d32[2] >> 16) & 0xFF);
+ digest[di++] = ((d32[2] >> 8) & 0xFF);
+ digest[di++] = ((d32[2]) & 0xFF);
+
+ digest[di++] = ((d32[3] >> 24) & 0xFF);
+ digest[di++] = ((d32[3] >> 16) & 0xFF);
+ digest[di++] = ((d32[3] >> 8) & 0xFF);
+ digest[di++] = ((d32[3]) & 0xFF);
+
+ digest[di++] = ((d32[4] >> 24) & 0xFF);
+ digest[di++] = ((d32[4] >> 16) & 0xFF);
+ digest[di++] = ((d32[4] >> 8) & 0xFF);
+ digest[di++] = ((d32[4]) & 0xFF);
+ return digest;
+ }
+
+protected:
+ void processBlock() {
+ uint32_t w[80];
+ for (size_t i = 0; i < 16; i++) {
+ w[i] = (m_block[i * 4 + 0] << 24);
+ w[i] |= (m_block[i * 4 + 1] << 16);
+ w[i] |= (m_block[i * 4 + 2] << 8);
+ w[i] |= (m_block[i * 4 + 3]);
+ }
+ for (size_t i = 16; i < 80; i++) {
+ w[i] = LeftRotate((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1);
+ }
+
+ uint32_t a = m_digest[0];
+ uint32_t b = m_digest[1];
+ uint32_t c = m_digest[2];
+ uint32_t d = m_digest[3];
+ uint32_t e = m_digest[4];
+
+ for (std::size_t i = 0; i < 80; ++i) {
+ uint32_t f = 0;
+ uint32_t k = 0;
+
+ if (i < 20) {
+ f = (b & c) | (~b & d);
+ k = 0x5A827999;
+ } else if (i < 40) {
+ f = b ^ c ^ d;
+ k = 0x6ED9EBA1;
+ } else if (i < 60) {
+ f = (b & c) | (b & d) | (c & d);
+ k = 0x8F1BBCDC;
+ } else {
+ f = b ^ c ^ d;
+ k = 0xCA62C1D6;
+ }
+ uint32_t temp = LeftRotate(a, 5) + f + e + k + w[i];
+ e = d;
+ d = c;
+ c = LeftRotate(b, 30);
+ b = a;
+ a = temp;
+ }
+
+ m_digest[0] += a;
+ m_digest[1] += b;
+ m_digest[2] += c;
+ m_digest[3] += d;
+ m_digest[4] += e;
+ }
+
+private:
+ digest32_t m_digest;
+ uint8_t m_block[64];
+ size_t m_blockByteIndex;
+ size_t m_byteCount;
+};
+} // namespace sha1
diff --git a/src/common/string_literal.h b/src/common/string_literal.h
new file mode 100644
index 000000000..9f64f62bd
--- /dev/null
+++ b/src/common/string_literal.h
@@ -0,0 +1,15 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+template
+struct StringLiteral {
+ static constexpr size_t len = N;
+
+ constexpr StringLiteral(const C (&str)[N]) {
+ std::copy_n(str, N, value);
+ }
+
+ C value[N]{};
+};
diff --git a/src/common/version.h b/src/common/version.h
deleted file mode 100644
index e7f6cc817..000000000
--- a/src/common/version.h
+++ /dev/null
@@ -1,14 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include
-#include
-
-namespace Common {
-
-constexpr char VERSION[] = "0.6.1 WIP";
-constexpr bool isRelease = false;
-
-} // namespace Common
diff --git a/src/core/cpu_patches.cpp b/src/core/cpu_patches.cpp
index 21acf1a7b..c8106b270 100644
--- a/src/core/cpu_patches.cpp
+++ b/src/core/cpu_patches.cpp
@@ -22,10 +22,6 @@
#include
#else
#include
-#ifdef __APPLE__
-#include
-#include
-#endif
#endif
using namespace Xbyak::util;
@@ -81,490 +77,6 @@ static Xbyak::Address ZydisToXbyakMemoryOperand(const ZydisDecodedOperand& opera
return ptr[expression];
}
-static u64 ZydisToXbyakImmediateOperand(const ZydisDecodedOperand& operand) {
- ASSERT_MSG(operand.type == ZYDIS_OPERAND_TYPE_IMMEDIATE,
- "Expected immediate operand, got type: {}", static_cast(operand.type));
- return operand.imm.value.u;
-}
-
-static std::unique_ptr ZydisToXbyakOperand(const ZydisDecodedOperand& operand) {
- switch (operand.type) {
- case ZYDIS_OPERAND_TYPE_REGISTER: {
- return std::make_unique(ZydisToXbyakRegisterOperand(operand));
- }
- case ZYDIS_OPERAND_TYPE_MEMORY: {
- return std::make_unique(ZydisToXbyakMemoryOperand(operand));
- }
- default:
- UNREACHABLE_MSG("Unsupported operand type: {}", static_cast(operand.type));
- }
-}
-
-static bool OperandUsesRegister(const Xbyak::Operand* operand, int index) {
- if (operand->isREG()) {
- return operand->getIdx() == index;
- }
- if (operand->isMEM()) {
- const Xbyak::RegExp& reg_exp = operand->getAddress().getRegExp();
- return reg_exp.getBase().getIdx() == index || reg_exp.getIndex().getIdx() == index;
- }
- UNREACHABLE_MSG("Unsupported operand kind: {}", static_cast(operand->getKind()));
-}
-
-static bool IsRegisterAllocated(
- const std::initializer_list& allocated_registers, const int index) {
- return std::ranges::find_if(allocated_registers.begin(), allocated_registers.end(),
- [index](const Xbyak::Operand* operand) {
- return OperandUsesRegister(operand, index);
- }) != allocated_registers.end();
-}
-
-static Xbyak::Reg AllocateScratchRegister(
- const std::initializer_list allocated_registers, const u32 bits) {
- for (int index = Xbyak::Operand::R8; index <= Xbyak::Operand::R15; index++) {
- if (!IsRegisterAllocated(allocated_registers, index)) {
- return Xbyak::Reg32e(index, static_cast(bits));
- }
- }
- UNREACHABLE_MSG("Out of scratch registers!");
-}
-
-#ifdef __APPLE__
-
-static pthread_key_t stack_pointer_slot;
-static pthread_key_t patch_stack_slot;
-static std::once_flag patch_context_slots_init_flag;
-static constexpr u32 patch_stack_size = 0x1000;
-
-static_assert(sizeof(void*) == sizeof(u64),
- "Cannot fit a register inside a thread local storage slot.");
-
-static void FreePatchStack(void* patch_stack) {
- // Subtract back to the bottom of the stack for free.
- std::free(static_cast(patch_stack) - patch_stack_size);
-}
-
-static void InitializePatchContextSlots() {
- ASSERT_MSG(pthread_key_create(&stack_pointer_slot, nullptr) == 0,
- "Unable to allocate thread-local register for stack pointer.");
- ASSERT_MSG(pthread_key_create(&patch_stack_slot, FreePatchStack) == 0,
- "Unable to allocate thread-local register for patch stack.");
-}
-
-void InitializeThreadPatchStack() {
- std::call_once(patch_context_slots_init_flag, InitializePatchContextSlots);
-
- pthread_setspecific(patch_stack_slot,
- static_cast(std::malloc(patch_stack_size)) + patch_stack_size);
-}
-
-/// Saves the stack pointer to thread local storage and loads the patch stack.
-static void SaveStack(Xbyak::CodeGenerator& c) {
- std::call_once(patch_context_slots_init_flag, InitializePatchContextSlots);
-
- // Save original stack pointer and load patch stack.
- c.putSeg(gs);
- c.mov(qword[reinterpret_cast(stack_pointer_slot * sizeof(void*))], rsp);
- c.putSeg(gs);
- c.mov(rsp, qword[reinterpret_cast(patch_stack_slot * sizeof(void*))]);
-}
-
-/// Restores the stack pointer from thread local storage.
-static void RestoreStack(Xbyak::CodeGenerator& c) {
- std::call_once(patch_context_slots_init_flag, InitializePatchContextSlots);
-
- // Save patch stack pointer and load original stack.
- c.putSeg(gs);
- c.mov(qword[reinterpret_cast(patch_stack_slot * sizeof(void*))], rsp);
- c.putSeg(gs);
- c.mov(rsp, qword[reinterpret_cast(stack_pointer_slot * sizeof(void*))]);
-}
-
-#else
-
-// These utilities are not implemented as we can't save anything to thread local storage without
-// temporary registers.
-void InitializeThreadPatchStack() {
- // No-op
-}
-
-/// Saves the stack pointer to thread local storage and loads the patch stack.
-static void SaveStack(Xbyak::CodeGenerator& c) {
- UNIMPLEMENTED();
-}
-
-/// Restores the stack pointer from thread local storage.
-static void RestoreStack(Xbyak::CodeGenerator& c) {
- UNIMPLEMENTED();
-}
-
-#endif
-
-/// Switches to the patch stack, saves registers, and restores the original stack.
-static void SaveRegisters(Xbyak::CodeGenerator& c, const std::initializer_list regs) {
- SaveStack(c);
- for (const auto& reg : regs) {
- c.push(reg.cvt64());
- }
- RestoreStack(c);
-}
-
-/// Switches to the patch stack, restores registers, and restores the original stack.
-static void RestoreRegisters(Xbyak::CodeGenerator& c,
- const std::initializer_list regs) {
- SaveStack(c);
- for (const auto& reg : regs) {
- c.pop(reg.cvt64());
- }
- RestoreStack(c);
-}
-
-/// Switches to the patch stack and stores all registers.
-static void SaveContext(Xbyak::CodeGenerator& c, bool save_flags = false) {
- SaveStack(c);
- for (int reg = Xbyak::Operand::RAX; reg <= Xbyak::Operand::R15; reg++) {
- c.push(Xbyak::Reg64(reg));
- }
- c.lea(rsp, ptr[rsp - 32 * 16]);
- for (int reg = 0; reg <= 15; reg++) {
- c.vmovdqu(ptr[rsp + 32 * reg], Xbyak::Ymm(reg));
- }
- if (save_flags) {
- c.pushfq();
- }
-}
-
-/// Restores all registers and restores the original stack.
-/// If the destination is a register, it is not restored to preserve the output.
-static void RestoreContext(Xbyak::CodeGenerator& c, const Xbyak::Operand& dst,
- bool restore_flags = false) {
- if (restore_flags) {
- c.popfq();
- }
- for (int reg = 15; reg >= 0; reg--) {
- if ((!dst.isXMM() && !dst.isYMM()) || dst.getIdx() != reg) {
- c.vmovdqu(Xbyak::Ymm(reg), ptr[rsp + 32 * reg]);
- }
- }
- c.lea(rsp, ptr[rsp + 32 * 16]);
- for (int reg = Xbyak::Operand::R15; reg >= Xbyak::Operand::RAX; reg--) {
- if (!dst.isREG() || dst.getIdx() != reg) {
- c.pop(Xbyak::Reg64(reg));
- } else {
- c.lea(rsp, ptr[rsp + 8]);
- }
- }
- RestoreStack(c);
-}
-
-#ifdef __APPLE__
-
-static void GenerateANDN(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
- const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
- const auto src1 = ZydisToXbyakRegisterOperand(operands[1]);
- const auto src2 = ZydisToXbyakOperand(operands[2]);
-
- // Check if src2 is a memory operand or a register different to dst.
- // In those cases, we don't need to use a temporary register and are free to modify dst.
- // In cases where dst and src2 are the same register, a temporary needs to be used to avoid
- // modifying src2.
- bool src2_uses_dst = false;
- if (src2->isMEM()) {
- const auto base = src2->getAddress().getRegExp().getBase().getIdx();
- const auto index = src2->getAddress().getRegExp().getIndex().getIdx();
- src2_uses_dst = base == dst.getIdx() || index == dst.getIdx();
- } else {
- ASSERT(src2->isREG());
- src2_uses_dst = src2->getReg() == dst;
- }
-
- if (!src2_uses_dst) {
- if (dst != src1)
- c.mov(dst, src1);
- c.not_(dst);
- c.and_(dst, *src2);
- } else {
- const auto scratch = AllocateScratchRegister({&dst, &src1, src2.get()}, dst.getBit());
-
- SaveRegisters(c, {scratch});
-
- c.mov(scratch, src1);
- c.not_(scratch);
- c.and_(scratch, *src2);
- c.mov(dst, scratch);
-
- RestoreRegisters(c, {scratch});
- }
-}
-
-static void GenerateBEXTR(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
- const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
- const auto src = ZydisToXbyakOperand(operands[1]);
- const auto start_len = ZydisToXbyakRegisterOperand(operands[2]);
-
- const Xbyak::Reg32e shift(Xbyak::Operand::RCX, static_cast(start_len.getBit()));
- const auto scratch1 =
- AllocateScratchRegister({&dst, src.get(), &start_len, &shift}, dst.getBit());
- const auto scratch2 =
- AllocateScratchRegister({&dst, src.get(), &start_len, &shift, &scratch1}, dst.getBit());
-
- if (dst.getIdx() == shift.getIdx()) {
- SaveRegisters(c, {scratch1, scratch2});
- } else {
- SaveRegisters(c, {scratch1, scratch2, shift});
- }
-
- c.mov(scratch1, *src);
- if (shift.getIdx() != start_len.getIdx()) {
- c.mov(shift, start_len);
- }
-
- c.shr(scratch1, shift.cvt8());
- c.shr(shift, 8);
- c.mov(scratch2, 1);
- c.shl(scratch2, shift.cvt8());
- c.dec(scratch2);
-
- c.mov(dst, scratch1);
- c.and_(dst, scratch2);
-
- if (dst.getIdx() == shift.getIdx()) {
- RestoreRegisters(c, {scratch2, scratch1});
- } else {
- RestoreRegisters(c, {shift, scratch2, scratch1});
- }
-}
-
-static void GenerateBLSI(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
- const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
- const auto src = ZydisToXbyakOperand(operands[1]);
-
- const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit());
-
- SaveRegisters(c, {scratch});
-
- // BLSI sets CF to zero if source is zero, otherwise it sets CF to one.
- Xbyak::Label clear_carry, end;
-
- c.mov(scratch, *src);
- c.neg(scratch); // NEG, like BLSI, clears CF if the source is zero and sets it otherwise
- c.jnc(clear_carry);
-
- c.and_(scratch, *src);
- c.stc(); // setting/clearing carry needs to happen after the AND because that clears CF
- c.jmp(end);
-
- c.L(clear_carry);
- c.and_(scratch, *src);
- // We don't need to clear carry here since AND does that for us
-
- c.L(end);
- c.mov(dst, scratch);
-
- RestoreRegisters(c, {scratch});
-}
-
-static void GenerateBLSMSK(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
- const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
- const auto src = ZydisToXbyakOperand(operands[1]);
-
- const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit());
-
- SaveRegisters(c, {scratch});
-
- Xbyak::Label clear_carry, end;
-
- // BLSMSK sets CF to zero if source is NOT zero, otherwise it sets CF to one.
- c.mov(scratch, *src);
- c.test(scratch, scratch);
- c.jnz(clear_carry);
-
- c.dec(scratch);
- c.xor_(scratch, *src);
- c.stc();
- c.jmp(end);
-
- c.L(clear_carry);
- c.dec(scratch);
- c.xor_(scratch, *src);
- // We don't need to clear carry here since XOR does that for us
-
- c.L(end);
- c.mov(dst, scratch);
-
- RestoreRegisters(c, {scratch});
-}
-
-static void GenerateBLSR(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
- const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
- const auto src = ZydisToXbyakOperand(operands[1]);
-
- const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit());
-
- SaveRegisters(c, {scratch});
-
- Xbyak::Label clear_carry, end;
-
- // BLSR sets CF to zero if source is NOT zero, otherwise it sets CF to one.
- c.mov(scratch, *src);
- c.test(scratch, scratch);
- c.jnz(clear_carry);
-
- c.dec(scratch);
- c.and_(scratch, *src);
- c.stc();
- c.jmp(end);
-
- c.L(clear_carry);
- c.dec(scratch);
- c.and_(scratch, *src);
- // We don't need to clear carry here since AND does that for us
-
- c.L(end);
- c.mov(dst, scratch);
-
- RestoreRegisters(c, {scratch});
-}
-
-static __attribute__((sysv_abi)) void PerformVCVTPH2PS(float* out, const half_float::half* in,
- const u32 count) {
- for (u32 i = 0; i < count; i++) {
- out[i] = half_float::half_cast(in[i]);
- }
-}
-
-static void GenerateVCVTPH2PS(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
- const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
- const auto src = ZydisToXbyakOperand(operands[1]);
-
- const auto float_count = dst.getBit() / 32;
- const auto byte_count = float_count * 4;
-
- SaveContext(c, true);
-
- // Allocate stack space for outputs and load into first parameter.
- c.sub(rsp, byte_count);
- c.mov(rdi, rsp);
-
- if (src->isXMM()) {
- // Allocate stack space for inputs and load into second parameter.
- c.sub(rsp, byte_count);
- c.mov(rsi, rsp);
-
- // Move input to the allocated space.
- c.movdqu(ptr[rsp], *reinterpret_cast(src.get()));
- } else {
- c.lea(rsi, src->getAddress());
- }
-
- // Load float count into third parameter.
- c.mov(rdx, float_count);
-
- c.mov(rax, reinterpret_cast(PerformVCVTPH2PS));
- c.call(rax);
-
- if (src->isXMM()) {
- // Clean up after inputs space.
- c.add(rsp, byte_count);
- }
-
- // Load outputs into destination register and clean up space.
- if (dst.isYMM()) {
- c.vmovdqu(*reinterpret_cast(&dst), ptr[rsp]);
- } else {
- c.movdqu(*reinterpret_cast(&dst), ptr[rsp]);
- }
- c.add(rsp, byte_count);
-
- RestoreContext(c, dst, true);
-}
-
-using SingleToHalfFloatConverter = half_float::half (*)(float);
-static const SingleToHalfFloatConverter SingleToHalfFloatConverters[4] = {
- half_float::half_cast,
- half_float::half_cast,
- half_float::half_cast,
- half_float::half_cast,
-};
-
-static __attribute__((sysv_abi)) void PerformVCVTPS2PH(half_float::half* out, const float* in,
- const u32 count, const u8 rounding_mode) {
- const auto conversion_func = SingleToHalfFloatConverters[rounding_mode];
-
- for (u32 i = 0; i < count; i++) {
- out[i] = conversion_func(in[i]);
- }
-}
-
-static void GenerateVCVTPS2PH(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
- const auto dst = ZydisToXbyakOperand(operands[0]);
- const auto src = ZydisToXbyakRegisterOperand(operands[1]);
- const auto ctrl = ZydisToXbyakImmediateOperand(operands[2]);
-
- const auto float_count = src.getBit() / 32;
- const auto byte_count = float_count * 4;
-
- SaveContext(c, true);
-
- if (dst->isXMM()) {
- // Allocate stack space for outputs and load into first parameter.
- c.sub(rsp, byte_count);
- c.mov(rdi, rsp);
- } else {
- c.lea(rdi, dst->getAddress());
- }
-
- // Allocate stack space for inputs and load into second parameter.
- c.sub(rsp, byte_count);
- c.mov(rsi, rsp);
-
- // Move input to the allocated space.
- if (src.isYMM()) {
- c.vmovdqu(ptr[rsp], *reinterpret_cast(&src));
- } else {
- c.movdqu(ptr[rsp], *reinterpret_cast(&src));
- }
-
- // Load float count into third parameter.
- c.mov(rdx, float_count);
-
- // Load rounding mode into fourth parameter.
- if (ctrl & 4) {
- // Load from MXCSR.RC.
- c.stmxcsr(ptr[rsp - 4]);
- c.mov(rcx, ptr[rsp - 4]);
- c.shr(rcx, 13);
- c.and_(rcx, 3);
- } else {
- c.mov(rcx, ctrl & 3);
- }
-
- c.mov(rax, reinterpret_cast(PerformVCVTPS2PH));
- c.call(rax);
-
- // Clean up after inputs space.
- c.add(rsp, byte_count);
-
- if (dst->isXMM()) {
- // Load outputs into destination register and clean up space.
- c.movdqu(*reinterpret_cast(dst.get()), ptr[rsp]);
- c.add(rsp, byte_count);
- }
-
- RestoreContext(c, *dst, true);
-}
-
-static bool FilterRosetta2Only(const ZydisDecodedOperand*) {
- int ret = 0;
- size_t size = sizeof(ret);
- if (sysctlbyname("sysctl.proc_translated", &ret, &size, nullptr, 0) != 0) {
- return false;
- }
- return ret;
-}
-
-#else // __APPLE__
-
static bool FilterTcbAccess(const ZydisDecodedOperand* operands) {
const auto& dst_op = operands[0];
const auto& src_op = operands[1];
@@ -609,8 +121,6 @@ static void GenerateTcbAccess(const ZydisDecodedOperand* operands, Xbyak::CodeGe
#endif
}
-#endif // __APPLE__
-
static bool FilterNoSSE4a(const ZydisDecodedOperand*) {
Cpu cpu;
return !cpu.has(Cpu::tSSE4a);
@@ -887,28 +397,16 @@ struct PatchInfo {
};
static const std::unordered_map Patches = {
+ // SSE4a
+ {ZYDIS_MNEMONIC_EXTRQ, {FilterNoSSE4a, GenerateEXTRQ, true}},
+ {ZYDIS_MNEMONIC_INSERTQ, {FilterNoSSE4a, GenerateINSERTQ, true}},
+
#if defined(_WIN32)
// Windows needs a trampoline.
{ZYDIS_MNEMONIC_MOV, {FilterTcbAccess, GenerateTcbAccess, true}},
#elif !defined(__APPLE__)
{ZYDIS_MNEMONIC_MOV, {FilterTcbAccess, GenerateTcbAccess, false}},
#endif
-
- {ZYDIS_MNEMONIC_EXTRQ, {FilterNoSSE4a, GenerateEXTRQ, true}},
- {ZYDIS_MNEMONIC_INSERTQ, {FilterNoSSE4a, GenerateINSERTQ, true}},
-
-#ifdef __APPLE__
- // Patches for instruction sets not supported by Rosetta 2.
- // BMI1
- {ZYDIS_MNEMONIC_ANDN, {FilterRosetta2Only, GenerateANDN, true}},
- {ZYDIS_MNEMONIC_BEXTR, {FilterRosetta2Only, GenerateBEXTR, true}},
- {ZYDIS_MNEMONIC_BLSI, {FilterRosetta2Only, GenerateBLSI, true}},
- {ZYDIS_MNEMONIC_BLSMSK, {FilterRosetta2Only, GenerateBLSMSK, true}},
- {ZYDIS_MNEMONIC_BLSR, {FilterRosetta2Only, GenerateBLSR, true}},
- // F16C
- {ZYDIS_MNEMONIC_VCVTPH2PS, {FilterRosetta2Only, GenerateVCVTPH2PS, true}},
- {ZYDIS_MNEMONIC_VCVTPS2PH, {FilterRosetta2Only, GenerateVCVTPS2PH, true}},
-#endif
};
static std::once_flag init_flag;
@@ -1225,18 +723,7 @@ void RegisterPatchModule(void* module_ptr, u64 module_size, void* trampoline_are
}
void PrePatchInstructions(u64 segment_addr, u64 segment_size) {
-#if defined(__APPLE__)
- // HACK: For some reason patching in the signal handler at the start of a page does not work
- // under Rosetta 2. Patch any instructions at the start of a page ahead of time.
- if (!Patches.empty()) {
- auto* code_page = reinterpret_cast(Common::AlignUp(segment_addr, 0x1000));
- const auto* end_page = code_page + Common::AlignUp(segment_size, 0x1000);
- while (code_page < end_page) {
- TryPatchJit(code_page);
- code_page += 0x1000;
- }
- }
-#elif !defined(_WIN32)
+#if !defined(_WIN32) && !defined(__APPLE__)
// Linux and others have an FS segment pointing to valid memory, so continue to do full
// ahead-of-time patching for now until a better solution is worked out.
if (!Patches.empty()) {
diff --git a/src/core/cpu_patches.h b/src/core/cpu_patches.h
index 1ccac073a..7a0546046 100644
--- a/src/core/cpu_patches.h
+++ b/src/core/cpu_patches.h
@@ -7,12 +7,6 @@
namespace Core {
-/// Initializes a stack for the current thread for use by patch implementations.
-void InitializeThreadPatchStack();
-
-/// Cleans up the patch stack for the current thread.
-void CleanupThreadPatchStack();
-
/// Registers a module for patching, providing an area to generate trampoline code.
void RegisterPatchModule(void* module_ptr, u64 module_size, void* trampoline_area_ptr,
u64 trampoline_area_size);
diff --git a/src/core/crypto/crypto.cpp b/src/core/crypto/crypto.cpp
deleted file mode 100644
index 4020edfd8..000000000
--- a/src/core/crypto/crypto.cpp
+++ /dev/null
@@ -1,215 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include
-
-#include "crypto.h"
-
-CryptoPP::RSA::PrivateKey Crypto::key_pkg_derived_key3_keyset_init() {
- CryptoPP::InvertibleRSAFunction params;
- params.SetPrime1(CryptoPP::Integer(PkgDerivedKey3Keyset::Prime1, 0x80));
- params.SetPrime2(CryptoPP::Integer(PkgDerivedKey3Keyset::Prime2, 0x80));
-
- params.SetPublicExponent(CryptoPP::Integer(PkgDerivedKey3Keyset::PublicExponent, 4));
- params.SetPrivateExponent(CryptoPP::Integer(PkgDerivedKey3Keyset::PrivateExponent, 0x100));
-
- params.SetModPrime1PrivateExponent(CryptoPP::Integer(PkgDerivedKey3Keyset::Exponent1, 0x80));
- params.SetModPrime2PrivateExponent(CryptoPP::Integer(PkgDerivedKey3Keyset::Exponent2, 0x80));
-
- params.SetModulus(CryptoPP::Integer(PkgDerivedKey3Keyset::Modulus, 0x100));
- params.SetMultiplicativeInverseOfPrime2ModPrime1(
- CryptoPP::Integer(PkgDerivedKey3Keyset::Coefficient, 0x80));
-
- CryptoPP::RSA::PrivateKey privateKey(params);
-
- return privateKey;
-}
-
-CryptoPP::RSA::PrivateKey Crypto::FakeKeyset_keyset_init() {
- CryptoPP::InvertibleRSAFunction params;
- params.SetPrime1(CryptoPP::Integer(FakeKeyset::Prime1, 0x80));
- params.SetPrime2(CryptoPP::Integer(FakeKeyset::Prime2, 0x80));
-
- params.SetPublicExponent(CryptoPP::Integer(FakeKeyset::PublicExponent, 4));
- params.SetPrivateExponent(CryptoPP::Integer(FakeKeyset::PrivateExponent, 0x100));
-
- params.SetModPrime1PrivateExponent(CryptoPP::Integer(FakeKeyset::Exponent1, 0x80));
- params.SetModPrime2PrivateExponent(CryptoPP::Integer(FakeKeyset::Exponent2, 0x80));
-
- params.SetModulus(CryptoPP::Integer(FakeKeyset::Modulus, 0x100));
- params.SetMultiplicativeInverseOfPrime2ModPrime1(
- CryptoPP::Integer(FakeKeyset::Coefficient, 0x80));
-
- CryptoPP::RSA::PrivateKey privateKey(params);
-
- return privateKey;
-}
-
-CryptoPP::RSA::PrivateKey Crypto::DebugRifKeyset_init() {
- CryptoPP::InvertibleRSAFunction params;
- params.SetPrime1(CryptoPP::Integer(DebugRifKeyset::Prime1, sizeof(DebugRifKeyset::Prime1)));
- params.SetPrime2(CryptoPP::Integer(DebugRifKeyset::Prime2, sizeof(DebugRifKeyset::Prime2)));
-
- params.SetPublicExponent(
- CryptoPP::Integer(DebugRifKeyset::PublicExponent, sizeof(DebugRifKeyset::PublicExponent)));
- params.SetPrivateExponent(CryptoPP::Integer(DebugRifKeyset::PrivateExponent,
- sizeof(DebugRifKeyset::PrivateExponent)));
-
- params.SetModPrime1PrivateExponent(
- CryptoPP::Integer(DebugRifKeyset::Exponent1, sizeof(DebugRifKeyset::Exponent1)));
- params.SetModPrime2PrivateExponent(
- CryptoPP::Integer(DebugRifKeyset::Exponent2, sizeof(DebugRifKeyset::Exponent2)));
-
- params.SetModulus(CryptoPP::Integer(DebugRifKeyset::Modulus, sizeof(DebugRifKeyset::Modulus)));
- params.SetMultiplicativeInverseOfPrime2ModPrime1(
- CryptoPP::Integer(DebugRifKeyset::Coefficient, sizeof(DebugRifKeyset::Coefficient)));
-
- CryptoPP::RSA::PrivateKey privateKey(params);
-
- return privateKey;
-}
-
-void Crypto::RSA2048Decrypt(std::span dec_key,
- std::span ciphertext,
- bool is_dk3) { // RSAES_PKCS1v15_
- // Create an RSA decryptor
- CryptoPP::RSA::PrivateKey privateKey;
- if (is_dk3) {
- privateKey = key_pkg_derived_key3_keyset_init();
- } else {
- privateKey = FakeKeyset_keyset_init();
- }
-
- CryptoPP::RSAES_PKCS1v15_Decryptor rsaDecryptor(privateKey);
-
- // Allocate memory for the decrypted data
- std::array decrypted;
-
- // Perform the decryption
- CryptoPP::AutoSeededRandomPool rng;
- CryptoPP::DecodingResult result =
- rsaDecryptor.Decrypt(rng, ciphertext.data(), decrypted.size(), decrypted.data());
- std::copy(decrypted.begin(), decrypted.begin() + dec_key.size(), dec_key.begin());
-}
-
-void Crypto::ivKeyHASH256(std::span cipher_input,
- std::span ivkey_result) {
- CryptoPP::SHA256 sha256;
- std::array hashResult;
- auto array_sink = new CryptoPP::ArraySink(hashResult.data(), CryptoPP::SHA256::DIGESTSIZE);
- auto filter = new CryptoPP::HashFilter(sha256, array_sink);
- CryptoPP::ArraySource r(cipher_input.data(), cipher_input.size(), true, filter);
- std::copy(hashResult.begin(), hashResult.begin() + ivkey_result.size(), ivkey_result.begin());
-}
-
-void Crypto::aesCbcCfb128Decrypt(std::span ivkey,
- std::span ciphertext,
- std::span decrypted) {
- std::array key;
- std::array iv;
-
- std::copy(ivkey.begin() + 16, ivkey.begin() + 16 + key.size(), key.begin());
- std::copy(ivkey.begin(), ivkey.begin() + iv.size(), iv.begin());
-
- CryptoPP::AES::Decryption aesDecryption(key.data(), CryptoPP::AES::DEFAULT_KEYLENGTH);
- CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, iv.data());
-
- for (size_t i = 0; i < decrypted.size(); i += CryptoPP::AES::BLOCKSIZE) {
- cbcDecryption.ProcessData(decrypted.data() + i, ciphertext.data() + i,
- CryptoPP::AES::BLOCKSIZE);
- }
-}
-
-void Crypto::aesCbcCfb128DecryptEntry(std::span ivkey,
- std::span ciphertext,
- std::span decrypted) {
- std::array key;
- std::array iv;
-
- std::copy(ivkey.begin() + 16, ivkey.begin() + 16 + key.size(), key.begin());
- std::copy(ivkey.begin(), ivkey.begin() + iv.size(), iv.begin());
-
- CryptoPP::AES::Decryption aesDecryption(key.data(), CryptoPP::AES::DEFAULT_KEYLENGTH);
- CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, iv.data());
-
- for (size_t i = 0; i < decrypted.size(); i += CryptoPP::AES::BLOCKSIZE) {
- cbcDecryption.ProcessData(decrypted.data() + i, ciphertext.data() + i,
- CryptoPP::AES::BLOCKSIZE);
- }
-}
-
-void Crypto::decryptEFSM(std::span trophyKey,
- std::span NPcommID,
- std::span efsmIv, std::span ciphertext,
- std::span decrypted) {
-
- // step 1: Encrypt NPcommID
- CryptoPP::CBC_Mode::Encryption encrypt;
-
- std::vector trophyIv(16, 0);
- std::vector trpKey(16);
-
- encrypt.SetKeyWithIV(trophyKey.data(), trophyKey.size(), trophyIv.data());
- encrypt.ProcessData(trpKey.data(), NPcommID.data(), 16);
-
- // step 2: decrypt efsm.
- CryptoPP::CBC_Mode::Decryption decrypt;
- decrypt.SetKeyWithIV(trpKey.data(), trpKey.size(), efsmIv.data());
-
- for (size_t i = 0; i < decrypted.size(); i += CryptoPP::AES::BLOCKSIZE) {
- decrypt.ProcessData(decrypted.data() + i, ciphertext.data() + i, CryptoPP::AES::BLOCKSIZE);
- }
-}
-
-void Crypto::PfsGenCryptoKey(std::span ekpfs,
- std::span seed,
- std::span dataKey,
- std::span tweakKey) {
- CryptoPP::HMAC hmac(ekpfs.data(), ekpfs.size());
-
- CryptoPP::SecByteBlock d(20); // Use Crypto++ SecByteBlock for better memory management
-
- // Copy the bytes of 'index' to the 'd' array
- uint32_t index = 1;
- std::memcpy(d, &index, sizeof(uint32_t));
-
- // Copy the bytes of 'seed' to the 'd' array starting from index 4
- std::memcpy(d + sizeof(uint32_t), seed.data(), seed.size());
-
- // Allocate memory for 'u64' using new
- std::vector data_tweak_key(hmac.DigestSize());
-
- // Calculate the HMAC
- hmac.CalculateDigest(data_tweak_key.data(), d, d.size());
- std::copy(data_tweak_key.begin(), data_tweak_key.begin() + dataKey.size(), tweakKey.begin());
- std::copy(data_tweak_key.begin() + tweakKey.size(),
- data_tweak_key.begin() + tweakKey.size() + dataKey.size(), dataKey.begin());
-}
-
-void Crypto::decryptPFS(std::span dataKey,
- std::span tweakKey, std::span src_image,
- std::span dst_image, u64 sector) {
- // Start at 0x10000 to keep the header when decrypting the whole pfs_image.
- for (int i = 0; i < src_image.size(); i += 0x1000) {
- const u64 current_sector = sector + (i / 0x1000);
- CryptoPP::ECB_Mode::Encryption encrypt(tweakKey.data(), tweakKey.size());
- CryptoPP::ECB_Mode::Decryption decrypt(dataKey.data(), dataKey.size());
-
- std::array tweak{};
- std::array encryptedTweak;
- std::array xorBuffer;
- std::memcpy(tweak.data(), ¤t_sector, sizeof(u64));
-
- // Encrypt the tweak for each sector.
- encrypt.ProcessData(encryptedTweak.data(), tweak.data(), 16);
-
- for (int plaintextOffset = 0; plaintextOffset < 0x1000; plaintextOffset += 16) {
- xtsXorBlock(xorBuffer.data(), src_image.data() + i + plaintextOffset,
- encryptedTweak.data()); // x, c, t
- decrypt.ProcessData(xorBuffer.data(), xorBuffer.data(), 16); // x, x
- xtsXorBlock(dst_image.data() + i + plaintextOffset, xorBuffer.data(),
- encryptedTweak.data()); //(p) c, x , t
- xtsMult(encryptedTweak);
- }
- }
-}
diff --git a/src/core/crypto/crypto.h b/src/core/crypto/crypto.h
deleted file mode 100644
index b5d8104b5..000000000
--- a/src/core/crypto/crypto.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "common/types.h"
-#include "keys.h"
-
-class Crypto {
-public:
- CryptoPP::RSA::PrivateKey key_pkg_derived_key3_keyset_init();
- CryptoPP::RSA::PrivateKey FakeKeyset_keyset_init();
- CryptoPP::RSA::PrivateKey DebugRifKeyset_init();
-
- void RSA2048Decrypt(std::span dk3,
- std::span ciphertext,
- bool is_dk3); // RSAES_PKCS1v15_
- void ivKeyHASH256(std::span cipher_input,
- std::span ivkey_result);
- void aesCbcCfb128Decrypt(std::span ivkey,
- std::span ciphertext,
- std::span decrypted);
- void aesCbcCfb128DecryptEntry(std::span ivkey,
- std::span