mirror of
https://github.com/shadps4-emu/shadps4-qtlauncher.git
synced 2026-01-31 01:05:16 +01:00
Clean up build and dead code/modules. (#56)
This commit is contained in:
24
.github/workflows/build.yml
vendored
24
.github/workflows/build.yml
vendored
@@ -115,8 +115,11 @@ jobs:
|
||||
path: upload/
|
||||
|
||||
macos-qt:
|
||||
runs-on: macos-15
|
||||
runs-on: macos-latest
|
||||
needs: get-info
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [ "arm64", "x86_64" ]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -140,7 +143,7 @@ jobs:
|
||||
- name: Cache CMake Configuration
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-qt-cache-cmake-configuration
|
||||
cache-name: ${{ runner.os }}-${{ matrix.arch }}-qt-cache-cmake-configuration
|
||||
with:
|
||||
path: |
|
||||
${{github.workspace}}/build
|
||||
@@ -151,29 +154,30 @@ jobs:
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.18
|
||||
env:
|
||||
cache-name: ${{runner.os}}-qt-cache-cmake-build
|
||||
cache-name: ${{ runner.os }}-${{ matrix.arch }}-qt-cache-cmake-build
|
||||
with:
|
||||
append-timestamp: false
|
||||
create-symlink: true
|
||||
key: ${{env.cache-name}}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
variant: sccache
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DENABLE_UPDATER=ON -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache
|
||||
run: cmake --fresh -B ${{ github.workspace }}/build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} -DENABLE_UPDATER=ON -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(sysctl -n hw.ncpu)
|
||||
run: cmake --build ${{ github.workspace }}/build --config ${{ env.BUILD_TYPE }} --parallel $(sysctl -n hw.ncpu)
|
||||
|
||||
- name: Package and Upload macOS Qt artifact
|
||||
run: |
|
||||
mkdir upload
|
||||
mv ${{github.workspace}}/build/shadPS4QtLauncher.app upload
|
||||
mv ${{ github.workspace }}/build/shadPS4QtLauncher.app upload
|
||||
macdeployqt upload/shadPS4QtLauncher.app
|
||||
tar cf shadPS4QtLauncher-macos-qt.tar.gz -C upload .
|
||||
codesign --deep -fs - upload/shadPS4QtLauncher.app
|
||||
tar cf shadPS4QtLauncher-macos-${{ matrix.arch }}-qt.tar.gz -C upload .
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: shadPS4QtLauncher-macos-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
|
||||
path: shadPS4QtLauncher-macos-qt.tar.gz
|
||||
name: shadPS4QtLauncher-macos-${{ matrix.arch }}-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
|
||||
path: shadPS4QtLauncher-macos-${{ matrix.arch }}-qt.tar.gz
|
||||
|
||||
linux-qt:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
63
.gitmodules
vendored
63
.gitmodules
vendored
@@ -1,7 +1,3 @@
|
||||
[submodule "externals/zlib-ng"]
|
||||
path = externals/zlib-ng
|
||||
url = https://github.com/shadps4-emu/ext-zlib-ng.git
|
||||
shallow = true
|
||||
[submodule "externals/sdl3"]
|
||||
path = externals/sdl3
|
||||
url = https://github.com/shadps4-emu/ext-SDL.git
|
||||
@@ -10,69 +6,14 @@
|
||||
path = externals/fmt
|
||||
url = https://github.com/shadps4-emu/ext-fmt.git
|
||||
shallow = true
|
||||
[submodule "externals/robin-map"]
|
||||
path = externals/robin-map
|
||||
url = https://github.com/Tessil/robin-map.git
|
||||
shallow = true
|
||||
[submodule "externals/xbyak"]
|
||||
path = externals/xbyak
|
||||
url = https://github.com/herumi/xbyak.git
|
||||
shallow = true
|
||||
[submodule "externals/magic_enum"]
|
||||
path = externals/magic_enum
|
||||
url = https://github.com/Neargye/magic_enum.git
|
||||
shallow = true
|
||||
[submodule "externals/toml11"]
|
||||
path = externals/toml11
|
||||
url = https://github.com/ToruNiina/toml11.git
|
||||
shallow = true
|
||||
[submodule "externals/zydis"]
|
||||
path = externals/zydis
|
||||
url = https://github.com/zyantific/zydis.git
|
||||
shallow = true
|
||||
[submodule "externals/sirit"]
|
||||
path = externals/sirit
|
||||
url = https://github.com/shadps4-emu/sirit.git
|
||||
shallow = true
|
||||
[submodule "externals/xxhash"]
|
||||
path = externals/xxhash
|
||||
url = https://github.com/Cyan4973/xxHash.git
|
||||
shallow = true
|
||||
[submodule "externals/ext-boost"]
|
||||
path = externals/ext-boost
|
||||
url = https://github.com/shadps4-emu/ext-boost.git
|
||||
shallow = true
|
||||
[submodule "externals/date"]
|
||||
path = externals/date
|
||||
url = https://github.com/HowardHinnant/date.git
|
||||
shallow = true
|
||||
[submodule "externals/ffmpeg-core"]
|
||||
path = externals/ffmpeg-core
|
||||
url = https://github.com/shadps4-emu/ext-ffmpeg-core.git
|
||||
shallow = true
|
||||
[submodule "externals/pugixml"]
|
||||
path = externals/pugixml
|
||||
url = https://github.com/zeux/pugixml.git
|
||||
shallow = true
|
||||
[submodule "externals/discord-rpc"]
|
||||
path = externals/discord-rpc
|
||||
url = https://github.com/shadps4-emu/ext-discord-rpc.git
|
||||
shallow = true
|
||||
[submodule "externals/LibAtrac9"]
|
||||
path = externals/LibAtrac9
|
||||
url = https://github.com/shadps4-emu/ext-LibAtrac9.git
|
||||
shallow = true
|
||||
[submodule "externals/libpng"]
|
||||
path = externals/libpng
|
||||
url = https://github.com/pnggroup/libpng
|
||||
shallow = true
|
||||
[submodule "externals/epoll-shim"]
|
||||
path = externals/epoll-shim
|
||||
url = https://github.com/jiixyj/epoll-shim.git
|
||||
[submodule "externals/hwinfo"]
|
||||
path = externals/hwinfo
|
||||
url = https://github.com/shadps4-emu/ext-hwinfo
|
||||
shallow = true
|
||||
[submodule "externals/volk"]
|
||||
path = externals/volk
|
||||
url = https://github.com/zeux/volk.git
|
||||
@@ -89,7 +30,3 @@
|
||||
path = externals/MoltenVK/MoltenVK
|
||||
url = https://github.com/KhronosGroup/MoltenVK
|
||||
shallow = true
|
||||
[submodule "externals/MoltenVK/cereal"]
|
||||
path = externals/MoltenVK/cereal
|
||||
url = https://github.com/USCiLab/cereal
|
||||
shallow = true
|
||||
21
CMakeDarwinPresets.json
Normal file
21
CMakeDarwinPresets.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"version": 9,
|
||||
"cmakeMinimumRequired": {
|
||||
"major": 3,
|
||||
"minor": 30,
|
||||
"patch": 0
|
||||
},
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "x64-Clang-Base",
|
||||
"hidden": true,
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/Build/${presetName}",
|
||||
"cacheVariables": {
|
||||
"CMAKE_C_COMPILER": "clang",
|
||||
"CMAKE_CXX_COMPILER": "clang++",
|
||||
"CMAKE_INSTALL_PREFIX": "${sourceDir}/Build/${presetName}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
100
CMakeLists.txt
100
CMakeLists.txt
@@ -31,7 +31,6 @@ if(UNIX AND NOT APPLE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(ENABLE_DISCORD_RPC "Enable the Discord RPC integration" ON)
|
||||
option(ENABLE_UPDATER "Enables the options to updater" ON)
|
||||
|
||||
# First, determine whether to use CMAKE_OSX_ARCHITECTURES or CMAKE_SYSTEM_PROCESSOR.
|
||||
@@ -224,29 +223,11 @@ if(WIN32 AND NOT CMAKE_PREFIX_PATH)
|
||||
endif ()
|
||||
|
||||
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)
|
||||
find_package(glslang 15 CONFIG)
|
||||
find_package(half 1.12.0 MODULE)
|
||||
find_package(magic_enum 0.9.7 CONFIG)
|
||||
find_package(PNG 1.6 MODULE)
|
||||
find_package(RenderDoc 1.6.0 MODULE)
|
||||
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.324 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 (APPLE)
|
||||
find_package(date 3.0.1 CONFIG)
|
||||
find_package(epoll-shim 3.14 CONFIG)
|
||||
endif()
|
||||
list(POP_BACK CMAKE_MODULE_PATH)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
@@ -294,86 +275,34 @@ set(COMMON src/common/logging/backend.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
|
||||
src/common/assert.h
|
||||
src/common/bit_array.h
|
||||
src/common/bit_field.h
|
||||
src/common/bounded_threadsafe_queue.h
|
||||
src/common/concepts.h
|
||||
src/common/config.cpp
|
||||
src/common/config.h
|
||||
src/common/cstring.h
|
||||
src/common/debug.h
|
||||
src/common/decoder.cpp
|
||||
src/common/decoder.h
|
||||
src/common/elf_info.h
|
||||
src/common/endian.h
|
||||
src/common/enum.h
|
||||
src/common/io_file.cpp
|
||||
src/common/io_file.h
|
||||
src/common/lru_cache.h
|
||||
src/common/error.cpp
|
||||
src/common/error.h
|
||||
src/common/scope_exit.h
|
||||
src/common/fixed_value.h
|
||||
src/common/func_traits.h
|
||||
# src/common/native_clock.cpp
|
||||
# src/common/native_clock.h
|
||||
src/common/path_util.cpp
|
||||
src/common/path_util.h
|
||||
src/common/object_pool.h
|
||||
src/common/polyfill_thread.h
|
||||
# src/common/range_lock.h
|
||||
# src/common/rdtsc.cpp
|
||||
# src/common/rdtsc.h
|
||||
src/common/recursive_lock.cpp
|
||||
src/common/recursive_lock.h
|
||||
src/common/sha1.h
|
||||
src/common/shared_first_mutex.h
|
||||
src/common/signal_context.h
|
||||
src/common/signal_context.cpp
|
||||
src/common/singleton.h
|
||||
# src/common/slab_heap.h
|
||||
# src/common/slot_vector.h
|
||||
# src/common/spin_lock.cpp
|
||||
# src/common/spin_lock.h
|
||||
src/common/string_literal.h
|
||||
src/common/string_util.cpp
|
||||
src/common/string_util.h
|
||||
src/common/thread.cpp
|
||||
src/common/thread.h
|
||||
src/common/types.h
|
||||
# src/common/uint128.h
|
||||
src/common/unique_function.h
|
||||
src/common/va_ctx.h
|
||||
src/common/ntapi.h
|
||||
src/common/ntapi.cpp
|
||||
# src/common/number_utils.h
|
||||
# src/common/number_utils.cpp
|
||||
src/common/memory_patcher.h
|
||||
src/common/memory_patcher.cpp
|
||||
src/core/memory_patcher.cpp
|
||||
src/core/memory_patcher.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/src/common/scm_rev.cpp
|
||||
src/common/scm_rev.h
|
||||
)
|
||||
|
||||
if (ENABLE_DISCORD_RPC)
|
||||
list(APPEND COMMON src/common/discord_rpc_handler.cpp src/common/discord_rpc_handler.h)
|
||||
endif()
|
||||
|
||||
set(CORE src/core/file_format/psf.cpp
|
||||
src/core/file_format/psf.h
|
||||
src/core/file_format/trp.cpp
|
||||
src/core/file_format/trp.h
|
||||
src/core/loader/elf.cpp
|
||||
src/core/loader/elf.h
|
||||
src/core/debug_state.cpp
|
||||
src/core/debug_state.h
|
||||
src/core/signals.cpp
|
||||
src/core/signals.h
|
||||
src/core/memory_patcher.cpp
|
||||
src/core/memory_patcher.h
|
||||
)
|
||||
|
||||
set(INPUT src/input/controller.cpp
|
||||
@@ -461,18 +390,13 @@ qt_add_executable(shadPS4QtLauncher
|
||||
|
||||
create_target_directory_groups(shadPS4QtLauncher)
|
||||
|
||||
target_link_libraries(shadPS4QtLauncher PRIVATE magic_enum::magic_enum Vulkan::Headers fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak ZLIB::ZLIB PNG::PNG)
|
||||
target_link_libraries(shadPS4QtLauncher PRIVATE Boost::headers LibAtrac9 xxHash::xxhash Zydis::Zydis SDL3::SDL3 pugixml::pugixml lfreist-hwinfo::hwinfo volk_headers)
|
||||
|
||||
if (ENABLE_DISCORD_RPC)
|
||||
target_compile_definitions(shadPS4QtLauncher PRIVATE ENABLE_DISCORD_RPC)
|
||||
endif()
|
||||
target_link_libraries(shadPS4QtLauncher PRIVATE Vulkan::Headers fmt::fmt toml11::toml11 SDL3::SDL3 pugixml::pugixml volk_headers)
|
||||
|
||||
if (APPLE)
|
||||
# Include MoltenVK, along with an ICD file so it can be found by the system Vulkan loader if used for loading layers.
|
||||
set(MVK_BUNDLE_PATH "Resources/vulkan/icd.d")
|
||||
set_property(TARGET shadPS4QtLauncher APPEND PROPERTY BUILD_RPATH "@executable_path/../${MVK_BUNDLE_PATH}")
|
||||
set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR}/shadps4.app/Contents/${MVK_BUNDLE_PATH})
|
||||
set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR}/shadPS4QtLauncher.app/Contents/${MVK_BUNDLE_PATH})
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${MVK_DST}
|
||||
@@ -494,18 +418,9 @@ if (APPLE)
|
||||
add_custom_target(CopyMoltenVK DEPENDS ${MVK_ICD_DST} ${MVK_DYLIB_DST})
|
||||
add_dependencies(CopyMoltenVK MoltenVK)
|
||||
add_dependencies(shadPS4QtLauncher CopyMoltenVK)
|
||||
|
||||
if (ARCHITECTURE STREQUAL "x86_64")
|
||||
# Reserve system-managed memory space.
|
||||
target_link_options(shadPS4QtLauncher PRIVATE -Wl,-ld_classic,-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(shadPS4QtLauncher PRIVATE date::date-tz epoll-shim)
|
||||
endif()
|
||||
|
||||
target_link_libraries(shadPS4QtLauncher PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network Qt6::Multimedia)
|
||||
add_definitions(-DENABLE_QT_GUI) # todo
|
||||
if (ENABLE_UPDATER)
|
||||
add_definitions(-DENABLE_UPDATER)
|
||||
endif()
|
||||
@@ -551,8 +466,6 @@ if (WIN32)
|
||||
target_sources(shadPS4QtLauncher PRIVATE src/shadps4.rc)
|
||||
endif()
|
||||
|
||||
add_definitions(-DBOOST_ASIO_STANDALONE)
|
||||
|
||||
target_include_directories(shadPS4QtLauncher PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
# embed resources
|
||||
@@ -584,11 +497,6 @@ if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(shadPS4QtLauncher PRIVATE ${OPENSSL_LIBRARIES})
|
||||
endif()
|
||||
|
||||
# Discord RPC
|
||||
if (ENABLE_DISCORD_RPC)
|
||||
target_link_libraries(shadPS4QtLauncher PRIVATE discord-rpc)
|
||||
endif()
|
||||
|
||||
# Install rules
|
||||
install(TARGETS shadPS4QtLauncher BUNDLE DESTINATION .)
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"configurationType": "Release",
|
||||
"buildRoot": "${projectDir}\\Build\\${name}",
|
||||
"installRoot": "${projectDir}\\Install\\${name}",
|
||||
"cmakeCommandArgs": "-DENABLE_QT_GUI=ON",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": "",
|
||||
"inheritEnvironments": [ "clang_cl_x64_x64" ],
|
||||
@@ -42,7 +42,7 @@
|
||||
"configurationType": "Debug",
|
||||
"buildRoot": "${projectDir}\\Build\\${name}",
|
||||
"installRoot": "${projectDir}\\Install\\${name}",
|
||||
"cmakeCommandArgs": "-DENABLE_QT_GUI=ON",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": "",
|
||||
"inheritEnvironments": [ "clang_cl_x64_x64" ],
|
||||
@@ -66,7 +66,7 @@
|
||||
"configurationType": "RelWithDebInfo",
|
||||
"buildRoot": "${projectDir}\\Build\\${name}",
|
||||
"installRoot": "${projectDir}\\Install\\${name}",
|
||||
"cmakeCommandArgs": "-DENABLE_QT_GUI=ON",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": "",
|
||||
"inheritEnvironments": [ "clang_cl_x64_x64" ],
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
||||
30
REUSE.toml
30
REUSE.toml
@@ -5,6 +5,7 @@ path = [
|
||||
"REUSE.toml",
|
||||
"crowdin.yml",
|
||||
"CMakeSettings.json",
|
||||
"CMakeDarwinPresets.json",
|
||||
"CMakeLinuxPresets.json",
|
||||
"CMakeWindowsPresets.json",
|
||||
"CMakePresets.json",
|
||||
@@ -17,6 +18,7 @@ path = [
|
||||
"dist/net.shadps4.shadPS4.desktop",
|
||||
"dist/net.shadps4.shadPS4_metadata.pot",
|
||||
"dist/net.shadps4.shadPS4.metainfo.xml",
|
||||
"dist/qt.conf",
|
||||
"documents/changelog.md",
|
||||
"documents/Quickstart/2.png",
|
||||
"documents/Screenshots/*",
|
||||
@@ -89,35 +91,7 @@ precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2009-2010 Iowa State University"
|
||||
SPDX-License-Identifier = "BSL-1.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "externals/renderdoc/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2019-2024 Baldur Karlsson"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "externals/stb/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2017 Sean Barrett"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "externals/tracy/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2017-2024 Bartosz Taudul <wolf@nereid.pl>"
|
||||
SPDX-License-Identifier = "BSD-3-Clause"
|
||||
|
||||
[[annotations]]
|
||||
path = "externals/gcn/include/**"
|
||||
SPDX-FileCopyrightText = "NONE"
|
||||
SPDX-License-Identifier = "CC0-1.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "cmake/CMakeRC.cmake"
|
||||
SPDX-FileCopyrightText = "Copyright (c) 2017 vector-of-bool <vectorofbool@gmail.com>"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "dist/qt.conf"
|
||||
SPDX-FileCopyrightText = "shadPS4 Emulator Project"
|
||||
SPDX-License-Identifier = "GPL-2.0-or-later"
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_check_modules(FFMPEG QUIET IMPORTED_TARGET libavcodec libavfilter libavformat libavutil libswresample libswscale)
|
||||
|
||||
find_file(FFMPEG_VERSION_FILE libavutil/ffversion.h HINTS "${FFMPEG_libavutil_INCLUDEDIR}")
|
||||
if (FFMPEG_VERSION_FILE)
|
||||
file(STRINGS "${FFMPEG_VERSION_FILE}" FFMPEG_VERSION_LINE REGEX "FFMPEG_VERSION")
|
||||
string(REGEX MATCH "[0-9.]+" FFMPEG_VERSION "${FFMPEG_VERSION_LINE}")
|
||||
unset(FFMPEG_VERSION_LINE)
|
||||
unset(FFMPEG_VERSION_FILE)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(FFmpeg
|
||||
REQUIRED_VARS FFMPEG_LINK_LIBRARIES
|
||||
VERSION_VAR FFMPEG_VERSION
|
||||
)
|
||||
|
||||
if (FFmpeg_FOUND AND NOT TARGET FFmpeg::ffmpeg)
|
||||
add_library(FFmpeg::ffmpeg ALIAS PkgConfig::FFMPEG)
|
||||
endif()
|
||||
@@ -1,25 +0,0 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
find_path(RENDERDOC_INCLUDE_DIR renderdoc_app.h)
|
||||
|
||||
if (RENDERDOC_INCLUDE_DIR AND EXISTS "${RENDERDOC_INCLUDE_DIR}/renderdoc_app.h")
|
||||
file(STRINGS "${RENDERDOC_INCLUDE_DIR}/renderdoc_app.h" RENDERDOC_VERSION_LINE REGEX "typedef struct RENDERDOC_API")
|
||||
string(REGEX REPLACE ".*typedef struct RENDERDOC_API_([0-9]+)_([0-9]+)_([0-9]+).*" "\\1.\\2.\\3" RENDERDOC_VERSION "${RENDERDOC_VERSION_LINE}")
|
||||
unset(RENDERDOC_VERSION_LINE)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(RenderDoc
|
||||
REQUIRED_VARS RENDERDOC_INCLUDE_DIR
|
||||
VERSION_VAR RENDERDOC_VERSION
|
||||
)
|
||||
|
||||
if (RenderDoc_FOUND AND NOT TARGET RenderDoc::API)
|
||||
add_library(RenderDoc::API INTERFACE IMPORTED)
|
||||
set_target_properties(RenderDoc::API PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${RENDERDOC_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(RENDERDOC_INCLUDE_DIR)
|
||||
@@ -1,28 +0,0 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
find_path(half_INCLUDE_DIR NAMES half.hpp PATH_SUFFIXES half)
|
||||
|
||||
if (half_INCLUDE_DIR)
|
||||
file(STRINGS "${half_INCLUDE_DIR}/half.hpp" _ver_line
|
||||
REGEX "^// Version [0-9.]+$"
|
||||
LIMIT_COUNT 1
|
||||
)
|
||||
string(REGEX MATCH "[0-9.]+" half_VERSION "${_ver_line}")
|
||||
unset(_ver_line)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(half
|
||||
REQUIRED_VARS half_INCLUDE_DIR
|
||||
VERSION_VAR half_VERSION
|
||||
)
|
||||
|
||||
if (half_FOUND AND NOT TARGET half::half)
|
||||
add_library(half::half INTERFACE IMPORTED)
|
||||
set_target_properties(half::half PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${half_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(half_INCLUDE_DIR)
|
||||
@@ -1,15 +0,0 @@
|
||||
# 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()
|
||||
@@ -1,19 +0,0 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
find_path(stb_image_INCLUDE_DIR stb_image.h PATH_SUFFIXES stb)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(stb
|
||||
REQUIRED_VARS stb_image_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if (stb_FOUND AND NOT TARGET stb::headers)
|
||||
add_library(stb::headers INTERFACE IMPORTED)
|
||||
set_property(TARGET stb::headers PROPERTY
|
||||
INTERFACE_INCLUDE_DIRECTORIES
|
||||
"${stb_image_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(stb_image_INCLUDE_DIR)
|
||||
@@ -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(XXHASH QUIET IMPORTED_TARGET libxxhash)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(xxHash
|
||||
REQUIRED_VARS XXHASH_LINK_LIBRARIES
|
||||
VERSION_VAR XXHASH_VERSION
|
||||
)
|
||||
|
||||
if (xxHash_FOUND AND NOT TARGET xxHash::xxhash)
|
||||
add_library(xxHash::xxhash ALIAS PkgConfig::XXHASH)
|
||||
endif()
|
||||
6
dist/MacOSBundleInfo.plist.in
vendored
6
dist/MacOSBundleInfo.plist.in
vendored
@@ -8,11 +8,11 @@
|
||||
<string>APPL</string>
|
||||
|
||||
<key>CFBundleName</key>
|
||||
<string>shadps4</string>
|
||||
<string>shadPS4QtLauncher</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.shadps4-emu.shadps4</string>
|
||||
<string>com.shadps4-emu.shadps4-qtlauncher</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>shadps4</string>
|
||||
<string>shadPS4QtLauncher</string>
|
||||
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0.0</string>
|
||||
|
||||
168
externals/CMakeLists.txt
vendored
168
externals/CMakeLists.txt
vendored
@@ -16,46 +16,11 @@ if (MSVC)
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
# Boost
|
||||
if (NOT TARGET Boost::headers)
|
||||
add_subdirectory(ext-boost)
|
||||
endif()
|
||||
|
||||
# fmtlib
|
||||
if (NOT TARGET fmt::fmt)
|
||||
add_subdirectory(fmt)
|
||||
endif()
|
||||
|
||||
# FFmpeg
|
||||
# if (NOT TARGET FFmpeg::ffmpeg)
|
||||
# add_subdirectory(ffmpeg-core)
|
||||
# add_library(FFmpeg::ffmpeg ALIAS ffmpeg)
|
||||
# endif()
|
||||
|
||||
# LibAtrac9
|
||||
file(GLOB LIBATRAC9_SOURCES LibAtrac9/C/src/*.c)
|
||||
add_library(LibAtrac9 STATIC ${LIBATRAC9_SOURCES})
|
||||
target_include_directories(LibAtrac9 INTERFACE LibAtrac9/C/src)
|
||||
|
||||
# zlib
|
||||
if (NOT TARGET ZLIB::ZLIB)
|
||||
set(ZLIB_ENABLE_TESTS OFF)
|
||||
set(WITH_GTEST OFF)
|
||||
set(WITH_NEW_STRATEGIES ON)
|
||||
set(WITH_NATIVE_INSTRUCTIONS ON)
|
||||
set(ZLIB_COMPAT ON CACHE BOOL "" FORCE)
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
ZLIB
|
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/zlib-ng"
|
||||
OVERRIDE_FIND_PACKAGE
|
||||
)
|
||||
FetchContent_MakeAvailable(ZLIB)
|
||||
add_library(ZLIB::ZLIB ALIAS zlib)
|
||||
# libpng expects this variable to exist after its find_package(ZLIB)
|
||||
set(ZLIB_INCLUDE_DIRS "${FETCHCONTENT_BASE_DIR}/zlib-build")
|
||||
endif()
|
||||
|
||||
# SDL3
|
||||
if (NOT TARGET SDL3::SDL3)
|
||||
set(SDL_TEST_LIBRARY OFF)
|
||||
@@ -69,47 +34,6 @@ if (NOT TARGET Vulkan::Headers)
|
||||
add_subdirectory(vulkan-headers)
|
||||
endif()
|
||||
|
||||
# VMA
|
||||
# if (NOT TARGET GPUOpen::VulkanMemoryAllocator)
|
||||
# add_subdirectory(vma)
|
||||
# endif()
|
||||
|
||||
# RenderDoc
|
||||
# if (NOT TARGET RenderDoc::API)
|
||||
# add_library(renderdoc INTERFACE)
|
||||
# target_include_directories(renderdoc INTERFACE ./renderdoc)
|
||||
# add_library(RenderDoc::API ALIAS renderdoc)
|
||||
# endif()
|
||||
|
||||
# glslang
|
||||
# if (NOT TARGET glslang::glslang)
|
||||
# set(SKIP_GLSLANG_INSTALL ON CACHE BOOL "")
|
||||
# set(ENABLE_GLSLANG_BINARIES OFF CACHE BOOL "")
|
||||
# set(ENABLE_SPVREMAPPER OFF CACHE BOOL "")
|
||||
# set(ENABLE_CTEST OFF CACHE BOOL "")
|
||||
# set(ENABLE_HLSL OFF CACHE BOOL "")
|
||||
# set(BUILD_EXTERNAL OFF CACHE BOOL "")
|
||||
# set(ENABLE_OPT OFF CACHE BOOL "")
|
||||
# add_subdirectory(glslang)
|
||||
# file(COPY glslang/SPIRV DESTINATION glslang/glslang FILES_MATCHING PATTERN "*.h")
|
||||
# target_include_directories(glslang INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/glslang")
|
||||
# endif()
|
||||
|
||||
# Robin-map
|
||||
if (NOT TARGET tsl::robin_map)
|
||||
add_subdirectory(robin-map)
|
||||
endif()
|
||||
|
||||
# Xbyak
|
||||
if (NOT TARGET xbyak::xbyak)
|
||||
add_subdirectory(xbyak)
|
||||
endif()
|
||||
|
||||
# MagicEnum
|
||||
if (NOT TARGET magic_enum::magic_enum)
|
||||
add_subdirectory(magic_enum)
|
||||
endif()
|
||||
|
||||
# Toml11
|
||||
if (NOT TARGET toml11::toml11)
|
||||
add_subdirectory(toml11)
|
||||
@@ -123,109 +47,17 @@ if (NOT TARGET toml11::toml11)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# xxHash
|
||||
if (NOT TARGET xxHash::xxhash)
|
||||
add_library(xxhash xxhash/xxhash.h xxhash/xxhash.c)
|
||||
target_include_directories(xxhash PUBLIC xxhash)
|
||||
add_library(xxHash::xxhash ALIAS xxhash)
|
||||
endif()
|
||||
|
||||
# Zydis
|
||||
if (NOT TARGET Zydis::Zydis)
|
||||
option(ZYDIS_BUILD_TOOLS "" OFF)
|
||||
option(ZYDIS_BUILD_EXAMPLES "" OFF)
|
||||
add_subdirectory(zydis)
|
||||
endif()
|
||||
|
||||
# sirit
|
||||
# add_subdirectory(sirit)
|
||||
# if (WIN32)
|
||||
# target_compile_options(sirit PUBLIC "-Wno-error=unused-command-line-argument")
|
||||
# endif()
|
||||
|
||||
# half
|
||||
# if (NOT TARGET half::half)
|
||||
# add_library(half INTERFACE)
|
||||
# target_include_directories(half INTERFACE half/include)
|
||||
# add_library(half::half ALIAS half)
|
||||
# endif()
|
||||
|
||||
# libpng
|
||||
if (NOT TARGET PNG::PNG)
|
||||
set(PNG_SHARED OFF CACHE BOOL "" FORCE)
|
||||
set(PNG_STATIC ON CACHE BOOL "" FORCE)
|
||||
set(PNG_TESTS OFF CACHE BOOL "" FORCE)
|
||||
set(PNG_TOOLS OFF CACHE BOOL "" FORCE)
|
||||
set(SKIP_INSTALL_ALL OFF CACHE BOOL "" FORCE)
|
||||
add_subdirectory(libpng)
|
||||
add_library(PNG::PNG ALIAS png_static)
|
||||
endif()
|
||||
|
||||
# Tracy
|
||||
# if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
# option(TRACY_ENABLE "" OFF)
|
||||
# else()
|
||||
# option(TRACY_ENABLE "" ON)
|
||||
# endif()
|
||||
# option(TRACY_NO_CRASH_HANDLER "" ON) # Otherwise texture cache exceptions will be treaten as a crash
|
||||
# option(TRACY_ON_DEMAND "" ON)
|
||||
# option(TRACY_NO_FRAME_IMAGE "" ON)
|
||||
# option(TRACY_FIBERS "" OFF) # For AmdGpu frontend profiling, disabled due to instability
|
||||
# option(TRACY_NO_SYSTEM_TRACING "" ON)
|
||||
# option(TRACY_NO_CALLSTACK "" ON)
|
||||
# option(TRACY_NO_CODE_TRANSFER "" ON)
|
||||
# option(TRACY_NO_SAMPLING "" ON)
|
||||
# option(TRACY_ONLY_LOCALHOST "" ON)
|
||||
# option(TRACY_NO_CONTEXT_SWITCH "" ON)
|
||||
# add_subdirectory(tracy)
|
||||
|
||||
# pugixml
|
||||
if (NOT TARGET pugixml::pugixml)
|
||||
add_subdirectory(pugixml)
|
||||
endif()
|
||||
|
||||
# libusb
|
||||
# if (NOT TARGET libusb::usb)
|
||||
# add_subdirectory(ext-libusb)
|
||||
# add_library(libusb::usb ALIAS usb-1.0)
|
||||
# endif()
|
||||
|
||||
# Discord RPC
|
||||
if (ENABLE_DISCORD_RPC)
|
||||
add_subdirectory(discord-rpc)
|
||||
endif()
|
||||
|
||||
# GCN Headers
|
||||
# add_subdirectory(gcn)
|
||||
|
||||
# stb
|
||||
# if (NOT TARGET stb::headers)
|
||||
# add_library(stb INTERFACE)
|
||||
# target_include_directories(stb INTERFACE stb)
|
||||
# add_library(stb::headers ALIAS stb)
|
||||
# endif()
|
||||
|
||||
# hwinfo
|
||||
set(HWINFO_STATIC ON)
|
||||
add_subdirectory(hwinfo)
|
||||
|
||||
# Apple-only dependencies
|
||||
if (APPLE)
|
||||
# date
|
||||
if (NOT TARGET date::date-tz)
|
||||
option(BUILD_TZ_LIB "" ON)
|
||||
option(USE_SYSTEM_TZ_DB "" ON)
|
||||
add_subdirectory(date)
|
||||
endif()
|
||||
|
||||
# MoltenVK
|
||||
if (NOT TARGET MoltenVK)
|
||||
add_subdirectory(MoltenVK)
|
||||
endif()
|
||||
|
||||
if (NOT TARGET epoll-shim)
|
||||
add_subdirectory(epoll-shim)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_subdirectory(volk)
|
||||
1
externals/LibAtrac9
vendored
1
externals/LibAtrac9
vendored
Submodule externals/LibAtrac9 deleted from ec8899dadf
12
externals/MoltenVK/CMakeLists.txt
vendored
12
externals/MoltenVK/CMakeLists.txt
vendored
@@ -33,14 +33,6 @@ find_library(IOSURFACE_LIBRARY IOSurface REQUIRED)
|
||||
find_library(METAL_LIBRARY Metal REQUIRED)
|
||||
find_library(QUARTZCORE_LIBRARY QuartzCore REQUIRED)
|
||||
|
||||
# cereal
|
||||
option(SKIP_PORTABILITY_TEST "" ON)
|
||||
option(BUILD_DOC "" OFF)
|
||||
option(BUILD_SANDBOX "" OFF)
|
||||
option(SKIP_PERFORMANCE_COMPARISON "" ON)
|
||||
option(SPIRV_CROSS_SKIP_INSTALL "" ON)
|
||||
add_subdirectory(cereal)
|
||||
|
||||
# SPIRV-Cross
|
||||
option(SPIRV_CROSS_CLI "" OFF)
|
||||
option(SPIRV_CROSS_ENABLE_TESTS "" OFF)
|
||||
@@ -89,5 +81,5 @@ target_include_directories(MoltenVK PRIVATE ${MVK_INCLUDES})
|
||||
target_compile_options(MoltenVK PRIVATE -w)
|
||||
target_link_libraries(MoltenVK PRIVATE
|
||||
${APPKIT_LIBRARY} ${FOUNDATION_LIBRARY} ${IOKIT_LIBRARY} ${IOSURFACE_LIBRARY} ${METAL_LIBRARY} ${QUARTZCORE_LIBRARY}
|
||||
Vulkan::Headers cereal::cereal spirv-cross-msl MoltenVKCommon MoltenVKShaderConverter)
|
||||
target_compile_definitions(MoltenVK PRIVATE MVK_FRAMEWORK_VERSION=${MVK_VERSION} MVK_USE_METAL_PRIVATE_API=1)
|
||||
Vulkan::Headers spirv-cross-msl MoltenVKCommon MoltenVKShaderConverter)
|
||||
target_compile_definitions(MoltenVK PRIVATE MVK_FRAMEWORK_VERSION=${MVK_VERSION} MVK_USE_METAL_PRIVATE_API=1 MVK_USE_CEREAL=0)
|
||||
|
||||
1
externals/MoltenVK/cereal
vendored
1
externals/MoltenVK/cereal
vendored
Submodule externals/MoltenVK/cereal deleted from a56bad8bbb
1
externals/date
vendored
1
externals/date
vendored
Submodule externals/date deleted from a45ea7c17b
1
externals/discord-rpc
vendored
1
externals/discord-rpc
vendored
Submodule externals/discord-rpc deleted from 19f66e6dca
1
externals/epoll-shim
vendored
1
externals/epoll-shim
vendored
Submodule externals/epoll-shim deleted from 18159584bb
1
externals/ext-boost
vendored
1
externals/ext-boost
vendored
Submodule externals/ext-boost deleted from ca6f230e67
1
externals/ffmpeg-core
vendored
1
externals/ffmpeg-core
vendored
Submodule externals/ffmpeg-core deleted from b0de1dcca2
12
externals/gcn/CMakeLists.txt
vendored
12
externals/gcn/CMakeLists.txt
vendored
@@ -1,12 +0,0 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
project(gcn LANGUAGES CXX)
|
||||
|
||||
add_library(gcn INTERFACE)
|
||||
target_sources(gcn PRIVATE
|
||||
"include/gcn/si_ci_vi_merged_offset.h"
|
||||
"include/gcn/si_ci_vi_merged_pm4_it_opcodes.h"
|
||||
)
|
||||
|
||||
target_include_directories(gcn INTERFACE include)
|
||||
15339
externals/gcn/include/gcn/si_ci_vi_merged_offset.h
vendored
15339
externals/gcn/include/gcn/si_ci_vi_merged_offset.h
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2015-2021 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
|
||||
#ifndef PM4_IT_OPCODES_H
|
||||
#define PM4_IT_OPCODES_H
|
||||
|
||||
enum IT_OpCodeType {
|
||||
IT_NOP = 0x10,
|
||||
IT_SET_BASE = 0x11,
|
||||
IT_CLEAR_STATE = 0x12,
|
||||
IT_INDEX_BUFFER_SIZE = 0x13,
|
||||
IT_DISPATCH_DIRECT = 0x15,
|
||||
IT_DISPATCH_INDIRECT = 0x16,
|
||||
IT_ATOMIC_GDS = 0x1D,
|
||||
IT_ATOMIC = 0x1E,
|
||||
IT_OCCLUSION_QUERY = 0x1F,
|
||||
IT_SET_PREDICATION = 0x20,
|
||||
IT_REG_RMW = 0x21,
|
||||
IT_COND_EXEC = 0x22,
|
||||
IT_PRED_EXEC = 0x23,
|
||||
IT_DRAW_INDIRECT = 0x24,
|
||||
IT_DRAW_INDEX_INDIRECT = 0x25,
|
||||
IT_INDEX_BASE = 0x26,
|
||||
IT_DRAW_INDEX_2 = 0x27,
|
||||
IT_CONTEXT_CONTROL = 0x28,
|
||||
IT_INDEX_TYPE = 0x2A,
|
||||
IT_DRAW_INDIRECT_MULTI = 0x2C,
|
||||
IT_DRAW_INDEX_AUTO = 0x2D,
|
||||
IT_NUM_INSTANCES = 0x2F,
|
||||
IT_DRAW_INDEX_MULTI_AUTO = 0x30,
|
||||
IT_INDIRECT_BUFFER_CNST = 0x33,
|
||||
IT_STRMOUT_BUFFER_UPDATE = 0x34,
|
||||
IT_DRAW_INDEX_OFFSET_2 = 0x35,
|
||||
IT_WRITE_DATA = 0x37,
|
||||
IT_DRAW_INDEX_INDIRECT_MULTI = 0x38,
|
||||
IT_MEM_SEMAPHORE = 0x39,
|
||||
IT_COPY_DW__SI__CI = 0x3B,
|
||||
IT_WAIT_REG_MEM = 0x3C,
|
||||
IT_INDIRECT_BUFFER = 0x3F,
|
||||
IT_COND_INDIRECT_BUFFER = 0x3F,
|
||||
IT_COPY_DATA = 0x40,
|
||||
IT_CP_DMA = 0x41,
|
||||
IT_PFP_SYNC_ME = 0x42,
|
||||
IT_SURFACE_SYNC = 0x43,
|
||||
IT_COND_WRITE = 0x45,
|
||||
IT_EVENT_WRITE = 0x46,
|
||||
IT_EVENT_WRITE_EOP = 0x47,
|
||||
IT_EVENT_WRITE_EOS = 0x48,
|
||||
IT_PREAMBLE_CNTL = 0x4A,
|
||||
IT_CONTEXT_REG_RMW = 0x51,
|
||||
IT_LOAD_SH_REG = 0x5F,
|
||||
IT_LOAD_CONFIG_REG = 0x60,
|
||||
IT_LOAD_CONTEXT_REG = 0x61,
|
||||
IT_SET_CONFIG_REG = 0x68,
|
||||
IT_SET_CONTEXT_REG = 0x69,
|
||||
IT_SET_CONTEXT_REG_INDIRECT = 0x73,
|
||||
IT_SET_SH_REG = 0x76,
|
||||
IT_SET_SH_REG_OFFSET = 0x77,
|
||||
IT_SCRATCH_RAM_WRITE = 0x7D,
|
||||
IT_SCRATCH_RAM_READ = 0x7E,
|
||||
IT_LOAD_CONST_RAM = 0x80,
|
||||
IT_WRITE_CONST_RAM = 0x81,
|
||||
IT_DUMP_CONST_RAM = 0x83,
|
||||
IT_INCREMENT_CE_COUNTER = 0x84,
|
||||
IT_INCREMENT_DE_COUNTER = 0x85,
|
||||
IT_WAIT_ON_CE_COUNTER = 0x86,
|
||||
IT_WAIT_ON_DE_COUNTER__SI = 0x87,
|
||||
IT_WAIT_ON_DE_COUNTER_DIFF = 0x88,
|
||||
IT_SWITCH_BUFFER = 0x8B,
|
||||
IT_DRAW_PREAMBLE__CI__VI = 0x36,
|
||||
IT_RELEASE_MEM__CI__VI = 0x49,
|
||||
IT_DMA_DATA__CI__VI = 0x50,
|
||||
IT_ACQUIRE_MEM__CI__VI = 0x58,
|
||||
IT_REWIND__CI__VI = 0x59,
|
||||
IT_LOAD_UCONFIG_REG__CI__VI = 0x5E,
|
||||
IT_SET_QUEUE_REG__CI__VI = 0x78,
|
||||
IT_SET_UCONFIG_REG__CI__VI = 0x79,
|
||||
IT_INDEX_ATTRIBUTES_INDIRECT__CI__VI = 0x91,
|
||||
IT_SET_SH_REG_INDEX__CI__VI = 0x9B,
|
||||
IT_SET_RESOURCES__CI__VI = 0xA0,
|
||||
IT_MAP_PROCESS__CI__VI = 0xA1,
|
||||
IT_MAP_QUEUES__CI__VI = 0xA2,
|
||||
IT_UNMAP_QUEUES__CI__VI = 0xA3,
|
||||
IT_QUERY_STATUS__CI__VI = 0xA4,
|
||||
IT_RUN_LIST__CI__VI = 0xA5,
|
||||
IT_LOAD_SH_REG_INDEX__VI = 0x63,
|
||||
IT_LOAD_CONTEXT_REG_INDEX__VI = 0x9F,
|
||||
IT_DUMP_CONST_RAM_OFFSET__VI = 0x9E,
|
||||
};
|
||||
|
||||
#define PM4_TYPE_0 0
|
||||
#define PM4_TYPE_2 2
|
||||
#define PM4_TYPE_3 3
|
||||
|
||||
#endif
|
||||
1
externals/hwinfo
vendored
1
externals/hwinfo
vendored
Submodule externals/hwinfo deleted from 351c59828a
1
externals/libpng
vendored
1
externals/libpng
vendored
Submodule externals/libpng deleted from c1cc0f3f4c
1
externals/magic_enum
vendored
1
externals/magic_enum
vendored
Submodule externals/magic_enum deleted from a413fcc9c4
741
externals/renderdoc/renderdoc_app.h
vendored
741
externals/renderdoc/renderdoc_app.h
vendored
@@ -1,741 +0,0 @@
|
||||
/******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019-2024 Baldur Karlsson
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Documentation for the API is available at https://renderdoc.org/docs/in_application_api.html
|
||||
//
|
||||
|
||||
#if !defined(RENDERDOC_NO_STDINT)
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
|
||||
#define RENDERDOC_CC __cdecl
|
||||
#elif defined(__linux__) || defined(__FreeBSD__)
|
||||
#define RENDERDOC_CC
|
||||
#elif defined(__APPLE__)
|
||||
#define RENDERDOC_CC
|
||||
#else
|
||||
#error "Unknown platform"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constants not used directly in below API
|
||||
|
||||
// This is a GUID/magic value used for when applications pass a path where shader debug
|
||||
// information can be found to match up with a stripped shader.
|
||||
// the define can be used like so: const GUID RENDERDOC_ShaderDebugMagicValue =
|
||||
// RENDERDOC_ShaderDebugMagicValue_value
|
||||
#define RENDERDOC_ShaderDebugMagicValue_struct \
|
||||
{ \
|
||||
0xeab25520, 0x6670, 0x4865, 0x84, 0x29, 0x6c, 0x8, 0x51, 0x54, 0x00, 0xff \
|
||||
}
|
||||
|
||||
// as an alternative when you want a byte array (assuming x86 endianness):
|
||||
#define RENDERDOC_ShaderDebugMagicValue_bytearray \
|
||||
{ \
|
||||
0x20, 0x55, 0xb2, 0xea, 0x70, 0x66, 0x65, 0x48, 0x84, 0x29, 0x6c, 0x8, 0x51, 0x54, 0x00, 0xff \
|
||||
}
|
||||
|
||||
// truncated version when only a uint64_t is available (e.g. Vulkan tags):
|
||||
#define RENDERDOC_ShaderDebugMagicValue_truncated 0x48656670eab25520ULL
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// RenderDoc capture options
|
||||
//
|
||||
|
||||
typedef enum RENDERDOC_CaptureOption
|
||||
{
|
||||
// Allow the application to enable vsync
|
||||
//
|
||||
// Default - enabled
|
||||
//
|
||||
// 1 - The application can enable or disable vsync at will
|
||||
// 0 - vsync is force disabled
|
||||
eRENDERDOC_Option_AllowVSync = 0,
|
||||
|
||||
// Allow the application to enable fullscreen
|
||||
//
|
||||
// Default - enabled
|
||||
//
|
||||
// 1 - The application can enable or disable fullscreen at will
|
||||
// 0 - fullscreen is force disabled
|
||||
eRENDERDOC_Option_AllowFullscreen = 1,
|
||||
|
||||
// Record API debugging events and messages
|
||||
//
|
||||
// Default - disabled
|
||||
//
|
||||
// 1 - Enable built-in API debugging features and records the results into
|
||||
// the capture, which is matched up with events on replay
|
||||
// 0 - no API debugging is forcibly enabled
|
||||
eRENDERDOC_Option_APIValidation = 2,
|
||||
eRENDERDOC_Option_DebugDeviceMode = 2, // deprecated name of this enum
|
||||
|
||||
// Capture CPU callstacks for API events
|
||||
//
|
||||
// Default - disabled
|
||||
//
|
||||
// 1 - Enables capturing of callstacks
|
||||
// 0 - no callstacks are captured
|
||||
eRENDERDOC_Option_CaptureCallstacks = 3,
|
||||
|
||||
// When capturing CPU callstacks, only capture them from actions.
|
||||
// This option does nothing without the above option being enabled
|
||||
//
|
||||
// Default - disabled
|
||||
//
|
||||
// 1 - Only captures callstacks for actions.
|
||||
// Ignored if CaptureCallstacks is disabled
|
||||
// 0 - Callstacks, if enabled, are captured for every event.
|
||||
eRENDERDOC_Option_CaptureCallstacksOnlyDraws = 4,
|
||||
eRENDERDOC_Option_CaptureCallstacksOnlyActions = 4,
|
||||
|
||||
// Specify a delay in seconds to wait for a debugger to attach, after
|
||||
// creating or injecting into a process, before continuing to allow it to run.
|
||||
//
|
||||
// 0 indicates no delay, and the process will run immediately after injection
|
||||
//
|
||||
// Default - 0 seconds
|
||||
//
|
||||
eRENDERDOC_Option_DelayForDebugger = 5,
|
||||
|
||||
// Verify buffer access. This includes checking the memory returned by a Map() call to
|
||||
// detect any out-of-bounds modification, as well as initialising buffers with undefined contents
|
||||
// to a marker value to catch use of uninitialised memory.
|
||||
//
|
||||
// NOTE: This option is only valid for OpenGL and D3D11. Explicit APIs such as D3D12 and Vulkan do
|
||||
// not do the same kind of interception & checking and undefined contents are really undefined.
|
||||
//
|
||||
// Default - disabled
|
||||
//
|
||||
// 1 - Verify buffer access
|
||||
// 0 - No verification is performed, and overwriting bounds may cause crashes or corruption in
|
||||
// RenderDoc.
|
||||
eRENDERDOC_Option_VerifyBufferAccess = 6,
|
||||
|
||||
// The old name for eRENDERDOC_Option_VerifyBufferAccess was eRENDERDOC_Option_VerifyMapWrites.
|
||||
// This option now controls the filling of uninitialised buffers with 0xdddddddd which was
|
||||
// previously always enabled
|
||||
eRENDERDOC_Option_VerifyMapWrites = eRENDERDOC_Option_VerifyBufferAccess,
|
||||
|
||||
// Hooks any system API calls that create child processes, and injects
|
||||
// RenderDoc into them recursively with the same options.
|
||||
//
|
||||
// Default - disabled
|
||||
//
|
||||
// 1 - Hooks into spawned child processes
|
||||
// 0 - Child processes are not hooked by RenderDoc
|
||||
eRENDERDOC_Option_HookIntoChildren = 7,
|
||||
|
||||
// By default RenderDoc only includes resources in the final capture necessary
|
||||
// for that frame, this allows you to override that behaviour.
|
||||
//
|
||||
// Default - disabled
|
||||
//
|
||||
// 1 - all live resources at the time of capture are included in the capture
|
||||
// and available for inspection
|
||||
// 0 - only the resources referenced by the captured frame are included
|
||||
eRENDERDOC_Option_RefAllResources = 8,
|
||||
|
||||
// **NOTE**: As of RenderDoc v1.1 this option has been deprecated. Setting or
|
||||
// getting it will be ignored, to allow compatibility with older versions.
|
||||
// In v1.1 the option acts as if it's always enabled.
|
||||
//
|
||||
// By default RenderDoc skips saving initial states for resources where the
|
||||
// previous contents don't appear to be used, assuming that writes before
|
||||
// reads indicate previous contents aren't used.
|
||||
//
|
||||
// Default - disabled
|
||||
//
|
||||
// 1 - initial contents at the start of each captured frame are saved, even if
|
||||
// they are later overwritten or cleared before being used.
|
||||
// 0 - unless a read is detected, initial contents will not be saved and will
|
||||
// appear as black or empty data.
|
||||
eRENDERDOC_Option_SaveAllInitials = 9,
|
||||
|
||||
// In APIs that allow for the recording of command lists to be replayed later,
|
||||
// RenderDoc may choose to not capture command lists before a frame capture is
|
||||
// triggered, to reduce overheads. This means any command lists recorded once
|
||||
// and replayed many times will not be available and may cause a failure to
|
||||
// capture.
|
||||
//
|
||||
// NOTE: This is only true for APIs where multithreading is difficult or
|
||||
// discouraged. Newer APIs like Vulkan and D3D12 will ignore this option
|
||||
// and always capture all command lists since the API is heavily oriented
|
||||
// around it and the overheads have been reduced by API design.
|
||||
//
|
||||
// 1 - All command lists are captured from the start of the application
|
||||
// 0 - Command lists are only captured if their recording begins during
|
||||
// the period when a frame capture is in progress.
|
||||
eRENDERDOC_Option_CaptureAllCmdLists = 10,
|
||||
|
||||
// Mute API debugging output when the API validation mode option is enabled
|
||||
//
|
||||
// Default - enabled
|
||||
//
|
||||
// 1 - Mute any API debug messages from being displayed or passed through
|
||||
// 0 - API debugging is displayed as normal
|
||||
eRENDERDOC_Option_DebugOutputMute = 11,
|
||||
|
||||
// Option to allow vendor extensions to be used even when they may be
|
||||
// incompatible with RenderDoc and cause corrupted replays or crashes.
|
||||
//
|
||||
// Default - inactive
|
||||
//
|
||||
// No values are documented, this option should only be used when absolutely
|
||||
// necessary as directed by a RenderDoc developer.
|
||||
eRENDERDOC_Option_AllowUnsupportedVendorExtensions = 12,
|
||||
|
||||
// Define a soft memory limit which some APIs may aim to keep overhead under where
|
||||
// possible. Anything above this limit will where possible be saved directly to disk during
|
||||
// capture.
|
||||
// This will cause increased disk space use (which may cause a capture to fail if disk space is
|
||||
// exhausted) as well as slower capture times.
|
||||
//
|
||||
// Not all memory allocations may be deferred like this so it is not a guarantee of a memory
|
||||
// limit.
|
||||
//
|
||||
// Units are in MBs, suggested values would range from 200MB to 1000MB.
|
||||
//
|
||||
// Default - 0 Megabytes
|
||||
eRENDERDOC_Option_SoftMemoryLimit = 13,
|
||||
} RENDERDOC_CaptureOption;
|
||||
|
||||
// Sets an option that controls how RenderDoc behaves on capture.
|
||||
//
|
||||
// Returns 1 if the option and value are valid
|
||||
// Returns 0 if either is invalid and the option is unchanged
|
||||
typedef int(RENDERDOC_CC *pRENDERDOC_SetCaptureOptionU32)(RENDERDOC_CaptureOption opt, uint32_t val);
|
||||
typedef int(RENDERDOC_CC *pRENDERDOC_SetCaptureOptionF32)(RENDERDOC_CaptureOption opt, float val);
|
||||
|
||||
// Gets the current value of an option as a uint32_t
|
||||
//
|
||||
// If the option is invalid, 0xffffffff is returned
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetCaptureOptionU32)(RENDERDOC_CaptureOption opt);
|
||||
|
||||
// Gets the current value of an option as a float
|
||||
//
|
||||
// If the option is invalid, -FLT_MAX is returned
|
||||
typedef float(RENDERDOC_CC *pRENDERDOC_GetCaptureOptionF32)(RENDERDOC_CaptureOption opt);
|
||||
|
||||
typedef enum RENDERDOC_InputButton
|
||||
{
|
||||
// '0' - '9' matches ASCII values
|
||||
eRENDERDOC_Key_0 = 0x30,
|
||||
eRENDERDOC_Key_1 = 0x31,
|
||||
eRENDERDOC_Key_2 = 0x32,
|
||||
eRENDERDOC_Key_3 = 0x33,
|
||||
eRENDERDOC_Key_4 = 0x34,
|
||||
eRENDERDOC_Key_5 = 0x35,
|
||||
eRENDERDOC_Key_6 = 0x36,
|
||||
eRENDERDOC_Key_7 = 0x37,
|
||||
eRENDERDOC_Key_8 = 0x38,
|
||||
eRENDERDOC_Key_9 = 0x39,
|
||||
|
||||
// 'A' - 'Z' matches ASCII values
|
||||
eRENDERDOC_Key_A = 0x41,
|
||||
eRENDERDOC_Key_B = 0x42,
|
||||
eRENDERDOC_Key_C = 0x43,
|
||||
eRENDERDOC_Key_D = 0x44,
|
||||
eRENDERDOC_Key_E = 0x45,
|
||||
eRENDERDOC_Key_F = 0x46,
|
||||
eRENDERDOC_Key_G = 0x47,
|
||||
eRENDERDOC_Key_H = 0x48,
|
||||
eRENDERDOC_Key_I = 0x49,
|
||||
eRENDERDOC_Key_J = 0x4A,
|
||||
eRENDERDOC_Key_K = 0x4B,
|
||||
eRENDERDOC_Key_L = 0x4C,
|
||||
eRENDERDOC_Key_M = 0x4D,
|
||||
eRENDERDOC_Key_N = 0x4E,
|
||||
eRENDERDOC_Key_O = 0x4F,
|
||||
eRENDERDOC_Key_P = 0x50,
|
||||
eRENDERDOC_Key_Q = 0x51,
|
||||
eRENDERDOC_Key_R = 0x52,
|
||||
eRENDERDOC_Key_S = 0x53,
|
||||
eRENDERDOC_Key_T = 0x54,
|
||||
eRENDERDOC_Key_U = 0x55,
|
||||
eRENDERDOC_Key_V = 0x56,
|
||||
eRENDERDOC_Key_W = 0x57,
|
||||
eRENDERDOC_Key_X = 0x58,
|
||||
eRENDERDOC_Key_Y = 0x59,
|
||||
eRENDERDOC_Key_Z = 0x5A,
|
||||
|
||||
// leave the rest of the ASCII range free
|
||||
// in case we want to use it later
|
||||
eRENDERDOC_Key_NonPrintable = 0x100,
|
||||
|
||||
eRENDERDOC_Key_Divide,
|
||||
eRENDERDOC_Key_Multiply,
|
||||
eRENDERDOC_Key_Subtract,
|
||||
eRENDERDOC_Key_Plus,
|
||||
|
||||
eRENDERDOC_Key_F1,
|
||||
eRENDERDOC_Key_F2,
|
||||
eRENDERDOC_Key_F3,
|
||||
eRENDERDOC_Key_F4,
|
||||
eRENDERDOC_Key_F5,
|
||||
eRENDERDOC_Key_F6,
|
||||
eRENDERDOC_Key_F7,
|
||||
eRENDERDOC_Key_F8,
|
||||
eRENDERDOC_Key_F9,
|
||||
eRENDERDOC_Key_F10,
|
||||
eRENDERDOC_Key_F11,
|
||||
eRENDERDOC_Key_F12,
|
||||
|
||||
eRENDERDOC_Key_Home,
|
||||
eRENDERDOC_Key_End,
|
||||
eRENDERDOC_Key_Insert,
|
||||
eRENDERDOC_Key_Delete,
|
||||
eRENDERDOC_Key_PageUp,
|
||||
eRENDERDOC_Key_PageDn,
|
||||
|
||||
eRENDERDOC_Key_Backspace,
|
||||
eRENDERDOC_Key_Tab,
|
||||
eRENDERDOC_Key_PrtScrn,
|
||||
eRENDERDOC_Key_Pause,
|
||||
|
||||
eRENDERDOC_Key_Max,
|
||||
} RENDERDOC_InputButton;
|
||||
|
||||
// Sets which key or keys can be used to toggle focus between multiple windows
|
||||
//
|
||||
// If keys is NULL or num is 0, toggle keys will be disabled
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_SetFocusToggleKeys)(RENDERDOC_InputButton *keys, int num);
|
||||
|
||||
// Sets which key or keys can be used to capture the next frame
|
||||
//
|
||||
// If keys is NULL or num is 0, captures keys will be disabled
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureKeys)(RENDERDOC_InputButton *keys, int num);
|
||||
|
||||
typedef enum RENDERDOC_OverlayBits
|
||||
{
|
||||
// This single bit controls whether the overlay is enabled or disabled globally
|
||||
eRENDERDOC_Overlay_Enabled = 0x1,
|
||||
|
||||
// Show the average framerate over several seconds as well as min/max
|
||||
eRENDERDOC_Overlay_FrameRate = 0x2,
|
||||
|
||||
// Show the current frame number
|
||||
eRENDERDOC_Overlay_FrameNumber = 0x4,
|
||||
|
||||
// Show a list of recent captures, and how many captures have been made
|
||||
eRENDERDOC_Overlay_CaptureList = 0x8,
|
||||
|
||||
// Default values for the overlay mask
|
||||
eRENDERDOC_Overlay_Default = (eRENDERDOC_Overlay_Enabled | eRENDERDOC_Overlay_FrameRate |
|
||||
eRENDERDOC_Overlay_FrameNumber | eRENDERDOC_Overlay_CaptureList),
|
||||
|
||||
// Enable all bits
|
||||
eRENDERDOC_Overlay_All = ~0U,
|
||||
|
||||
// Disable all bits
|
||||
eRENDERDOC_Overlay_None = 0,
|
||||
} RENDERDOC_OverlayBits;
|
||||
|
||||
// returns the overlay bits that have been set
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetOverlayBits)();
|
||||
// sets the overlay bits with an and & or mask
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_MaskOverlayBits)(uint32_t And, uint32_t Or);
|
||||
|
||||
// this function will attempt to remove RenderDoc's hooks in the application.
|
||||
//
|
||||
// Note: that this can only work correctly if done immediately after
|
||||
// the module is loaded, before any API work happens. RenderDoc will remove its
|
||||
// injected hooks and shut down. Behaviour is undefined if this is called
|
||||
// after any API functions have been called, and there is still no guarantee of
|
||||
// success.
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_RemoveHooks)();
|
||||
|
||||
// DEPRECATED: compatibility for code compiled against pre-1.4.1 headers.
|
||||
typedef pRENDERDOC_RemoveHooks pRENDERDOC_Shutdown;
|
||||
|
||||
// This function will unload RenderDoc's crash handler.
|
||||
//
|
||||
// If you use your own crash handler and don't want RenderDoc's handler to
|
||||
// intercede, you can call this function to unload it and any unhandled
|
||||
// exceptions will pass to the next handler.
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_UnloadCrashHandler)();
|
||||
|
||||
// Sets the capture file path template
|
||||
//
|
||||
// pathtemplate is a UTF-8 string that gives a template for how captures will be named
|
||||
// and where they will be saved.
|
||||
//
|
||||
// Any extension is stripped off the path, and captures are saved in the directory
|
||||
// specified, and named with the filename and the frame number appended. If the
|
||||
// directory does not exist it will be created, including any parent directories.
|
||||
//
|
||||
// If pathtemplate is NULL, the template will remain unchanged
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// SetCaptureFilePathTemplate("my_captures/example");
|
||||
//
|
||||
// Capture #1 -> my_captures/example_frame123.rdc
|
||||
// Capture #2 -> my_captures/example_frame456.rdc
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureFilePathTemplate)(const char *pathtemplate);
|
||||
|
||||
// returns the current capture path template, see SetCaptureFileTemplate above, as a UTF-8 string
|
||||
typedef const char *(RENDERDOC_CC *pRENDERDOC_GetCaptureFilePathTemplate)();
|
||||
|
||||
// DEPRECATED: compatibility for code compiled against pre-1.1.2 headers.
|
||||
typedef pRENDERDOC_SetCaptureFilePathTemplate pRENDERDOC_SetLogFilePathTemplate;
|
||||
typedef pRENDERDOC_GetCaptureFilePathTemplate pRENDERDOC_GetLogFilePathTemplate;
|
||||
|
||||
// returns the number of captures that have been made
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetNumCaptures)();
|
||||
|
||||
// This function returns the details of a capture, by index. New captures are added
|
||||
// to the end of the list.
|
||||
//
|
||||
// filename will be filled with the absolute path to the capture file, as a UTF-8 string
|
||||
// pathlength will be written with the length in bytes of the filename string
|
||||
// timestamp will be written with the time of the capture, in seconds since the Unix epoch
|
||||
//
|
||||
// Any of the parameters can be NULL and they'll be skipped.
|
||||
//
|
||||
// The function will return 1 if the capture index is valid, or 0 if the index is invalid
|
||||
// If the index is invalid, the values will be unchanged
|
||||
//
|
||||
// Note: when captures are deleted in the UI they will remain in this list, so the
|
||||
// capture path may not exist anymore.
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetCapture)(uint32_t idx, char *filename,
|
||||
uint32_t *pathlength, uint64_t *timestamp);
|
||||
|
||||
// Sets the comments associated with a capture file. These comments are displayed in the
|
||||
// UI program when opening.
|
||||
//
|
||||
// filePath should be a path to the capture file to add comments to. If set to NULL or ""
|
||||
// the most recent capture file created made will be used instead.
|
||||
// comments should be a NULL-terminated UTF-8 string to add as comments.
|
||||
//
|
||||
// Any existing comments will be overwritten.
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureFileComments)(const char *filePath,
|
||||
const char *comments);
|
||||
|
||||
// returns 1 if the RenderDoc UI is connected to this application, 0 otherwise
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_IsTargetControlConnected)();
|
||||
|
||||
// DEPRECATED: compatibility for code compiled against pre-1.1.1 headers.
|
||||
// This was renamed to IsTargetControlConnected in API 1.1.1, the old typedef is kept here for
|
||||
// backwards compatibility with old code, it is castable either way since it's ABI compatible
|
||||
// as the same function pointer type.
|
||||
typedef pRENDERDOC_IsTargetControlConnected pRENDERDOC_IsRemoteAccessConnected;
|
||||
|
||||
// This function will launch the Replay UI associated with the RenderDoc library injected
|
||||
// into the running application.
|
||||
//
|
||||
// if connectTargetControl is 1, the Replay UI will be launched with a command line parameter
|
||||
// to connect to this application
|
||||
// cmdline is the rest of the command line, as a UTF-8 string. E.g. a captures to open
|
||||
// if cmdline is NULL, the command line will be empty.
|
||||
//
|
||||
// returns the PID of the replay UI if successful, 0 if not successful.
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_LaunchReplayUI)(uint32_t connectTargetControl,
|
||||
const char *cmdline);
|
||||
|
||||
// RenderDoc can return a higher version than requested if it's backwards compatible,
|
||||
// this function returns the actual version returned. If a parameter is NULL, it will be
|
||||
// ignored and the others will be filled out.
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_GetAPIVersion)(int *major, int *minor, int *patch);
|
||||
|
||||
// Requests that the replay UI show itself (if hidden or not the current top window). This can be
|
||||
// used in conjunction with IsTargetControlConnected and LaunchReplayUI to intelligently handle
|
||||
// showing the UI after making a capture.
|
||||
//
|
||||
// This will return 1 if the request was successfully passed on, though it's not guaranteed that
|
||||
// the UI will be on top in all cases depending on OS rules. It will return 0 if there is no current
|
||||
// target control connection to make such a request, or if there was another error
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_ShowReplayUI)();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Capturing functions
|
||||
//
|
||||
|
||||
// A device pointer is a pointer to the API's root handle.
|
||||
//
|
||||
// This would be an ID3D11Device, HGLRC/GLXContext, ID3D12Device, etc
|
||||
typedef void *RENDERDOC_DevicePointer;
|
||||
|
||||
// A window handle is the OS's native window handle
|
||||
//
|
||||
// This would be an HWND, GLXDrawable, etc
|
||||
typedef void *RENDERDOC_WindowHandle;
|
||||
|
||||
// A helper macro for Vulkan, where the device handle cannot be used directly.
|
||||
//
|
||||
// Passing the VkInstance to this macro will return the RENDERDOC_DevicePointer to use.
|
||||
//
|
||||
// Specifically, the value needed is the dispatch table pointer, which sits as the first
|
||||
// pointer-sized object in the memory pointed to by the VkInstance. Thus we cast to a void** and
|
||||
// indirect once.
|
||||
#define RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(inst) (*((void **)(inst)))
|
||||
|
||||
// This sets the RenderDoc in-app overlay in the API/window pair as 'active' and it will
|
||||
// respond to keypresses. Neither parameter can be NULL
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_SetActiveWindow)(RENDERDOC_DevicePointer device,
|
||||
RENDERDOC_WindowHandle wndHandle);
|
||||
|
||||
// capture the next frame on whichever window and API is currently considered active
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_TriggerCapture)();
|
||||
|
||||
// capture the next N frames on whichever window and API is currently considered active
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_TriggerMultiFrameCapture)(uint32_t numFrames);
|
||||
|
||||
// When choosing either a device pointer or a window handle to capture, you can pass NULL.
|
||||
// Passing NULL specifies a 'wildcard' match against anything. This allows you to specify
|
||||
// any API rendering to a specific window, or a specific API instance rendering to any window,
|
||||
// or in the simplest case of one window and one API, you can just pass NULL for both.
|
||||
//
|
||||
// In either case, if there are two or more possible matching (device,window) pairs it
|
||||
// is undefined which one will be captured.
|
||||
//
|
||||
// Note: for headless rendering you can pass NULL for the window handle and either specify
|
||||
// a device pointer or leave it NULL as above.
|
||||
|
||||
// Immediately starts capturing API calls on the specified device pointer and window handle.
|
||||
//
|
||||
// If there is no matching thing to capture (e.g. no supported API has been initialised),
|
||||
// this will do nothing.
|
||||
//
|
||||
// The results are undefined (including crashes) if two captures are started overlapping,
|
||||
// even on separate devices and/oror windows.
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_StartFrameCapture)(RENDERDOC_DevicePointer device,
|
||||
RENDERDOC_WindowHandle wndHandle);
|
||||
|
||||
// Returns whether or not a frame capture is currently ongoing anywhere.
|
||||
//
|
||||
// This will return 1 if a capture is ongoing, and 0 if there is no capture running
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_IsFrameCapturing)();
|
||||
|
||||
// Ends capturing immediately.
|
||||
//
|
||||
// This will return 1 if the capture succeeded, and 0 if there was an error capturing.
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_EndFrameCapture)(RENDERDOC_DevicePointer device,
|
||||
RENDERDOC_WindowHandle wndHandle);
|
||||
|
||||
// Ends capturing immediately and discard any data stored without saving to disk.
|
||||
//
|
||||
// This will return 1 if the capture was discarded, and 0 if there was an error or no capture
|
||||
// was in progress
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_DiscardFrameCapture)(RENDERDOC_DevicePointer device,
|
||||
RENDERDOC_WindowHandle wndHandle);
|
||||
|
||||
// Only valid to be called between a call to StartFrameCapture and EndFrameCapture. Gives a custom
|
||||
// title to the capture produced which will be displayed in the UI.
|
||||
//
|
||||
// If multiple captures are ongoing, this title will be applied to the first capture to end after
|
||||
// this call. The second capture to end will have no title, unless this function is called again.
|
||||
//
|
||||
// Calling this function has no effect if no capture is currently running, and if it is called
|
||||
// multiple times only the last title will be used.
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureTitle)(const char *title);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// RenderDoc API versions
|
||||
//
|
||||
|
||||
// RenderDoc uses semantic versioning (http://semver.org/).
|
||||
//
|
||||
// MAJOR version is incremented when incompatible API changes happen.
|
||||
// MINOR version is incremented when functionality is added in a backwards-compatible manner.
|
||||
// PATCH version is incremented when backwards-compatible bug fixes happen.
|
||||
//
|
||||
// Note that this means the API returned can be higher than the one you might have requested.
|
||||
// e.g. if you are running against a newer RenderDoc that supports 1.0.1, it will be returned
|
||||
// instead of 1.0.0. You can check this with the GetAPIVersion entry point
|
||||
typedef enum RENDERDOC_Version
|
||||
{
|
||||
eRENDERDOC_API_Version_1_0_0 = 10000, // RENDERDOC_API_1_0_0 = 1 00 00
|
||||
eRENDERDOC_API_Version_1_0_1 = 10001, // RENDERDOC_API_1_0_1 = 1 00 01
|
||||
eRENDERDOC_API_Version_1_0_2 = 10002, // RENDERDOC_API_1_0_2 = 1 00 02
|
||||
eRENDERDOC_API_Version_1_1_0 = 10100, // RENDERDOC_API_1_1_0 = 1 01 00
|
||||
eRENDERDOC_API_Version_1_1_1 = 10101, // RENDERDOC_API_1_1_1 = 1 01 01
|
||||
eRENDERDOC_API_Version_1_1_2 = 10102, // RENDERDOC_API_1_1_2 = 1 01 02
|
||||
eRENDERDOC_API_Version_1_2_0 = 10200, // RENDERDOC_API_1_2_0 = 1 02 00
|
||||
eRENDERDOC_API_Version_1_3_0 = 10300, // RENDERDOC_API_1_3_0 = 1 03 00
|
||||
eRENDERDOC_API_Version_1_4_0 = 10400, // RENDERDOC_API_1_4_0 = 1 04 00
|
||||
eRENDERDOC_API_Version_1_4_1 = 10401, // RENDERDOC_API_1_4_1 = 1 04 01
|
||||
eRENDERDOC_API_Version_1_4_2 = 10402, // RENDERDOC_API_1_4_2 = 1 04 02
|
||||
eRENDERDOC_API_Version_1_5_0 = 10500, // RENDERDOC_API_1_5_0 = 1 05 00
|
||||
eRENDERDOC_API_Version_1_6_0 = 10600, // RENDERDOC_API_1_6_0 = 1 06 00
|
||||
} RENDERDOC_Version;
|
||||
|
||||
// API version changelog:
|
||||
//
|
||||
// 1.0.0 - initial release
|
||||
// 1.0.1 - Bugfix: IsFrameCapturing() was returning false for captures that were triggered
|
||||
// by keypress or TriggerCapture, instead of Start/EndFrameCapture.
|
||||
// 1.0.2 - Refactor: Renamed eRENDERDOC_Option_DebugDeviceMode to eRENDERDOC_Option_APIValidation
|
||||
// 1.1.0 - Add feature: TriggerMultiFrameCapture(). Backwards compatible with 1.0.x since the new
|
||||
// function pointer is added to the end of the struct, the original layout is identical
|
||||
// 1.1.1 - Refactor: Renamed remote access to target control (to better disambiguate from remote
|
||||
// replay/remote server concept in replay UI)
|
||||
// 1.1.2 - Refactor: Renamed "log file" in function names to just capture, to clarify that these
|
||||
// are captures and not debug logging files. This is the first API version in the v1.0
|
||||
// branch.
|
||||
// 1.2.0 - Added feature: SetCaptureFileComments() to add comments to a capture file that will be
|
||||
// displayed in the UI program on load.
|
||||
// 1.3.0 - Added feature: New capture option eRENDERDOC_Option_AllowUnsupportedVendorExtensions
|
||||
// which allows users to opt-in to allowing unsupported vendor extensions to function.
|
||||
// Should be used at the user's own risk.
|
||||
// Refactor: Renamed eRENDERDOC_Option_VerifyMapWrites to
|
||||
// eRENDERDOC_Option_VerifyBufferAccess, which now also controls initialisation to
|
||||
// 0xdddddddd of uninitialised buffer contents.
|
||||
// 1.4.0 - Added feature: DiscardFrameCapture() to discard a frame capture in progress and stop
|
||||
// capturing without saving anything to disk.
|
||||
// 1.4.1 - Refactor: Renamed Shutdown to RemoveHooks to better clarify what is happening
|
||||
// 1.4.2 - Refactor: Renamed 'draws' to 'actions' in callstack capture option.
|
||||
// 1.5.0 - Added feature: ShowReplayUI() to request that the replay UI show itself if connected
|
||||
// 1.6.0 - Added feature: SetCaptureTitle() which can be used to set a title for a
|
||||
// capture made with StartFrameCapture() or EndFrameCapture()
|
||||
|
||||
typedef struct RENDERDOC_API_1_6_0
|
||||
{
|
||||
pRENDERDOC_GetAPIVersion GetAPIVersion;
|
||||
|
||||
pRENDERDOC_SetCaptureOptionU32 SetCaptureOptionU32;
|
||||
pRENDERDOC_SetCaptureOptionF32 SetCaptureOptionF32;
|
||||
|
||||
pRENDERDOC_GetCaptureOptionU32 GetCaptureOptionU32;
|
||||
pRENDERDOC_GetCaptureOptionF32 GetCaptureOptionF32;
|
||||
|
||||
pRENDERDOC_SetFocusToggleKeys SetFocusToggleKeys;
|
||||
pRENDERDOC_SetCaptureKeys SetCaptureKeys;
|
||||
|
||||
pRENDERDOC_GetOverlayBits GetOverlayBits;
|
||||
pRENDERDOC_MaskOverlayBits MaskOverlayBits;
|
||||
|
||||
// Shutdown was renamed to RemoveHooks in 1.4.1.
|
||||
// These unions allow old code to continue compiling without changes
|
||||
union
|
||||
{
|
||||
pRENDERDOC_Shutdown Shutdown;
|
||||
pRENDERDOC_RemoveHooks RemoveHooks;
|
||||
};
|
||||
pRENDERDOC_UnloadCrashHandler UnloadCrashHandler;
|
||||
|
||||
// Get/SetLogFilePathTemplate was renamed to Get/SetCaptureFilePathTemplate in 1.1.2.
|
||||
// These unions allow old code to continue compiling without changes
|
||||
union
|
||||
{
|
||||
// deprecated name
|
||||
pRENDERDOC_SetLogFilePathTemplate SetLogFilePathTemplate;
|
||||
// current name
|
||||
pRENDERDOC_SetCaptureFilePathTemplate SetCaptureFilePathTemplate;
|
||||
};
|
||||
union
|
||||
{
|
||||
// deprecated name
|
||||
pRENDERDOC_GetLogFilePathTemplate GetLogFilePathTemplate;
|
||||
// current name
|
||||
pRENDERDOC_GetCaptureFilePathTemplate GetCaptureFilePathTemplate;
|
||||
};
|
||||
|
||||
pRENDERDOC_GetNumCaptures GetNumCaptures;
|
||||
pRENDERDOC_GetCapture GetCapture;
|
||||
|
||||
pRENDERDOC_TriggerCapture TriggerCapture;
|
||||
|
||||
// IsRemoteAccessConnected was renamed to IsTargetControlConnected in 1.1.1.
|
||||
// This union allows old code to continue compiling without changes
|
||||
union
|
||||
{
|
||||
// deprecated name
|
||||
pRENDERDOC_IsRemoteAccessConnected IsRemoteAccessConnected;
|
||||
// current name
|
||||
pRENDERDOC_IsTargetControlConnected IsTargetControlConnected;
|
||||
};
|
||||
pRENDERDOC_LaunchReplayUI LaunchReplayUI;
|
||||
|
||||
pRENDERDOC_SetActiveWindow SetActiveWindow;
|
||||
|
||||
pRENDERDOC_StartFrameCapture StartFrameCapture;
|
||||
pRENDERDOC_IsFrameCapturing IsFrameCapturing;
|
||||
pRENDERDOC_EndFrameCapture EndFrameCapture;
|
||||
|
||||
// new function in 1.1.0
|
||||
pRENDERDOC_TriggerMultiFrameCapture TriggerMultiFrameCapture;
|
||||
|
||||
// new function in 1.2.0
|
||||
pRENDERDOC_SetCaptureFileComments SetCaptureFileComments;
|
||||
|
||||
// new function in 1.4.0
|
||||
pRENDERDOC_DiscardFrameCapture DiscardFrameCapture;
|
||||
|
||||
// new function in 1.5.0
|
||||
pRENDERDOC_ShowReplayUI ShowReplayUI;
|
||||
|
||||
// new function in 1.6.0
|
||||
pRENDERDOC_SetCaptureTitle SetCaptureTitle;
|
||||
} RENDERDOC_API_1_6_0;
|
||||
|
||||
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_0_0;
|
||||
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_0_1;
|
||||
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_0_2;
|
||||
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_1_0;
|
||||
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_1_1;
|
||||
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_1_2;
|
||||
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_2_0;
|
||||
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_3_0;
|
||||
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_4_0;
|
||||
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_4_1;
|
||||
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_4_2;
|
||||
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_5_0;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// RenderDoc API entry point
|
||||
//
|
||||
// This entry point can be obtained via GetProcAddress/dlsym if RenderDoc is available.
|
||||
//
|
||||
// The name is the same as the typedef - "RENDERDOC_GetAPI"
|
||||
//
|
||||
// This function is not thread safe, and should not be called on multiple threads at once.
|
||||
// Ideally, call this once as early as possible in your application's startup, before doing
|
||||
// any API work, since some configuration functionality etc has to be done also before
|
||||
// initialising any APIs.
|
||||
//
|
||||
// Parameters:
|
||||
// version is a single value from the RENDERDOC_Version above.
|
||||
//
|
||||
// outAPIPointers will be filled out with a pointer to the corresponding struct of function
|
||||
// pointers.
|
||||
//
|
||||
// Returns:
|
||||
// 1 - if the outAPIPointers has been filled with a pointer to the API struct requested
|
||||
// 0 - if the requested version is not supported or the arguments are invalid.
|
||||
//
|
||||
typedef int(RENDERDOC_CC *pRENDERDOC_GetAPI)(RENDERDOC_Version version, void **outAPIPointers);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
1
externals/robin-map
vendored
1
externals/robin-map
vendored
Submodule externals/robin-map deleted from 4ec1bf19c6
1
externals/sirit
vendored
1
externals/sirit
vendored
Submodule externals/sirit deleted from 282083a595
1
externals/xbyak
vendored
1
externals/xbyak
vendored
Submodule externals/xbyak deleted from 44a72f3692
1
externals/xxhash
vendored
1
externals/xxhash
vendored
Submodule externals/xxhash deleted from 953a09abc3
1
externals/zlib-ng
vendored
1
externals/zlib-ng
vendored
Submodule externals/zlib-ng deleted from fd0d263ced
1
externals/zydis
vendored
1
externals/zydis
vendored
Submodule externals/zydis deleted from 120e0e705f
@@ -1,30 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __linux__
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
|
||||
class AdaptiveMutex {
|
||||
public:
|
||||
void lock() {
|
||||
pthread_mutex_lock(&mutex);
|
||||
}
|
||||
void unlock() {
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
[[nodiscard]] bool try_lock() {
|
||||
return pthread_mutex_trylock(&mutex) == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
pthread_mutex_t mutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP;
|
||||
};
|
||||
#endif // PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,49 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2014 Jannik Vogel <email@jannikvogel.de>
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr T AlignUp(T value, std::size_t size) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
auto mod{static_cast<T>(value % size)};
|
||||
value -= mod;
|
||||
return static_cast<T>(mod == T{0} ? value : value + size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr T AlignDown(T value, std::size_t size) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return static_cast<T>(value - value % size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
[[nodiscard]] constexpr bool IsAligned(T value, std::size_t alignment) {
|
||||
return (value & (alignment - 1)) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
[[nodiscard]] constexpr bool Is16KBAligned(T value) {
|
||||
return (value & 0x3FFF) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
[[nodiscard]] constexpr bool Is64KBAligned(T value) {
|
||||
return (value & 0xFFFF) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
[[nodiscard]] constexpr bool Is2MBAligned(T value) {
|
||||
return (value & 0x1FFFFF) == 0;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,10 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
#define ARCH_X86_64 1
|
||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||
#define ARCH_ARM64 1
|
||||
#endif
|
||||
@@ -1,13 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/arch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/backend.h"
|
||||
|
||||
#if defined(ARCH_X86_64)
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
#define Crash() __asm__ __volatile__("int $3")
|
||||
#elif defined(ARCH_ARM64)
|
||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||
#define Crash() __asm__ __volatile__("brk 0")
|
||||
#else
|
||||
#error "Missing Crash() implementation for target CPU architecture."
|
||||
|
||||
@@ -1,406 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include "common/types.h"
|
||||
|
||||
#ifdef __AVX2__
|
||||
#define BIT_ARRAY_USE_AVX
|
||||
#include <immintrin.h>
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <size_t N>
|
||||
class BitArray {
|
||||
static_assert(N % 64 == 0, "BitArray size must be a multiple of 64 bits.");
|
||||
|
||||
static constexpr size_t BITS_PER_WORD = 64;
|
||||
static constexpr size_t WORD_COUNT = N / BITS_PER_WORD;
|
||||
static constexpr size_t WORDS_PER_AVX = 4;
|
||||
static constexpr size_t AVX_WORD_COUNT = WORD_COUNT / WORDS_PER_AVX;
|
||||
|
||||
public:
|
||||
using Range = std::pair<size_t, size_t>;
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
explicit Iterator(const BitArray& bit_array_, u64 start) : bit_array(bit_array_) {
|
||||
range = bit_array.FirstRangeFrom(start);
|
||||
}
|
||||
|
||||
Iterator& operator++() {
|
||||
range = bit_array.FirstRangeFrom(range.second);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const Iterator& other) const {
|
||||
return range == other.range;
|
||||
}
|
||||
|
||||
bool operator!=(const Iterator& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
const Range& operator*() const {
|
||||
return range;
|
||||
}
|
||||
|
||||
const Range* operator->() const {
|
||||
return ⦥
|
||||
}
|
||||
|
||||
private:
|
||||
const BitArray& bit_array;
|
||||
Range range;
|
||||
};
|
||||
|
||||
using const_iterator = Iterator;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = Range;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = const Range*;
|
||||
using reference = const Range&;
|
||||
|
||||
BitArray() = default;
|
||||
BitArray(const BitArray& other) = default;
|
||||
BitArray& operator=(const BitArray& other) = default;
|
||||
BitArray(BitArray&& other) noexcept = default;
|
||||
BitArray& operator=(BitArray&& other) noexcept = default;
|
||||
~BitArray() = default;
|
||||
|
||||
BitArray(const BitArray& other, size_t start, size_t end) {
|
||||
if (start >= end || end > N) {
|
||||
return;
|
||||
}
|
||||
const size_t first_word = start / BITS_PER_WORD;
|
||||
const size_t last_word = (end - 1) / BITS_PER_WORD;
|
||||
const size_t start_bit = start % BITS_PER_WORD;
|
||||
const size_t end_bit = (end - 1) % BITS_PER_WORD;
|
||||
const u64 start_mask = ~((1ULL << start_bit) - 1);
|
||||
const u64 end_mask = end_bit == BITS_PER_WORD - 1 ? ~0ULL : (1ULL << (end_bit + 1)) - 1;
|
||||
if (first_word == last_word) {
|
||||
data[first_word] = other.data[first_word] & (start_mask & end_mask);
|
||||
} else {
|
||||
data[first_word] = other.data[first_word] & start_mask;
|
||||
size_t i = first_word + 1;
|
||||
#ifdef BIT_ARRAY_USE_AVX
|
||||
for (; i + WORDS_PER_AVX <= last_word; i += WORDS_PER_AVX) {
|
||||
const __m256i current =
|
||||
_mm256_loadu_si256(reinterpret_cast<const __m256i*>(&other.data[i]));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(&data[i]), current);
|
||||
}
|
||||
#endif
|
||||
for (; i < last_word; ++i) {
|
||||
data[i] = other.data[i];
|
||||
}
|
||||
data[last_word] = other.data[last_word] & end_mask;
|
||||
}
|
||||
}
|
||||
|
||||
BitArray(const BitArray& other, const Range& range)
|
||||
: BitArray(other, range.first, range.second) {}
|
||||
|
||||
const_iterator begin() const {
|
||||
return Iterator(*this, 0);
|
||||
}
|
||||
const_iterator end() const {
|
||||
return Iterator(*this, N);
|
||||
}
|
||||
|
||||
inline constexpr void Set(size_t idx) {
|
||||
data[idx / BITS_PER_WORD] |= (1ULL << (idx % BITS_PER_WORD));
|
||||
}
|
||||
|
||||
inline constexpr void Unset(size_t idx) {
|
||||
data[idx / BITS_PER_WORD] &= ~(1ULL << (idx % BITS_PER_WORD));
|
||||
}
|
||||
|
||||
inline constexpr bool Get(size_t idx) const {
|
||||
return (data[idx / BITS_PER_WORD] & (1ULL << (idx % BITS_PER_WORD))) != 0;
|
||||
}
|
||||
|
||||
inline void SetRange(size_t start, size_t end) {
|
||||
if (start >= end || end > N) {
|
||||
return;
|
||||
}
|
||||
const size_t first_word = start / BITS_PER_WORD;
|
||||
const size_t last_word = (end - 1) / BITS_PER_WORD;
|
||||
const size_t start_bit = start % BITS_PER_WORD;
|
||||
const size_t end_bit = (end - 1) % BITS_PER_WORD;
|
||||
const u64 start_mask = ~((1ULL << start_bit) - 1);
|
||||
const u64 end_mask = end_bit == BITS_PER_WORD - 1 ? ~0ULL : (1ULL << (end_bit + 1)) - 1;
|
||||
if (first_word == last_word) {
|
||||
data[first_word] |= start_mask & end_mask;
|
||||
} else {
|
||||
data[first_word] |= start_mask;
|
||||
size_t i = first_word + 1;
|
||||
#ifdef BIT_ARRAY_USE_AVX
|
||||
const __m256i value = _mm256_set1_epi64x(-1);
|
||||
for (; i + WORDS_PER_AVX <= last_word; i += WORDS_PER_AVX) {
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(&data[i]), value);
|
||||
}
|
||||
#endif
|
||||
for (; i < last_word; ++i) {
|
||||
data[i] = ~0ULL;
|
||||
}
|
||||
data[last_word] |= end_mask;
|
||||
}
|
||||
}
|
||||
|
||||
inline void UnsetRange(size_t start, size_t end) {
|
||||
if (start >= end || end > N) {
|
||||
return;
|
||||
}
|
||||
size_t first_word = start / BITS_PER_WORD;
|
||||
const size_t last_word = (end - 1) / BITS_PER_WORD;
|
||||
const size_t start_bit = start % BITS_PER_WORD;
|
||||
const size_t end_bit = (end - 1) % BITS_PER_WORD;
|
||||
const u64 start_mask = (1ULL << start_bit) - 1;
|
||||
const u64 end_mask = end_bit == BITS_PER_WORD - 1 ? 0ULL : ~((1ULL << (end_bit + 1)) - 1);
|
||||
if (first_word == last_word) {
|
||||
data[first_word] &= start_mask | end_mask;
|
||||
} else {
|
||||
data[first_word] &= start_mask;
|
||||
size_t i = first_word + 1;
|
||||
#ifdef BIT_ARRAY_USE_AVX
|
||||
const __m256i value = _mm256_setzero_si256();
|
||||
for (; i + WORDS_PER_AVX <= last_word; i += WORDS_PER_AVX) {
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(&data[i]), value);
|
||||
}
|
||||
#endif
|
||||
for (; i < last_word; ++i) {
|
||||
data[i] = 0ULL;
|
||||
}
|
||||
data[last_word] &= end_mask;
|
||||
}
|
||||
}
|
||||
|
||||
inline constexpr void SetRange(const Range& range) {
|
||||
SetRange(range.first, range.second);
|
||||
}
|
||||
|
||||
inline constexpr void UnsetRange(const Range& range) {
|
||||
UnsetRange(range.first, range.second);
|
||||
}
|
||||
|
||||
inline constexpr void Clear() {
|
||||
data.fill(0);
|
||||
}
|
||||
|
||||
inline constexpr void Fill() {
|
||||
data.fill(~0ULL);
|
||||
}
|
||||
|
||||
inline constexpr bool None() const {
|
||||
u64 result = 0;
|
||||
for (const auto& word : data) {
|
||||
result |= word;
|
||||
}
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
inline constexpr bool Any() const {
|
||||
return !None();
|
||||
}
|
||||
|
||||
Range FirstRangeFrom(size_t start) const {
|
||||
if (start >= N) {
|
||||
return {N, N};
|
||||
}
|
||||
const auto find_end_bit = [&](size_t word) {
|
||||
#ifdef BIT_ARRAY_USE_AVX
|
||||
const __m256i all_one = _mm256_set1_epi64x(-1);
|
||||
for (; word + WORDS_PER_AVX <= WORD_COUNT; word += WORDS_PER_AVX) {
|
||||
const __m256i current =
|
||||
_mm256_loadu_si256(reinterpret_cast<const __m256i*>(&data[word]));
|
||||
const __m256i cmp = _mm256_cmpeq_epi64(current, all_one);
|
||||
if (_mm256_movemask_epi8(cmp) != 0xFFFFFFFF) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; word < WORD_COUNT; ++word) {
|
||||
if (data[word] != ~0ULL) {
|
||||
return (word * BITS_PER_WORD) + std::countr_one(data[word]);
|
||||
}
|
||||
}
|
||||
return N;
|
||||
};
|
||||
|
||||
const auto word_bits = [&](size_t index, u64 word) {
|
||||
const int empty_bits = std::countr_zero(word);
|
||||
const int ones_count = std::countr_one(word >> empty_bits);
|
||||
const size_t start_bit = index * BITS_PER_WORD + empty_bits;
|
||||
if (ones_count + empty_bits < BITS_PER_WORD) {
|
||||
return Range{start_bit, start_bit + ones_count};
|
||||
}
|
||||
return Range{start_bit, find_end_bit(index + 1)};
|
||||
};
|
||||
|
||||
const size_t start_word = start / BITS_PER_WORD;
|
||||
const size_t start_bit = start % BITS_PER_WORD;
|
||||
const u64 masked_first = data[start_word] & (~((1ULL << start_bit) - 1));
|
||||
if (masked_first) {
|
||||
return word_bits(start_word, masked_first);
|
||||
}
|
||||
|
||||
size_t word = start_word + 1;
|
||||
#ifdef BIT_ARRAY_USE_AVX
|
||||
for (; word + WORDS_PER_AVX <= WORD_COUNT; word += WORDS_PER_AVX) {
|
||||
const __m256i current =
|
||||
_mm256_loadu_si256(reinterpret_cast<const __m256i*>(&data[word]));
|
||||
if (!_mm256_testz_si256(current, current)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; word < WORD_COUNT; ++word) {
|
||||
if (data[word] != 0) {
|
||||
return word_bits(word, data[word]);
|
||||
}
|
||||
}
|
||||
return {N, N};
|
||||
}
|
||||
|
||||
inline constexpr Range FirstRange() const {
|
||||
return FirstRangeFrom(0);
|
||||
}
|
||||
|
||||
Range LastRangeFrom(size_t end) const {
|
||||
if (end == 0) {
|
||||
return {0, 0};
|
||||
}
|
||||
if (end > N) {
|
||||
end = N;
|
||||
}
|
||||
const auto find_start_bit = [&](size_t word) {
|
||||
#ifdef BIT_ARRAY_USE_AVX
|
||||
const __m256i all_zero = _mm256_setzero_si256();
|
||||
for (; word >= WORDS_PER_AVX; word -= WORDS_PER_AVX) {
|
||||
const __m256i current = _mm256_loadu_si256(
|
||||
reinterpret_cast<const __m256i*>(&data[word - WORDS_PER_AVX]));
|
||||
const __m256i cmp = _mm256_cmpeq_epi64(current, all_zero);
|
||||
if (_mm256_movemask_epi8(cmp) != 0xFFFFFFFF) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; word > 0; --word) {
|
||||
if (data[word - 1] != ~0ULL) {
|
||||
return word * BITS_PER_WORD - std::countl_one(data[word - 1]);
|
||||
}
|
||||
}
|
||||
return size_t(0);
|
||||
};
|
||||
const auto word_bits = [&](size_t index, u64 word) {
|
||||
const int empty_bits = std::countl_zero(word);
|
||||
const int ones_count = std::countl_one(word << empty_bits);
|
||||
const size_t end_bit = index * BITS_PER_WORD - empty_bits;
|
||||
if (empty_bits + ones_count < BITS_PER_WORD) {
|
||||
return Range{end_bit - ones_count, end_bit};
|
||||
}
|
||||
return Range{find_start_bit(index - 1), end_bit};
|
||||
};
|
||||
const size_t end_word = ((end - 1) / BITS_PER_WORD) + 1;
|
||||
const size_t end_bit = (end - 1) % BITS_PER_WORD;
|
||||
u64 masked_last = data[end_word - 1];
|
||||
if (end_bit < BITS_PER_WORD - 1) {
|
||||
masked_last &= (1ULL << (end_bit + 1)) - 1;
|
||||
}
|
||||
if (masked_last) {
|
||||
return word_bits(end_word, masked_last);
|
||||
}
|
||||
size_t word = end_word - 1;
|
||||
#ifdef BIT_ARRAY_USE_AVX
|
||||
for (; word >= WORDS_PER_AVX; word -= WORDS_PER_AVX) {
|
||||
const __m256i current =
|
||||
_mm256_loadu_si256(reinterpret_cast<const __m256i*>(&data[word - WORDS_PER_AVX]));
|
||||
if (!_mm256_testz_si256(current, current)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; word > 0; --word) {
|
||||
if (data[word - 1] != 0) {
|
||||
return word_bits(word, data[word - 1]);
|
||||
}
|
||||
}
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
inline constexpr Range LastRange() const {
|
||||
return LastRangeFrom(N);
|
||||
}
|
||||
|
||||
inline constexpr size_t Size() const {
|
||||
return N;
|
||||
}
|
||||
|
||||
inline constexpr BitArray& operator|=(const BitArray& other) {
|
||||
for (size_t i = 0; i < WORD_COUNT; ++i) {
|
||||
data[i] |= other.data[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline constexpr BitArray& operator&=(const BitArray& other) {
|
||||
for (size_t i = 0; i < WORD_COUNT; ++i) {
|
||||
data[i] &= other.data[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline constexpr BitArray& operator^=(const BitArray& other) {
|
||||
for (size_t i = 0; i < WORD_COUNT; ++i) {
|
||||
data[i] ^= other.data[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline constexpr BitArray operator|(const BitArray& other) const {
|
||||
BitArray result = *this;
|
||||
result |= other;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline constexpr BitArray operator&(const BitArray& other) const {
|
||||
BitArray result = *this;
|
||||
result &= other;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline constexpr BitArray operator^(const BitArray& other) const {
|
||||
BitArray result = *this;
|
||||
result ^= other;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline constexpr BitArray operator~() const {
|
||||
BitArray result = *this;
|
||||
for (size_t i = 0; i < WORD_COUNT; ++i) {
|
||||
result.data[i] = ~result.data[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline constexpr bool operator==(const BitArray& other) const {
|
||||
u64 result = 0;
|
||||
for (size_t i = 0; i < WORD_COUNT; ++i) {
|
||||
result |= data[i] ^ other.data[i];
|
||||
}
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
inline constexpr bool operator!=(const BitArray& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<u64, WORD_COUNT> data{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include "common/polyfill_thread.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
@@ -122,7 +121,7 @@ private:
|
||||
} else if constexpr (Mode == PopMode::WaitWithStopToken) {
|
||||
// Wait until the queue is not empty.
|
||||
std::unique_lock lock{consumer_cv_mutex};
|
||||
Common::CondvarWait(consumer_cv, lock, stop_token, [this, read_index] {
|
||||
consumer_cv.wait(lock, stop_token, [this, read_index] {
|
||||
return read_index != m_write_index.load(std::memory_order::acquire);
|
||||
});
|
||||
if (stop_token.stop_requested()) {
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Common {
|
||||
|
||||
// Check if type satisfies the ContiguousContainer named requirement.
|
||||
template <typename T>
|
||||
concept IsContiguousContainer = std::contiguous_iterator<typename T::iterator>;
|
||||
|
||||
template <typename Derived, typename Base>
|
||||
concept DerivedFrom = std::derived_from<Derived, Base>;
|
||||
|
||||
// TODO: Replace with std::convertible_to when libc++ implements it.
|
||||
template <typename From, typename To>
|
||||
concept ConvertibleTo = std::is_convertible_v<From, To>;
|
||||
|
||||
// No equivalents in the stdlib
|
||||
|
||||
template <typename T>
|
||||
concept IsArithmetic = std::is_arithmetic_v<T>;
|
||||
|
||||
template <typename T>
|
||||
concept IsIntegral = std::is_integral_v<T>;
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,171 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "assert.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wtautological-undefined-compare"
|
||||
|
||||
/**
|
||||
* @brief A null-terminated string with a fixed maximum length
|
||||
* This class is not meant to be used as a general-purpose string class
|
||||
* It is meant to be used as `char[N]` where memory layout is fixed
|
||||
* @tparam N Maximum length of the string
|
||||
* @tparam T Type of character
|
||||
*/
|
||||
template <size_t N, typename T = char>
|
||||
class CString {
|
||||
T data[N]{};
|
||||
|
||||
public:
|
||||
class Iterator;
|
||||
|
||||
CString() = default;
|
||||
|
||||
template <size_t M>
|
||||
explicit CString(const CString<M>& other)
|
||||
requires(M <= N)
|
||||
{
|
||||
if (this == nullptr) {
|
||||
return;
|
||||
}
|
||||
std::ranges::copy(other.begin(), other.end(), data);
|
||||
}
|
||||
|
||||
void FromString(const std::basic_string_view<T>& str) {
|
||||
if (this == nullptr) {
|
||||
return;
|
||||
}
|
||||
size_t p = str.copy(data, N - 1);
|
||||
data[p] = '\0';
|
||||
}
|
||||
|
||||
void Zero() {
|
||||
if (this == nullptr) {
|
||||
return;
|
||||
}
|
||||
std::ranges::fill(data, 0);
|
||||
}
|
||||
|
||||
explicit(false) operator std::basic_string_view<T>() const {
|
||||
if (this == nullptr) {
|
||||
return {};
|
||||
}
|
||||
return std::basic_string_view<T>{data};
|
||||
}
|
||||
|
||||
explicit operator std::basic_string<T>() const {
|
||||
if (this == nullptr) {
|
||||
return {};
|
||||
}
|
||||
return std::basic_string<T>{data};
|
||||
}
|
||||
|
||||
std::basic_string<T> to_string() const {
|
||||
if (this == nullptr) {
|
||||
return {};
|
||||
}
|
||||
return std::basic_string<T>{data};
|
||||
}
|
||||
|
||||
std::basic_string_view<T> to_view() const {
|
||||
if (this == nullptr) {
|
||||
return {};
|
||||
}
|
||||
return std::basic_string_view<T>{data};
|
||||
}
|
||||
|
||||
T* begin() {
|
||||
if (this == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
const T* begin() const {
|
||||
if (this == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
T* end() {
|
||||
if (this == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return data + N;
|
||||
}
|
||||
|
||||
const T* end() const {
|
||||
if (this == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return data + N;
|
||||
}
|
||||
|
||||
constexpr std::size_t capacity() const {
|
||||
return N;
|
||||
}
|
||||
|
||||
std::size_t size() const {
|
||||
return std::char_traits<T>::length(data);
|
||||
}
|
||||
|
||||
T& operator[](size_t idx) {
|
||||
return data[idx];
|
||||
}
|
||||
|
||||
const T& operator[](size_t idx) const {
|
||||
return data[idx];
|
||||
}
|
||||
|
||||
class Iterator {
|
||||
T* ptr;
|
||||
T* end;
|
||||
|
||||
public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using reference = T&;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
Iterator() = default;
|
||||
explicit Iterator(T* ptr) : ptr(ptr), end(ptr + N) {}
|
||||
|
||||
Iterator& operator++() {
|
||||
++ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator operator++(int) {
|
||||
Iterator tmp = *this;
|
||||
++ptr;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
operator T*() {
|
||||
ASSERT_MSG(ptr >= end, "CString iterator out of bounds");
|
||||
return ptr;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
static_assert(sizeof(CString<13>) == sizeof(char[13])); // Ensure size still matches a simple array
|
||||
static_assert(std::weakly_incrementable<CString<13>::Iterator>);
|
||||
|
||||
template <size_t N>
|
||||
using CWString = CString<N, wchar_t>;
|
||||
|
||||
template <size_t N>
|
||||
using CU16String = CString<N, char16_t>;
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,71 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define BREAKPOINT __debugbreak
|
||||
#elif defined(__GNUC__)
|
||||
#define BREAKPOINT __builtin_trap
|
||||
#else
|
||||
#error What the fuck is this compiler
|
||||
#endif
|
||||
|
||||
static inline bool IsProfilerConnected() {
|
||||
#if TRACY_ENABLE
|
||||
return tracy::GetProfiler().IsConnected();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#define TRACY_GPU_ENABLED 0
|
||||
|
||||
#define CUSTOM_LOCK(type, varname) \
|
||||
tracy::LockableCtx varname { \
|
||||
[]() -> const tracy::SourceLocationData* { \
|
||||
static constexpr tracy::SourceLocationData srcloc{nullptr, #type " " #varname, \
|
||||
TracyFile, TracyLine, 0}; \
|
||||
return &srcloc; \
|
||||
}() \
|
||||
}
|
||||
|
||||
#define TRACK_ALLOC(ptr, size, pool) TracyAllocN(std::bit_cast<void*>(ptr), (size), (pool))
|
||||
#define TRACK_FREE(ptr, pool) TracyFreeN(std::bit_cast<void*>(ptr), (pool))
|
||||
|
||||
enum MarkersPalette : int {
|
||||
EmulatorMarkerColor = 0x264653,
|
||||
RendererMarkerColor = 0x2a9d8f,
|
||||
HleMarkerColor = 0xe9c46a,
|
||||
GpuMarkerColor = 0xf4a261,
|
||||
Reserved1 = 0xe76f51,
|
||||
};
|
||||
|
||||
#define EMULATOR_TRACE ZoneScopedC(EmulatorMarkerColor)
|
||||
#define RENDERER_TRACE ZoneScopedC(RendererMarkerColor)
|
||||
#define HLE_TRACE ZoneScopedC(HleMarkerColor)
|
||||
|
||||
#define TRACE_HINT(str) ZoneText(str.data(), str.size())
|
||||
|
||||
#define TRACE_WARN(msg) \
|
||||
[](const auto& msg) { TracyMessageC(msg.c_str(), msg.size(), tracy::Color::DarkOrange); }(msg);
|
||||
#define TRACE_ERROR(msg) \
|
||||
[](const auto& msg) { TracyMessageC(msg.c_str(), msg.size(), tracy::Color::Red); }(msg)
|
||||
#define TRACE_CRIT(msg) \
|
||||
[](const auto& msg) { TracyMessageC(msg.c_str(), msg.size(), tracy::Color::HotPink); }(msg)
|
||||
|
||||
#define GPU_SCOPE_LOCATION(name, color) \
|
||||
tracy::SourceLocationData{name, TracyFunction, TracyFile, (uint32_t)TracyLine, color};
|
||||
|
||||
#define MUTEX_LOCATION(name) \
|
||||
tracy::SourceLocationData{nullptr, name, TracyFile, (uint32_t)TracyLine, 0};
|
||||
|
||||
#define FRAME_END FrameMark
|
||||
|
||||
#ifdef TRACY_FIBERS
|
||||
#define FIBER_ENTER(name) TracyFiberEnter(name)
|
||||
#define FIBER_EXIT TracyFiberLeave
|
||||
#else
|
||||
#define FIBER_ENTER(name)
|
||||
#define FIBER_EXIT
|
||||
#endif
|
||||
@@ -1,48 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include "common/decoder.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
DecoderImpl::DecoderImpl() {
|
||||
ZydisDecoderInit(&m_decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64);
|
||||
ZydisFormatterInit(&m_formatter, ZYDIS_FORMATTER_STYLE_INTEL);
|
||||
}
|
||||
|
||||
DecoderImpl::~DecoderImpl() = default;
|
||||
|
||||
std::string DecoderImpl::disassembleInst(ZydisDecodedInstruction& inst,
|
||||
ZydisDecodedOperand* operands, u64 address) {
|
||||
const int bufLen = 256;
|
||||
char szBuffer[bufLen];
|
||||
ZydisFormatterFormatInstruction(&m_formatter, &inst, operands, inst.operand_count_visible,
|
||||
szBuffer, sizeof(szBuffer), address, ZYAN_NULL);
|
||||
return szBuffer;
|
||||
}
|
||||
|
||||
void DecoderImpl::printInstruction(void* code, u64 address) {
|
||||
ZydisDecodedInstruction instruction;
|
||||
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT_VISIBLE];
|
||||
ZyanStatus status =
|
||||
ZydisDecoderDecodeFull(&m_decoder, code, sizeof(code), &instruction, operands);
|
||||
if (!ZYAN_SUCCESS(status)) {
|
||||
fmt::print("decode instruction failed at {}\n", fmt::ptr(code));
|
||||
} else {
|
||||
printInst(instruction, operands, address);
|
||||
}
|
||||
}
|
||||
|
||||
void DecoderImpl::printInst(ZydisDecodedInstruction& inst, ZydisDecodedOperand* operands,
|
||||
u64 address) {
|
||||
std::string s = disassembleInst(inst, operands, address);
|
||||
fmt::print("instruction: {}\n", s);
|
||||
}
|
||||
|
||||
ZyanStatus DecoderImpl::decodeInstruction(ZydisDecodedInstruction& inst,
|
||||
ZydisDecodedOperand* operands, void* data, u64 size) {
|
||||
return ZydisDecoderDecodeFull(&m_decoder, data, size, &inst, operands);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,31 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Zydis/Zydis.h>
|
||||
#include "common/singleton.h"
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
class DecoderImpl {
|
||||
public:
|
||||
DecoderImpl();
|
||||
~DecoderImpl();
|
||||
|
||||
std::string disassembleInst(ZydisDecodedInstruction& inst, ZydisDecodedOperand* operands,
|
||||
u64 address);
|
||||
void printInst(ZydisDecodedInstruction& inst, ZydisDecodedOperand* operands, u64 address);
|
||||
void printInstruction(void* code, u64 address);
|
||||
ZyanStatus decodeInstruction(ZydisDecodedInstruction& inst, ZydisDecodedOperand* operands,
|
||||
void* data, u64 size = 15);
|
||||
|
||||
private:
|
||||
ZydisDecoder m_decoder;
|
||||
ZydisFormatter m_formatter;
|
||||
};
|
||||
|
||||
using Decoder = Common::Singleton<DecoderImpl>;
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,57 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include "discord_rpc_handler.h"
|
||||
|
||||
namespace DiscordRPCHandler {
|
||||
|
||||
void RPC::init() {
|
||||
DiscordEventHandlers handlers{};
|
||||
|
||||
Discord_Initialize("1139939140494971051", &handlers, 1, nullptr);
|
||||
startTimestamp = time(nullptr);
|
||||
rpcEnabled = true;
|
||||
}
|
||||
|
||||
void RPC::setStatusIdling() {
|
||||
DiscordRichPresence rpc{};
|
||||
rpc.largeImageKey = "https://cdn.jsdelivr.net/gh/shadps4-emu/shadPS4@main/.github/shadps4.png";
|
||||
rpc.largeImageText = "shadPS4 is a PS4 emulator";
|
||||
rpc.startTimestamp = startTimestamp;
|
||||
rpc.details = "Idle";
|
||||
|
||||
status = RPCStatus::Idling;
|
||||
Discord_UpdatePresence(&rpc);
|
||||
}
|
||||
|
||||
void RPC::setStatusPlaying(const std::string& game_name, const std::string& game_id) {
|
||||
DiscordRichPresence rpc{};
|
||||
|
||||
rpc.details = "Playing";
|
||||
rpc.state = game_name.c_str();
|
||||
std::string largeImageUrl =
|
||||
"https://store.playstation.com/store/api/chihiro/00_09_000/titlecontainer/US/en/999/" +
|
||||
game_id + "_00/image";
|
||||
rpc.largeImageKey = largeImageUrl.c_str();
|
||||
rpc.largeImageText = game_name.c_str();
|
||||
rpc.startTimestamp = startTimestamp;
|
||||
|
||||
status = RPCStatus::Playing;
|
||||
Discord_UpdatePresence(&rpc);
|
||||
}
|
||||
|
||||
void RPC::shutdown() {
|
||||
if (rpcEnabled) {
|
||||
rpcEnabled = false;
|
||||
Discord_ClearPresence();
|
||||
Discord_Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool RPC::getRPCEnabled() {
|
||||
return rpcEnabled;
|
||||
}
|
||||
|
||||
} // namespace DiscordRPCHandler
|
||||
@@ -1,30 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <discord_rpc.h>
|
||||
|
||||
namespace DiscordRPCHandler {
|
||||
|
||||
enum class RPCStatus {
|
||||
Idling,
|
||||
Playing,
|
||||
};
|
||||
|
||||
class RPC {
|
||||
std::uint64_t startTimestamp;
|
||||
bool rpcEnabled = false;
|
||||
RPCStatus status;
|
||||
|
||||
public:
|
||||
void init();
|
||||
void setStatusIdling();
|
||||
void setStatusPlaying(const std::string& game_name, const std::string& game_id);
|
||||
void shutdown();
|
||||
bool getRPCEnabled();
|
||||
};
|
||||
|
||||
} // namespace DiscordRPCHandler
|
||||
@@ -1,25 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Common {
|
||||
|
||||
/// Ceiled integer division.
|
||||
template <typename N, typename D>
|
||||
requires std::is_integral_v<N> && std::is_unsigned_v<D>
|
||||
[[nodiscard]] constexpr N DivCeil(N number, D divisor) {
|
||||
return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
|
||||
}
|
||||
|
||||
/// Ceiled integer division with logarithmic divisor in base 2
|
||||
template <typename N, typename D>
|
||||
requires std::is_integral_v<N> && std::is_unsigned_v<D>
|
||||
[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) {
|
||||
return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,134 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "assert.h"
|
||||
#include "bit_field.h"
|
||||
#include "singleton.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace Core {
|
||||
class Emulator;
|
||||
}
|
||||
|
||||
namespace Common {
|
||||
|
||||
union PSFAttributes {
|
||||
/// Supports initial user's logout
|
||||
BitField<0, 1, u32> support_initial_user_logout;
|
||||
/// Enter button for the common dialog is cross.
|
||||
BitField<1, 1, u32> enter_button_cross;
|
||||
/// Warning dialog for PS Move is displayed in the options menu.
|
||||
BitField<2, 1, u32> ps_move_warning;
|
||||
/// Supports stereoscopic 3D.
|
||||
BitField<3, 1, u32> support_stereoscopic_3d;
|
||||
/// Suspends when PS button is pressed.
|
||||
BitField<4, 1, u32> ps_button_suspend;
|
||||
/// Enter button for the common dialog is assigned by the system software.
|
||||
BitField<5, 1, u32> enter_button_system;
|
||||
/// Overrides share menu behavior.
|
||||
BitField<6, 1, u32> override_share_menu;
|
||||
/// Suspends when PS button is pressed and special output resolution is set.
|
||||
BitField<8, 1, u32> special_res_ps_button_suspend;
|
||||
/// Enable HDCP.
|
||||
BitField<9, 1, u32> enable_hdcp;
|
||||
/// Disable HDCP for non-game.
|
||||
BitField<10, 1, u32> disable_hdcp_non_game;
|
||||
/// Supports PS VR.
|
||||
BitField<14, 1, u32> support_ps_vr;
|
||||
/// CPU mode (6 CPU)
|
||||
BitField<15, 1, u32> six_cpu_mode;
|
||||
/// CPU mode (7 CPU)
|
||||
BitField<16, 1, u32> seven_cpu_mode;
|
||||
/// Supports PS4 Pro (Neo) mode.
|
||||
BitField<23, 1, u32> support_neo_mode;
|
||||
/// Requires PS VR.
|
||||
BitField<26, 1, u32> require_ps_vr;
|
||||
/// Supports HDR.
|
||||
BitField<29, 1, u32> support_hdr;
|
||||
/// Display location.
|
||||
BitField<31, 1, u32> display_location;
|
||||
|
||||
u32 raw{};
|
||||
};
|
||||
static_assert(sizeof(PSFAttributes) == 4);
|
||||
|
||||
class ElfInfo {
|
||||
friend class Core::Emulator;
|
||||
|
||||
bool initialized = false;
|
||||
|
||||
std::string game_serial{};
|
||||
std::string title{};
|
||||
std::string app_ver{};
|
||||
u32 firmware_ver = 0;
|
||||
u32 raw_firmware_ver = 0;
|
||||
PSFAttributes psf_attributes{};
|
||||
|
||||
std::filesystem::path splash_path{};
|
||||
std::filesystem::path game_folder{};
|
||||
|
||||
public:
|
||||
static constexpr u32 FW_15 = 0x1500000;
|
||||
static constexpr u32 FW_16 = 0x1600000;
|
||||
static constexpr u32 FW_17 = 0x1700000;
|
||||
static constexpr u32 FW_20 = 0x2000000;
|
||||
static constexpr u32 FW_25 = 0x2500000;
|
||||
static constexpr u32 FW_30 = 0x3000000;
|
||||
static constexpr u32 FW_35 = 0x3500000;
|
||||
static constexpr u32 FW_40 = 0x4000000;
|
||||
static constexpr u32 FW_45 = 0x4500000;
|
||||
static constexpr u32 FW_50 = 0x5000000;
|
||||
static constexpr u32 FW_55 = 0x5500000;
|
||||
static constexpr u32 FW_60 = 0x6000000;
|
||||
static constexpr u32 FW_80 = 0x8000000;
|
||||
|
||||
static ElfInfo& Instance() {
|
||||
return *Singleton<ElfInfo>::Instance();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view GameSerial() const {
|
||||
ASSERT(initialized);
|
||||
return Instance().game_serial;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view Title() const {
|
||||
ASSERT(initialized);
|
||||
return title;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view AppVer() const {
|
||||
ASSERT(initialized);
|
||||
return app_ver;
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 FirmwareVer() const {
|
||||
ASSERT(initialized);
|
||||
return firmware_ver;
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 RawFirmwareVer() const {
|
||||
ASSERT(initialized);
|
||||
return raw_firmware_ver;
|
||||
}
|
||||
|
||||
[[nodiscard]] const PSFAttributes& GetPSFAttributes() const {
|
||||
ASSERT(initialized);
|
||||
return psf_attributes;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::filesystem::path& GetSplashPath() const {
|
||||
return splash_path;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::filesystem::path& GetGameFolder() const {
|
||||
return game_folder;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,57 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstddef>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#endif
|
||||
|
||||
#include "common/error.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
std::string NativeErrorToString(int e) {
|
||||
#ifdef _WIN32
|
||||
LPSTR err_str;
|
||||
|
||||
DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr, e, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
|
||||
reinterpret_cast<LPSTR>(&err_str), 1, nullptr);
|
||||
if (!res) {
|
||||
return "(FormatMessageA failed to format error)";
|
||||
}
|
||||
std::string ret(err_str);
|
||||
LocalFree(err_str);
|
||||
return ret;
|
||||
#else
|
||||
char err_str[255];
|
||||
#if defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600)) || \
|
||||
defined(ANDROID)
|
||||
// Thread safe (GNU-specific)
|
||||
const char* str = strerror_r(e, err_str, sizeof(err_str));
|
||||
return std::string(str);
|
||||
#else
|
||||
// Thread safe (XSI-compliant)
|
||||
int second_err = strerror_r(e, err_str, sizeof(err_str));
|
||||
if (second_err != 0) {
|
||||
return "(strerror_r failed to format error)";
|
||||
}
|
||||
return std::string(err_str);
|
||||
#endif // GLIBC etc.
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
std::string GetLastErrorMsg() {
|
||||
#ifdef _WIN32
|
||||
return NativeErrorToString(GetLastError());
|
||||
#else
|
||||
return NativeErrorToString(errno);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,21 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Common {
|
||||
|
||||
// Generic function to get last error message.
|
||||
// Call directly after the command or use the error num.
|
||||
// This function might change the error code.
|
||||
// Defined in error.cpp.
|
||||
[[nodiscard]] std::string GetLastErrorMsg();
|
||||
|
||||
// Like GetLastErrorMsg(), but passing an explicit error code.
|
||||
// Defined in error.cpp.
|
||||
[[nodiscard]] std::string NativeErrorToString(int e);
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,35 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @brief A template class that encapsulates a fixed, compile-time constant value.
|
||||
*
|
||||
* @tparam T The type of the value.
|
||||
* @tparam Value The fixed value of type T.
|
||||
*
|
||||
* This class provides a way to encapsulate a value that is constant and known at compile-time.
|
||||
* The value is stored as a private member and cannot be changed. Any attempt to assign a new
|
||||
* value to an object of this class will reset it to the fixed value.
|
||||
*/
|
||||
template <typename T, T Value>
|
||||
class FixedValue {
|
||||
T m_value{Value};
|
||||
|
||||
public:
|
||||
constexpr FixedValue() = default;
|
||||
|
||||
constexpr explicit(false) operator T() const {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
FixedValue& operator=(const T&) {
|
||||
m_value = Value;
|
||||
return *this;
|
||||
}
|
||||
FixedValue& operator=(T&&) noexcept {
|
||||
m_value = {Value};
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
@@ -1,34 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <class Func>
|
||||
struct FuncTraits {};
|
||||
|
||||
template <class ReturnType_, class... Args>
|
||||
struct FuncTraits<ReturnType_ (*)(Args...)> {
|
||||
using ReturnType = ReturnType_;
|
||||
|
||||
static constexpr size_t NUM_ARGS = sizeof...(Args);
|
||||
|
||||
template <size_t I>
|
||||
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
|
||||
};
|
||||
|
||||
template <typename Func>
|
||||
struct LambdaTraits : LambdaTraits<decltype(&std::remove_reference_t<Func>::operator())> {};
|
||||
|
||||
template <typename ReturnType, typename LambdaType, typename... Args>
|
||||
struct LambdaTraits<ReturnType (LambdaType::*)(Args...) const> {
|
||||
template <size_t I>
|
||||
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
|
||||
|
||||
static constexpr size_t NUM_ARGS{sizeof...(Args)};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,14 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
[[nodiscard]] inline u64 HashCombine(const u64 seed, const u64 hash) {
|
||||
return seed ^ (hash + 0x9e3779b9 + (seed << 12) + (seed >> 4));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 HashCombine(const u32 seed, const u32 hash) {
|
||||
return seed ^ (hash + 0x9e3779b9 + (seed << 6) + (seed >> 2));
|
||||
}
|
||||
@@ -3,16 +3,12 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/error.h"
|
||||
#include "common/io_file.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/path_util.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "common/ntapi.h"
|
||||
|
||||
#include <io.h>
|
||||
#include <share.h>
|
||||
#include <windows.h>
|
||||
@@ -225,58 +221,6 @@ void IOFile::Close() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void IOFile::Unlink() {
|
||||
if (!IsOpen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark the file for deletion
|
||||
// TODO: Also remove the file path?
|
||||
#ifdef _WIN64
|
||||
FILE_DISPOSITION_INFORMATION disposition;
|
||||
IO_STATUS_BLOCK iosb;
|
||||
|
||||
const int fd = fileno(file);
|
||||
HANDLE hfile = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
|
||||
|
||||
disposition.DeleteFile = TRUE;
|
||||
NtSetInformationFile(hfile, &iosb, &disposition, sizeof(disposition),
|
||||
FileDispositionInformation);
|
||||
#else
|
||||
if (unlink(file_path.c_str()) != 0) {
|
||||
const auto ec = std::error_code{errno, std::generic_category()};
|
||||
LOG_ERROR(Common_Filesystem, "Failed to unlink the file at path={}, ec_message={}",
|
||||
PathToUTF8String(file_path), ec.message());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
uintptr_t IOFile::GetFileMapping() {
|
||||
if (file_mapping) {
|
||||
return file_mapping;
|
||||
}
|
||||
#ifdef _WIN64
|
||||
const int fd = fileno(file);
|
||||
|
||||
HANDLE hfile = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
|
||||
HANDLE mapping = nullptr;
|
||||
|
||||
if (file_access_mode == FileAccessMode::ReadWrite) {
|
||||
mapping = CreateFileMapping2(hfile, NULL, FILE_MAP_WRITE, PAGE_READWRITE, SEC_COMMIT, 0,
|
||||
NULL, NULL, 0);
|
||||
} else {
|
||||
mapping = hfile;
|
||||
}
|
||||
|
||||
file_mapping = std::bit_cast<uintptr_t>(mapping);
|
||||
ASSERT_MSG(file_mapping, "{}", Common::GetLastErrorMsg());
|
||||
return file_mapping;
|
||||
#else
|
||||
file_mapping = fileno(file);
|
||||
return file_mapping;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string IOFile::ReadString(size_t length) const {
|
||||
std::vector<char> string_buffer(length);
|
||||
|
||||
|
||||
@@ -8,9 +8,8 @@
|
||||
#include <span>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/concepts.h"
|
||||
#include "common/enum.h"
|
||||
#include "common/types.h"
|
||||
#include "enum.h"
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
@@ -63,6 +62,9 @@ enum class SeekOrigin : u32 {
|
||||
End, // Seeks from the end of the file.
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept IsContiguousContainer = std::contiguous_iterator<typename T::iterator>;
|
||||
|
||||
class IOFile final {
|
||||
public:
|
||||
IOFile();
|
||||
@@ -102,15 +104,11 @@ public:
|
||||
return file != nullptr;
|
||||
}
|
||||
|
||||
uintptr_t GetFileMapping();
|
||||
|
||||
int Open(const std::filesystem::path& path, FileAccessMode mode,
|
||||
FileType type = FileType::BinaryFile,
|
||||
FileShareFlag flag = FileShareFlag::ShareReadOnly);
|
||||
void Close();
|
||||
|
||||
void Unlink();
|
||||
|
||||
bool Flush() const;
|
||||
bool Commit() const;
|
||||
|
||||
|
||||
@@ -4,24 +4,16 @@
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <thread>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h> // For OutputDebugStringW
|
||||
#endif
|
||||
|
||||
#include "common/bounded_threadsafe_queue.h"
|
||||
#include "common/config.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/io_file.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/logging/log_entry.h"
|
||||
#include "common/logging/text_formatter.h"
|
||||
#include "common/path_util.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/thread.h"
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
@@ -96,26 +88,6 @@ private:
|
||||
std::size_t bytes_written = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Backend that writes to Visual Studio's output window
|
||||
*/
|
||||
class DebuggerBackend {
|
||||
public:
|
||||
explicit DebuggerBackend() = default;
|
||||
|
||||
~DebuggerBackend() = default;
|
||||
|
||||
void Write(const Entry& entry) {
|
||||
#ifdef _WIN32
|
||||
::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
void Flush() {}
|
||||
|
||||
void EnableForStacktrace() {}
|
||||
};
|
||||
|
||||
bool initialization_in_progress_suppress_logging = true;
|
||||
|
||||
/**
|
||||
@@ -173,11 +145,6 @@ public:
|
||||
|
||||
void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num,
|
||||
const char* function, std::string message) {
|
||||
// Propagate important log messages to the profiler
|
||||
if (IsProfilerConnected()) {
|
||||
const auto& msg_str = fmt::format("[{}] {}", GetLogClassName(log_class), message);
|
||||
}
|
||||
|
||||
if (!filter.CheckMessage(log_class, log_level) || !Config::getLoggingEnabled()) {
|
||||
return;
|
||||
}
|
||||
@@ -211,7 +178,6 @@ private:
|
||||
|
||||
void StartBackendThread() {
|
||||
backend_thread = std::jthread([this](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("shadPS4:Log");
|
||||
Entry entry;
|
||||
const auto write_logs = [this, &entry]() {
|
||||
ForEachBackend([&entry](auto& backend) { backend.Write(entry); });
|
||||
@@ -241,7 +207,6 @@ private:
|
||||
}
|
||||
|
||||
void ForEachBackend(auto lambda) {
|
||||
// lambda(debugger_backend);
|
||||
lambda(color_console_backend);
|
||||
lambda(file_backend);
|
||||
}
|
||||
@@ -253,7 +218,6 @@ private:
|
||||
static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter};
|
||||
|
||||
Filter filter;
|
||||
DebuggerBackend debugger_backend{};
|
||||
ColorConsoleBackend color_console_backend{};
|
||||
FileBackend file_backend;
|
||||
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename ObjectType, typename TickType>
|
||||
class LeastRecentlyUsedCache {
|
||||
struct Item {
|
||||
ObjectType obj;
|
||||
TickType tick;
|
||||
Item* next{};
|
||||
Item* prev{};
|
||||
};
|
||||
|
||||
public:
|
||||
LeastRecentlyUsedCache() : first_item{}, last_item{} {}
|
||||
~LeastRecentlyUsedCache() = default;
|
||||
|
||||
size_t Insert(ObjectType obj, TickType tick) {
|
||||
const auto new_id = Build();
|
||||
auto& item = item_pool[new_id];
|
||||
item.obj = obj;
|
||||
item.tick = tick;
|
||||
Attach(item);
|
||||
return new_id;
|
||||
}
|
||||
|
||||
void Touch(size_t id, TickType tick) {
|
||||
auto& item = item_pool[id];
|
||||
if (item.tick >= tick) {
|
||||
return;
|
||||
}
|
||||
item.tick = tick;
|
||||
if (&item == last_item) {
|
||||
return;
|
||||
}
|
||||
Detach(item);
|
||||
Attach(item);
|
||||
}
|
||||
|
||||
void Free(size_t id) {
|
||||
auto& item = item_pool[id];
|
||||
Detach(item);
|
||||
item.prev = nullptr;
|
||||
item.next = nullptr;
|
||||
free_items.push_back(id);
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
void ForEachItemBelow(TickType tick, Func&& func) {
|
||||
static constexpr bool RETURNS_BOOL =
|
||||
std::is_same_v<std::invoke_result<Func, ObjectType>, bool>;
|
||||
Item* iterator = first_item;
|
||||
while (iterator) {
|
||||
if (static_cast<s64>(tick) - static_cast<s64>(iterator->tick) < 0) {
|
||||
return;
|
||||
}
|
||||
Item* next = iterator->next;
|
||||
if constexpr (RETURNS_BOOL) {
|
||||
if (func(iterator->obj)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
func(iterator->obj);
|
||||
}
|
||||
iterator = next;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
size_t Build() {
|
||||
if (free_items.empty()) {
|
||||
const size_t item_id = item_pool.size();
|
||||
auto& item = item_pool.emplace_back();
|
||||
item.next = nullptr;
|
||||
item.prev = nullptr;
|
||||
return item_id;
|
||||
}
|
||||
const size_t item_id = free_items.front();
|
||||
free_items.pop_front();
|
||||
auto& item = item_pool[item_id];
|
||||
item.next = nullptr;
|
||||
item.prev = nullptr;
|
||||
return item_id;
|
||||
}
|
||||
|
||||
void Attach(Item& item) {
|
||||
if (!first_item) {
|
||||
first_item = &item;
|
||||
}
|
||||
if (!last_item) {
|
||||
last_item = &item;
|
||||
} else {
|
||||
item.prev = last_item;
|
||||
last_item->next = &item;
|
||||
item.next = nullptr;
|
||||
last_item = &item;
|
||||
}
|
||||
}
|
||||
|
||||
void Detach(Item& item) {
|
||||
if (item.prev) {
|
||||
item.prev->next = item.next;
|
||||
}
|
||||
if (item.next) {
|
||||
item.next->prev = item.prev;
|
||||
}
|
||||
if (&item == first_item) {
|
||||
first_item = item.next;
|
||||
if (first_item) {
|
||||
first_item->prev = nullptr;
|
||||
}
|
||||
}
|
||||
if (&item == last_item) {
|
||||
last_item = item.prev;
|
||||
if (last_item) {
|
||||
last_item->next = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::deque<Item> item_pool;
|
||||
std::deque<size_t> free_items;
|
||||
Item* first_item{};
|
||||
Item* last_item{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -5,8 +5,6 @@
|
||||
#include <codecvt>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <pugixml.hpp>
|
||||
#ifdef ENABLE_QT_GUI
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
@@ -16,10 +14,10 @@
|
||||
#include <QMessageBox>
|
||||
#include <QString>
|
||||
#include <QXmlStreamReader>
|
||||
#endif
|
||||
#include "common/elf_info.h"
|
||||
#include <pugixml.hpp>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/path_util.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/file_format/psf.h"
|
||||
#include "memory_patcher.h"
|
||||
|
||||
@@ -206,7 +204,6 @@ void OnGameLoaded() {
|
||||
}
|
||||
ApplyPendingPatches();
|
||||
|
||||
#ifdef ENABLE_QT_GUI
|
||||
// We use the QT headers for the xml and json parsing, this define is only true on QT builds
|
||||
QString patchDir;
|
||||
Common::FS::PathToQString(patchDir, Common::FS::GetUserPath(Common::FS::PathType::PatchesDir));
|
||||
@@ -371,7 +368,6 @@ void OnGameLoaded() {
|
||||
}
|
||||
ApplyPendingPatches();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void AddPatchToQueue(patchInfo patchToAdd) {
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "ntapi.h"
|
||||
|
||||
NtClose_t NtClose = nullptr;
|
||||
NtSetInformationFile_t NtSetInformationFile = nullptr;
|
||||
NtCreateThread_t NtCreateThread = nullptr;
|
||||
NtTerminateThread_t NtTerminateThread = nullptr;
|
||||
NtQueueApcThreadEx_t NtQueueApcThreadEx = nullptr;
|
||||
|
||||
namespace Common::NtApi {
|
||||
|
||||
void Initialize() {
|
||||
HMODULE nt_handle = GetModuleHandleA("ntdll.dll");
|
||||
|
||||
// http://stackoverflow.com/a/31411628/4725495
|
||||
NtClose = (NtClose_t)GetProcAddress(nt_handle, "NtClose");
|
||||
NtSetInformationFile =
|
||||
(NtSetInformationFile_t)GetProcAddress(nt_handle, "NtSetInformationFile");
|
||||
NtCreateThread = (NtCreateThread_t)GetProcAddress(nt_handle, "NtCreateThread");
|
||||
NtTerminateThread = (NtTerminateThread_t)GetProcAddress(nt_handle, "NtTerminateThread");
|
||||
NtQueueApcThreadEx = (NtQueueApcThreadEx_t)GetProcAddress(nt_handle, "NtQueueApcThreadEx");
|
||||
}
|
||||
|
||||
} // namespace Common::NtApi
|
||||
|
||||
#endif
|
||||
@@ -1,554 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <windows.h>
|
||||
#include "common/types.h"
|
||||
|
||||
typedef enum _FILE_INFORMATION_CLASS {
|
||||
FileDirectoryInformation = 1,
|
||||
FileFullDirectoryInformation = 2,
|
||||
FileBothDirectoryInformation = 3,
|
||||
FileBasicInformation = 4,
|
||||
FileStandardInformation = 5,
|
||||
FileInternalInformation = 6,
|
||||
FileEaInformation = 7,
|
||||
FileAccessInformation = 8,
|
||||
FileNameInformation = 9,
|
||||
FileRenameInformation = 10,
|
||||
FileLinkInformation = 11,
|
||||
FileNamesInformation = 12,
|
||||
FileDispositionInformation = 13,
|
||||
FilePositionInformation = 14,
|
||||
FileFullEaInformation = 15,
|
||||
FileModeInformation = 16,
|
||||
FileAlignmentInformation = 17,
|
||||
FileAllInformation = 18,
|
||||
FileAllocationInformation = 19,
|
||||
FileEndOfFileInformation = 20,
|
||||
FileAlternateNameInformation = 21,
|
||||
FileStreamInformation = 22,
|
||||
FilePipeInformation = 23,
|
||||
FilePipeLocalInformation = 24,
|
||||
FilePipeRemoteInformation = 25,
|
||||
FileMailslotQueryInformation = 26,
|
||||
FileMailslotSetInformation = 27,
|
||||
FileCompressionInformation = 28,
|
||||
FileObjectIdInformation = 29,
|
||||
FileCompletionInformation = 30,
|
||||
FileMoveClusterInformation = 31,
|
||||
FileQuotaInformation = 32,
|
||||
FileReparsePointInformation = 33,
|
||||
FileNetworkOpenInformation = 34,
|
||||
FileAttributeTagInformation = 35,
|
||||
FileTrackingInformation = 36,
|
||||
FileIdBothDirectoryInformation = 37,
|
||||
FileIdFullDirectoryInformation = 38,
|
||||
FileValidDataLengthInformation = 39,
|
||||
FileShortNameInformation = 40,
|
||||
FileIoCompletionNotificationInformation = 41,
|
||||
FileIoStatusBlockRangeInformation = 42,
|
||||
FileIoPriorityHintInformation = 43,
|
||||
FileSfioReserveInformation = 44,
|
||||
FileSfioVolumeInformation = 45,
|
||||
FileHardLinkInformation = 46,
|
||||
FileProcessIdsUsingFileInformation = 47,
|
||||
FileNormalizedNameInformation = 48,
|
||||
FileNetworkPhysicalNameInformation = 49,
|
||||
FileIdGlobalTxDirectoryInformation = 50,
|
||||
FileIsRemoteDeviceInformation = 51,
|
||||
FileUnusedInformation = 52,
|
||||
FileNumaNodeInformation = 53,
|
||||
FileStandardLinkInformation = 54,
|
||||
FileRemoteProtocolInformation = 55,
|
||||
FileRenameInformationBypassAccessCheck = 56,
|
||||
FileLinkInformationBypassAccessCheck = 57,
|
||||
FileVolumeNameInformation = 58,
|
||||
FileIdInformation = 59,
|
||||
FileIdExtdDirectoryInformation = 60,
|
||||
FileReplaceCompletionInformation = 61,
|
||||
FileHardLinkFullIdInformation = 62,
|
||||
FileIdExtdBothDirectoryInformation = 63,
|
||||
FileDispositionInformationEx = 64,
|
||||
FileRenameInformationEx = 65,
|
||||
FileRenameInformationExBypassAccessCheck = 66,
|
||||
FileDesiredStorageClassInformation = 67,
|
||||
FileStatInformation = 68,
|
||||
FileMemoryPartitionInformation = 69,
|
||||
FileStatLxInformation = 70,
|
||||
FileCaseSensitiveInformation = 71,
|
||||
FileLinkInformationEx = 72,
|
||||
FileLinkInformationExBypassAccessCheck = 73,
|
||||
FileStorageReserveIdInformation = 74,
|
||||
FileCaseSensitiveInformationForceAccessCheck = 75,
|
||||
FileKnownFolderInformation = 76,
|
||||
FileStatBasicInformation = 77,
|
||||
FileId64ExtdDirectoryInformation = 78,
|
||||
FileId64ExtdBothDirectoryInformation = 79,
|
||||
FileIdAllExtdDirectoryInformation = 80,
|
||||
FileIdAllExtdBothDirectoryInformation = 81,
|
||||
FileStreamReservationInformation,
|
||||
FileMupProviderInfo,
|
||||
FileMaximumInformation
|
||||
} FILE_INFORMATION_CLASS,
|
||||
*PFILE_INFORMATION_CLASS;
|
||||
|
||||
typedef struct _IO_STATUS_BLOCK {
|
||||
union {
|
||||
u32 Status;
|
||||
PVOID Pointer;
|
||||
};
|
||||
ULONG_PTR Information;
|
||||
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
|
||||
|
||||
typedef struct _FILE_DISPOSITION_INFORMATION {
|
||||
BOOLEAN DeleteFile;
|
||||
} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION;
|
||||
|
||||
typedef struct _UNICODE_STRING {
|
||||
USHORT Length;
|
||||
USHORT MaximumLength;
|
||||
PWCH Buffer;
|
||||
} UNICODE_STRING, *PUNICODE_STRING;
|
||||
|
||||
typedef const UNICODE_STRING* PCUNICODE_STRING;
|
||||
|
||||
typedef struct _OBJECT_ATTRIBUTES {
|
||||
ULONG Length;
|
||||
HANDLE RootDirectory;
|
||||
PCUNICODE_STRING ObjectName;
|
||||
ULONG Attributes;
|
||||
PVOID SecurityDescriptor; // PSECURITY_DESCRIPTOR;
|
||||
PVOID SecurityQualityOfService; // PSECURITY_QUALITY_OF_SERVICE
|
||||
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
|
||||
|
||||
typedef const OBJECT_ATTRIBUTES* PCOBJECT_ATTRIBUTES;
|
||||
|
||||
typedef struct _CLIENT_ID {
|
||||
HANDLE UniqueProcess;
|
||||
HANDLE UniqueThread;
|
||||
} CLIENT_ID, *PCLIENT_ID;
|
||||
|
||||
typedef struct _INITIAL_TEB {
|
||||
struct {
|
||||
PVOID OldStackBase;
|
||||
PVOID OldStackLimit;
|
||||
} OldInitialTeb;
|
||||
PVOID StackBase;
|
||||
PVOID StackLimit;
|
||||
PVOID StackAllocationBase;
|
||||
} INITIAL_TEB, *PINITIAL_TEB;
|
||||
|
||||
typedef struct _PEB_LDR_DATA {
|
||||
ULONG Length;
|
||||
BOOLEAN Initialized;
|
||||
PVOID SsHandle;
|
||||
LIST_ENTRY InLoadOrderModuleList;
|
||||
LIST_ENTRY InMemoryOrderModuleList;
|
||||
LIST_ENTRY InInitializationOrderModuleList;
|
||||
PVOID EntryInProgress;
|
||||
BOOLEAN ShutdownInProgress;
|
||||
HANDLE ShutdownThreadId;
|
||||
} PEB_LDR_DATA, *PPEB_LDR_DATA;
|
||||
|
||||
typedef struct _CURDIR {
|
||||
UNICODE_STRING DosPath;
|
||||
PVOID Handle;
|
||||
} CURDIR, *PCURDIR;
|
||||
|
||||
typedef struct RTL_DRIVE_LETTER_CURDIR {
|
||||
USHORT Flags;
|
||||
USHORT Length;
|
||||
ULONG TimeStamp;
|
||||
UNICODE_STRING DosPath;
|
||||
} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR;
|
||||
|
||||
typedef struct _RTL_USER_PROCESS_PARAMETERS {
|
||||
ULONG AllocationSize;
|
||||
ULONG Size;
|
||||
ULONG Flags;
|
||||
ULONG DebugFlags;
|
||||
HANDLE ConsoleHandle;
|
||||
ULONG ConsoleFlags;
|
||||
HANDLE hStdInput;
|
||||
HANDLE hStdOutput;
|
||||
HANDLE hStdError;
|
||||
CURDIR CurrentDirectory;
|
||||
UNICODE_STRING DllPath;
|
||||
UNICODE_STRING ImagePathName;
|
||||
UNICODE_STRING CommandLine;
|
||||
PWSTR Environment;
|
||||
ULONG dwX;
|
||||
ULONG dwY;
|
||||
ULONG dwXSize;
|
||||
ULONG dwYSize;
|
||||
ULONG dwXCountChars;
|
||||
ULONG dwYCountChars;
|
||||
ULONG dwFillAttribute;
|
||||
ULONG dwFlags;
|
||||
ULONG wShowWindow;
|
||||
UNICODE_STRING WindowTitle;
|
||||
UNICODE_STRING Desktop;
|
||||
UNICODE_STRING ShellInfo;
|
||||
UNICODE_STRING RuntimeInfo;
|
||||
RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20];
|
||||
ULONG_PTR EnvironmentSize;
|
||||
ULONG_PTR EnvironmentVersion;
|
||||
PVOID PackageDependencyData;
|
||||
ULONG ProcessGroupId;
|
||||
ULONG LoaderThreads;
|
||||
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
|
||||
|
||||
typedef struct tagRTL_BITMAP {
|
||||
ULONG SizeOfBitMap;
|
||||
PULONG Buffer;
|
||||
} RTL_BITMAP, *PRTL_BITMAP;
|
||||
|
||||
typedef struct {
|
||||
UINT next;
|
||||
UINT id;
|
||||
ULONGLONG addr;
|
||||
ULONGLONG size;
|
||||
UINT args[4];
|
||||
} CROSS_PROCESS_WORK_ENTRY;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
UINT first;
|
||||
UINT counter;
|
||||
};
|
||||
volatile LONGLONG hdr;
|
||||
} CROSS_PROCESS_WORK_HDR;
|
||||
|
||||
typedef struct {
|
||||
CROSS_PROCESS_WORK_HDR free_list;
|
||||
CROSS_PROCESS_WORK_HDR work_list;
|
||||
ULONGLONG unknown[4];
|
||||
CROSS_PROCESS_WORK_ENTRY entries[1];
|
||||
} CROSS_PROCESS_WORK_LIST;
|
||||
|
||||
typedef struct _CHPEV2_PROCESS_INFO {
|
||||
ULONG Wow64ExecuteFlags; /* 000 */
|
||||
USHORT NativeMachineType; /* 004 */
|
||||
USHORT EmulatedMachineType; /* 006 */
|
||||
HANDLE SectionHandle; /* 008 */
|
||||
CROSS_PROCESS_WORK_LIST* CrossProcessWorkList; /* 010 */
|
||||
void* unknown; /* 018 */
|
||||
} CHPEV2_PROCESS_INFO, *PCHPEV2_PROCESS_INFO;
|
||||
|
||||
typedef u64(__stdcall* KERNEL_CALLBACK_PROC)(void*, ULONG);
|
||||
|
||||
typedef struct _PEB { /* win32/win64 */
|
||||
BOOLEAN InheritedAddressSpace; /* 000/000 */
|
||||
BOOLEAN ReadImageFileExecOptions; /* 001/001 */
|
||||
BOOLEAN BeingDebugged; /* 002/002 */
|
||||
UCHAR ImageUsedLargePages : 1; /* 003/003 */
|
||||
UCHAR IsProtectedProcess : 1;
|
||||
UCHAR IsImageDynamicallyRelocated : 1;
|
||||
UCHAR SkipPatchingUser32Forwarders : 1;
|
||||
UCHAR IsPackagedProcess : 1;
|
||||
UCHAR IsAppContainer : 1;
|
||||
UCHAR IsProtectedProcessLight : 1;
|
||||
UCHAR IsLongPathAwareProcess : 1;
|
||||
HANDLE Mutant; /* 004/008 */
|
||||
HMODULE ImageBaseAddress; /* 008/010 */
|
||||
PPEB_LDR_DATA LdrData; /* 00c/018 */
|
||||
RTL_USER_PROCESS_PARAMETERS* ProcessParameters; /* 010/020 */
|
||||
PVOID SubSystemData; /* 014/028 */
|
||||
HANDLE ProcessHeap; /* 018/030 */
|
||||
PRTL_CRITICAL_SECTION FastPebLock; /* 01c/038 */
|
||||
PVOID AtlThunkSListPtr; /* 020/040 */
|
||||
PVOID IFEOKey; /* 024/048 */
|
||||
ULONG ProcessInJob : 1; /* 028/050 */
|
||||
ULONG ProcessInitializing : 1;
|
||||
ULONG ProcessUsingVEH : 1;
|
||||
ULONG ProcessUsingVCH : 1;
|
||||
ULONG ProcessUsingFTH : 1;
|
||||
ULONG ProcessPreviouslyThrottled : 1;
|
||||
ULONG ProcessCurrentlyThrottled : 1;
|
||||
ULONG ProcessImagesHotPatched : 1;
|
||||
ULONG ReservedBits0 : 24;
|
||||
KERNEL_CALLBACK_PROC* KernelCallbackTable; /* 02c/058 */
|
||||
ULONG Reserved; /* 030/060 */
|
||||
ULONG AtlThunkSListPtr32; /* 034/064 */
|
||||
PVOID ApiSetMap; /* 038/068 */
|
||||
ULONG TlsExpansionCounter; /* 03c/070 */
|
||||
PRTL_BITMAP TlsBitmap; /* 040/078 */
|
||||
ULONG TlsBitmapBits[2]; /* 044/080 */
|
||||
PVOID ReadOnlySharedMemoryBase; /* 04c/088 */
|
||||
PVOID SharedData; /* 050/090 */
|
||||
PVOID* ReadOnlyStaticServerData; /* 054/098 */
|
||||
PVOID AnsiCodePageData; /* 058/0a0 */
|
||||
PVOID OemCodePageData; /* 05c/0a8 */
|
||||
PVOID UnicodeCaseTableData; /* 060/0b0 */
|
||||
ULONG NumberOfProcessors; /* 064/0b8 */
|
||||
ULONG NtGlobalFlag; /* 068/0bc */
|
||||
LARGE_INTEGER CriticalSectionTimeout; /* 070/0c0 */
|
||||
SIZE_T HeapSegmentReserve; /* 078/0c8 */
|
||||
SIZE_T HeapSegmentCommit; /* 07c/0d0 */
|
||||
SIZE_T HeapDeCommitTotalFreeThreshold; /* 080/0d8 */
|
||||
SIZE_T HeapDeCommitFreeBlockThreshold; /* 084/0e0 */
|
||||
ULONG NumberOfHeaps; /* 088/0e8 */
|
||||
ULONG MaximumNumberOfHeaps; /* 08c/0ec */
|
||||
PVOID* ProcessHeaps; /* 090/0f0 */
|
||||
PVOID GdiSharedHandleTable; /* 094/0f8 */
|
||||
PVOID ProcessStarterHelper; /* 098/100 */
|
||||
PVOID GdiDCAttributeList; /* 09c/108 */
|
||||
PVOID LoaderLock; /* 0a0/110 */
|
||||
ULONG OSMajorVersion; /* 0a4/118 */
|
||||
ULONG OSMinorVersion; /* 0a8/11c */
|
||||
ULONG OSBuildNumber; /* 0ac/120 */
|
||||
ULONG OSPlatformId; /* 0b0/124 */
|
||||
ULONG ImageSubSystem; /* 0b4/128 */
|
||||
ULONG ImageSubSystemMajorVersion; /* 0b8/12c */
|
||||
ULONG ImageSubSystemMinorVersion; /* 0bc/130 */
|
||||
KAFFINITY ActiveProcessAffinityMask; /* 0c0/138 */
|
||||
#ifdef _WIN64
|
||||
ULONG GdiHandleBuffer[60]; /* /140 */
|
||||
#else
|
||||
ULONG GdiHandleBuffer[34]; /* 0c4/ */
|
||||
#endif
|
||||
PVOID PostProcessInitRoutine; /* 14c/230 */
|
||||
PRTL_BITMAP TlsExpansionBitmap; /* 150/238 */
|
||||
ULONG TlsExpansionBitmapBits[32]; /* 154/240 */
|
||||
ULONG SessionId; /* 1d4/2c0 */
|
||||
ULARGE_INTEGER AppCompatFlags; /* 1d8/2c8 */
|
||||
ULARGE_INTEGER AppCompatFlagsUser; /* 1e0/2d0 */
|
||||
PVOID ShimData; /* 1e8/2d8 */
|
||||
PVOID AppCompatInfo; /* 1ec/2e0 */
|
||||
UNICODE_STRING CSDVersion; /* 1f0/2e8 */
|
||||
PVOID ActivationContextData; /* 1f8/2f8 */
|
||||
PVOID ProcessAssemblyStorageMap; /* 1fc/300 */
|
||||
PVOID SystemDefaultActivationData; /* 200/308 */
|
||||
PVOID SystemAssemblyStorageMap; /* 204/310 */
|
||||
SIZE_T MinimumStackCommit; /* 208/318 */
|
||||
PVOID* FlsCallback; /* 20c/320 */
|
||||
LIST_ENTRY FlsListHead; /* 210/328 */
|
||||
union {
|
||||
PRTL_BITMAP FlsBitmap; /* 218/338 */
|
||||
#ifdef _WIN64
|
||||
CHPEV2_PROCESS_INFO* ChpeV2ProcessInfo; /* /338 */
|
||||
#endif
|
||||
};
|
||||
ULONG FlsBitmapBits[4]; /* 21c/340 */
|
||||
ULONG FlsHighIndex; /* 22c/350 */
|
||||
PVOID WerRegistrationData; /* 230/358 */
|
||||
PVOID WerShipAssertPtr; /* 234/360 */
|
||||
PVOID EcCodeBitMap; /* 238/368 */
|
||||
PVOID pImageHeaderHash; /* 23c/370 */
|
||||
ULONG HeapTracingEnabled : 1; /* 240/378 */
|
||||
ULONG CritSecTracingEnabled : 1;
|
||||
ULONG LibLoaderTracingEnabled : 1;
|
||||
ULONG SpareTracingBits : 29;
|
||||
ULONGLONG CsrServerReadOnlySharedMemoryBase; /* 248/380 */
|
||||
ULONG TppWorkerpListLock; /* 250/388 */
|
||||
LIST_ENTRY TppWorkerpList; /* 254/390 */
|
||||
PVOID WaitOnAddressHashTable[0x80]; /* 25c/3a0 */
|
||||
PVOID TelemetryCoverageHeader; /* 45c/7a0 */
|
||||
ULONG CloudFileFlags; /* 460/7a8 */
|
||||
ULONG CloudFileDiagFlags; /* 464/7ac */
|
||||
CHAR PlaceholderCompatibilityMode; /* 468/7b0 */
|
||||
CHAR PlaceholderCompatibilityModeReserved[7]; /* 469/7b1 */
|
||||
PVOID LeapSecondData; /* 470/7b8 */
|
||||
ULONG LeapSecondFlags; /* 474/7c0 */
|
||||
ULONG NtGlobalFlag2; /* 478/7c4 */
|
||||
} PEB, *PPEB;
|
||||
|
||||
typedef struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME {
|
||||
struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME* Previous;
|
||||
struct _ACTIVATION_CONTEXT* ActivationContext;
|
||||
ULONG Flags;
|
||||
} RTL_ACTIVATION_CONTEXT_STACK_FRAME, *PRTL_ACTIVATION_CONTEXT_STACK_FRAME;
|
||||
|
||||
typedef struct _ACTIVATION_CONTEXT_STACK {
|
||||
RTL_ACTIVATION_CONTEXT_STACK_FRAME* ActiveFrame;
|
||||
LIST_ENTRY FrameListCache;
|
||||
ULONG Flags;
|
||||
ULONG NextCookieSequenceNumber;
|
||||
ULONG_PTR StackId;
|
||||
} ACTIVATION_CONTEXT_STACK, *PACTIVATION_CONTEXT_STACK;
|
||||
|
||||
typedef struct _GDI_TEB_BATCH {
|
||||
ULONG Offset;
|
||||
HANDLE HDC;
|
||||
ULONG Buffer[0x136];
|
||||
} GDI_TEB_BATCH;
|
||||
|
||||
typedef struct _TEB_ACTIVE_FRAME_CONTEXT {
|
||||
ULONG Flags;
|
||||
const char* FrameName;
|
||||
} TEB_ACTIVE_FRAME_CONTEXT, *PTEB_ACTIVE_FRAME_CONTEXT;
|
||||
|
||||
typedef struct _TEB_ACTIVE_FRAME {
|
||||
ULONG Flags;
|
||||
struct _TEB_ACTIVE_FRAME* Previous;
|
||||
TEB_ACTIVE_FRAME_CONTEXT* Context;
|
||||
} TEB_ACTIVE_FRAME, *PTEB_ACTIVE_FRAME;
|
||||
|
||||
typedef struct _TEB { /* win32/win64 */
|
||||
NT_TIB Tib; /* 000/0000 */
|
||||
PVOID EnvironmentPointer; /* 01c/0038 */
|
||||
CLIENT_ID ClientId; /* 020/0040 */
|
||||
PVOID ActiveRpcHandle; /* 028/0050 */
|
||||
PVOID ThreadLocalStoragePointer; /* 02c/0058 */
|
||||
PPEB Peb; /* 030/0060 */
|
||||
ULONG LastErrorValue; /* 034/0068 */
|
||||
ULONG CountOfOwnedCriticalSections; /* 038/006c */
|
||||
PVOID CsrClientThread; /* 03c/0070 */
|
||||
PVOID Win32ThreadInfo; /* 040/0078 */
|
||||
ULONG User32Reserved[26]; /* 044/0080 */
|
||||
ULONG UserReserved[5]; /* 0ac/00e8 */
|
||||
PVOID WOW32Reserved; /* 0c0/0100 */
|
||||
ULONG CurrentLocale; /* 0c4/0108 */
|
||||
ULONG FpSoftwareStatusRegister; /* 0c8/010c */
|
||||
PVOID ReservedForDebuggerInstrumentation[16]; /* 0cc/0110 */
|
||||
#ifdef _WIN64
|
||||
PVOID SystemReserved1[30]; /* /0190 */
|
||||
#else
|
||||
PVOID SystemReserved1[26]; /* 10c/ */
|
||||
#endif
|
||||
char PlaceholderCompatibilityMode; /* 174/0280 */
|
||||
BOOLEAN PlaceholderHydrationAlwaysExplicit; /* 175/0281 */
|
||||
char PlaceholderReserved[10]; /* 176/0282 */
|
||||
DWORD ProxiedProcessId; /* 180/028c */
|
||||
ACTIVATION_CONTEXT_STACK ActivationContextStack; /* 184/0290 */
|
||||
UCHAR WorkingOnBehalfOfTicket[8]; /* 19c/02b8 */
|
||||
LONG ExceptionCode; /* 1a4/02c0 */
|
||||
ACTIVATION_CONTEXT_STACK* ActivationContextStackPointer; /* 1a8/02c8 */
|
||||
ULONG_PTR InstrumentationCallbackSp; /* 1ac/02d0 */
|
||||
ULONG_PTR InstrumentationCallbackPreviousPc; /* 1b0/02d8 */
|
||||
ULONG_PTR InstrumentationCallbackPreviousSp; /* 1b4/02e0 */
|
||||
#ifdef _WIN64
|
||||
ULONG TxFsContext; /* /02e8 */
|
||||
BOOLEAN InstrumentationCallbackDisabled; /* /02ec */
|
||||
BOOLEAN UnalignedLoadStoreExceptions; /* /02ed */
|
||||
#else
|
||||
BOOLEAN InstrumentationCallbackDisabled; /* 1b8/ */
|
||||
BYTE SpareBytes1[23]; /* 1b9/ */
|
||||
ULONG TxFsContext; /* 1d0/ */
|
||||
#endif
|
||||
GDI_TEB_BATCH GdiTebBatch; /* 1d4/02f0 */
|
||||
CLIENT_ID RealClientId; /* 6b4/07d8 */
|
||||
HANDLE GdiCachedProcessHandle; /* 6bc/07e8 */
|
||||
ULONG GdiClientPID; /* 6c0/07f0 */
|
||||
ULONG GdiClientTID; /* 6c4/07f4 */
|
||||
PVOID GdiThreadLocaleInfo; /* 6c8/07f8 */
|
||||
ULONG_PTR Win32ClientInfo[62]; /* 6cc/0800 */
|
||||
PVOID glDispatchTable[233]; /* 7c4/09f0 */
|
||||
PVOID glReserved1[29]; /* b68/1138 */
|
||||
PVOID glReserved2; /* bdc/1220 */
|
||||
PVOID glSectionInfo; /* be0/1228 */
|
||||
PVOID glSection; /* be4/1230 */
|
||||
PVOID glTable; /* be8/1238 */
|
||||
PVOID glCurrentRC; /* bec/1240 */
|
||||
PVOID glContext; /* bf0/1248 */
|
||||
ULONG LastStatusValue; /* bf4/1250 */
|
||||
UNICODE_STRING StaticUnicodeString; /* bf8/1258 */
|
||||
WCHAR StaticUnicodeBuffer[261]; /* c00/1268 */
|
||||
PVOID DeallocationStack; /* e0c/1478 */
|
||||
PVOID TlsSlots[64]; /* e10/1480 */
|
||||
LIST_ENTRY TlsLinks; /* f10/1680 */
|
||||
PVOID Vdm; /* f18/1690 */
|
||||
PVOID ReservedForNtRpc; /* f1c/1698 */
|
||||
PVOID DbgSsReserved[2]; /* f20/16a0 */
|
||||
ULONG HardErrorMode; /* f28/16b0 */
|
||||
#ifdef _WIN64
|
||||
PVOID Instrumentation[11]; /* /16b8 */
|
||||
#else
|
||||
PVOID Instrumentation[9]; /* f2c/ */
|
||||
#endif
|
||||
GUID ActivityId; /* f50/1710 */
|
||||
PVOID SubProcessTag; /* f60/1720 */
|
||||
PVOID PerflibData; /* f64/1728 */
|
||||
PVOID EtwTraceData; /* f68/1730 */
|
||||
PVOID WinSockData; /* f6c/1738 */
|
||||
ULONG GdiBatchCount; /* f70/1740 */
|
||||
ULONG IdealProcessorValue; /* f74/1744 */
|
||||
ULONG GuaranteedStackBytes; /* f78/1748 */
|
||||
PVOID ReservedForPerf; /* f7c/1750 */
|
||||
PVOID ReservedForOle; /* f80/1758 */
|
||||
ULONG WaitingOnLoaderLock; /* f84/1760 */
|
||||
PVOID SavedPriorityState; /* f88/1768 */
|
||||
ULONG_PTR ReservedForCodeCoverage; /* f8c/1770 */
|
||||
PVOID ThreadPoolData; /* f90/1778 */
|
||||
PVOID* TlsExpansionSlots; /* f94/1780 */
|
||||
#ifdef _WIN64
|
||||
union {
|
||||
PVOID DeallocationBStore; /* /1788 */
|
||||
PVOID* ChpeV2CpuAreaInfo; /* /1788 */
|
||||
} DUMMYUNIONNAME;
|
||||
PVOID BStoreLimit; /* /1790 */
|
||||
#endif
|
||||
ULONG MuiGeneration; /* f98/1798 */
|
||||
ULONG IsImpersonating; /* f9c/179c */
|
||||
PVOID NlsCache; /* fa0/17a0 */
|
||||
PVOID ShimData; /* fa4/17a8 */
|
||||
ULONG HeapVirtualAffinity; /* fa8/17b0 */
|
||||
PVOID CurrentTransactionHandle; /* fac/17b8 */
|
||||
TEB_ACTIVE_FRAME* ActiveFrame; /* fb0/17c0 */
|
||||
PVOID* FlsSlots; /* fb4/17c8 */
|
||||
PVOID PreferredLanguages; /* fb8/17d0 */
|
||||
PVOID UserPrefLanguages; /* fbc/17d8 */
|
||||
PVOID MergedPrefLanguages; /* fc0/17e0 */
|
||||
ULONG MuiImpersonation; /* fc4/17e8 */
|
||||
USHORT CrossTebFlags; /* fc8/17ec */
|
||||
USHORT SameTebFlags; /* fca/17ee */
|
||||
PVOID TxnScopeEnterCallback; /* fcc/17f0 */
|
||||
PVOID TxnScopeExitCallback; /* fd0/17f8 */
|
||||
PVOID TxnScopeContext; /* fd4/1800 */
|
||||
ULONG LockCount; /* fd8/1808 */
|
||||
LONG WowTebOffset; /* fdc/180c */
|
||||
PVOID ResourceRetValue; /* fe0/1810 */
|
||||
PVOID ReservedForWdf; /* fe4/1818 */
|
||||
ULONGLONG ReservedForCrt; /* fe8/1820 */
|
||||
GUID EffectiveContainerId; /* ff0/1828 */
|
||||
} TEB, *PTEB;
|
||||
static_assert(offsetof(TEB, DeallocationStack) ==
|
||||
0x1478); /* The only member we care about at the moment */
|
||||
|
||||
typedef enum _QUEUE_USER_APC_FLAGS {
|
||||
QueueUserApcFlagsNone,
|
||||
QueueUserApcFlagsSpecialUserApc,
|
||||
QueueUserApcFlagsMaxValue
|
||||
} QUEUE_USER_APC_FLAGS;
|
||||
|
||||
typedef union _USER_APC_OPTION {
|
||||
ULONG_PTR UserApcFlags;
|
||||
HANDLE MemoryReserveHandle;
|
||||
} USER_APC_OPTION, *PUSER_APC_OPTION;
|
||||
|
||||
using PPS_APC_ROUTINE = void (*)(PVOID ApcArgument1, PVOID ApcArgument2, PVOID ApcArgument3,
|
||||
PCONTEXT Context);
|
||||
|
||||
typedef u64(__stdcall* NtClose_t)(HANDLE Handle);
|
||||
|
||||
typedef u64(__stdcall* NtSetInformationFile_t)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock,
|
||||
PVOID FileInformation, ULONG Length,
|
||||
FILE_INFORMATION_CLASS FileInformationClass);
|
||||
|
||||
typedef u64(__stdcall* NtCreateThread_t)(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess,
|
||||
PCOBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle,
|
||||
PCLIENT_ID ClientId, PCONTEXT ThreadContext,
|
||||
PINITIAL_TEB InitialTeb, BOOLEAN CreateSuspended);
|
||||
|
||||
typedef u64(__stdcall* NtTerminateThread_t)(HANDLE ThreadHandle, u64 ExitStatus);
|
||||
|
||||
typedef u64(__stdcall* NtQueueApcThreadEx_t)(HANDLE ThreadHandle,
|
||||
USER_APC_OPTION UserApcReserveHandle,
|
||||
PPS_APC_ROUTINE ApcRoutine, PVOID ApcArgument1,
|
||||
PVOID ApcArgument2, PVOID ApcArgument3);
|
||||
|
||||
extern NtClose_t NtClose;
|
||||
extern NtSetInformationFile_t NtSetInformationFile;
|
||||
extern NtCreateThread_t NtCreateThread;
|
||||
extern NtTerminateThread_t NtTerminateThread;
|
||||
extern NtQueueApcThreadEx_t NtQueueApcThreadEx;
|
||||
|
||||
namespace Common::NtApi {
|
||||
void Initialize();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,107 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename T>
|
||||
requires std::is_destructible_v<T>
|
||||
class ObjectPool {
|
||||
public:
|
||||
explicit ObjectPool(size_t chunk_size = 8192) : new_chunk_size{chunk_size} {
|
||||
node = &chunks.emplace_back(new_chunk_size);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
requires std::is_constructible_v<T, Args...>
|
||||
[[nodiscard]] T* Create(Args&&... args) {
|
||||
return std::construct_at(Memory(), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void ReleaseContents() {
|
||||
if (chunks.empty()) {
|
||||
return;
|
||||
}
|
||||
Chunk& root{chunks.front()};
|
||||
if (root.used_objects == root.num_objects) {
|
||||
// Root chunk has been filled, squash allocations into it
|
||||
const size_t total_objects{root.num_objects + new_chunk_size * (chunks.size() - 1)};
|
||||
chunks.clear();
|
||||
chunks.emplace_back(total_objects);
|
||||
} else {
|
||||
root.Release();
|
||||
chunks.resize(1);
|
||||
}
|
||||
chunks.shrink_to_fit();
|
||||
node = &chunks.front();
|
||||
}
|
||||
|
||||
private:
|
||||
struct NonTrivialDummy {
|
||||
NonTrivialDummy() noexcept {}
|
||||
};
|
||||
|
||||
union Storage {
|
||||
Storage() noexcept {}
|
||||
~Storage() noexcept {}
|
||||
|
||||
NonTrivialDummy dummy{};
|
||||
T object;
|
||||
};
|
||||
|
||||
struct Chunk {
|
||||
explicit Chunk() = default;
|
||||
explicit Chunk(size_t size)
|
||||
: num_objects{size}, storage{std::make_unique<Storage[]>(size)} {}
|
||||
|
||||
Chunk& operator=(Chunk&& rhs) noexcept {
|
||||
Release();
|
||||
used_objects = std::exchange(rhs.used_objects, 0);
|
||||
num_objects = std::exchange(rhs.num_objects, 0);
|
||||
storage = std::move(rhs.storage);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Chunk(Chunk&& rhs) noexcept
|
||||
: used_objects{std::exchange(rhs.used_objects, 0)},
|
||||
num_objects{std::exchange(rhs.num_objects, 0)}, storage{std::move(rhs.storage)} {}
|
||||
|
||||
~Chunk() {
|
||||
Release();
|
||||
}
|
||||
|
||||
void Release() {
|
||||
std::destroy_n(storage.get(), used_objects);
|
||||
used_objects = 0;
|
||||
}
|
||||
|
||||
size_t used_objects{};
|
||||
size_t num_objects{};
|
||||
std::unique_ptr<Storage[]> storage;
|
||||
};
|
||||
|
||||
[[nodiscard]] T* Memory() {
|
||||
Chunk* const chunk{FreeChunk()};
|
||||
return &chunk->storage[chunk->used_objects++].object;
|
||||
}
|
||||
|
||||
[[nodiscard]] Chunk* FreeChunk() {
|
||||
if (node->used_objects != node->num_objects) {
|
||||
return node;
|
||||
}
|
||||
node = &chunks.emplace_back(new_chunk_size);
|
||||
return node;
|
||||
}
|
||||
|
||||
Chunk* node{};
|
||||
std::vector<Chunk> chunks;
|
||||
size_t new_chunk_size{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <unordered_map>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/path_util.h"
|
||||
#include "common/scope_exit.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <CoreFoundation/CFBundle.h>
|
||||
@@ -25,9 +24,7 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_QT_GUI
|
||||
#include <QString>
|
||||
#endif
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
@@ -40,12 +37,9 @@ using CreateOriginalPathForURLFunc = CFURLRef __nullable (*)(CFURLRef translocat
|
||||
CFErrorRef* __nullable error);
|
||||
|
||||
static CFURLRef UntranslocateBundlePath(const CFURLRef bundle_path) {
|
||||
CFURLRef path = nullptr;
|
||||
if (void* security_handle =
|
||||
dlopen("/System/Library/Frameworks/Security.framework/Security", RTLD_LAZY)) {
|
||||
SCOPE_EXIT {
|
||||
dlclose(security_handle);
|
||||
};
|
||||
|
||||
const auto IsTranslocatedURL = reinterpret_cast<IsTranslocatedURLFunc>(
|
||||
dlsym(security_handle, "SecTranslocateIsTranslocatedURL"));
|
||||
const auto CreateOriginalPathForURL = reinterpret_cast<CreateOriginalPathForURLFunc>(
|
||||
@@ -54,41 +48,40 @@ static CFURLRef UntranslocateBundlePath(const CFURLRef bundle_path) {
|
||||
bool is_translocated = false;
|
||||
if (IsTranslocatedURL && CreateOriginalPathForURL &&
|
||||
IsTranslocatedURL(bundle_path, &is_translocated, nullptr) && is_translocated) {
|
||||
return CreateOriginalPathForURL(bundle_path, nullptr);
|
||||
path = CreateOriginalPathForURL(bundle_path, nullptr);
|
||||
}
|
||||
|
||||
dlclose(security_handle);
|
||||
}
|
||||
return nullptr;
|
||||
return path;
|
||||
}
|
||||
|
||||
static std::optional<std::filesystem::path> GetBundleParentDirectory() {
|
||||
std::optional<std::filesystem::path> path = std::nullopt;
|
||||
if (CFBundleRef bundle_ref = CFBundleGetMainBundle()) {
|
||||
if (CFURLRef bundle_url_ref = CFBundleCopyBundleURL(bundle_ref)) {
|
||||
SCOPE_EXIT {
|
||||
CFRelease(bundle_url_ref);
|
||||
};
|
||||
|
||||
CFURLRef untranslocated_url_ref = UntranslocateBundlePath(bundle_url_ref);
|
||||
SCOPE_EXIT {
|
||||
if (untranslocated_url_ref) {
|
||||
CFRelease(untranslocated_url_ref);
|
||||
}
|
||||
};
|
||||
|
||||
char app_bundle_path[MAXPATHLEN];
|
||||
if (CFURLGetFileSystemRepresentation(
|
||||
untranslocated_url_ref ? untranslocated_url_ref : bundle_url_ref, true,
|
||||
reinterpret_cast<u8*>(app_bundle_path), sizeof(app_bundle_path))) {
|
||||
std::filesystem::path bundle_path{app_bundle_path};
|
||||
return bundle_path.parent_path();
|
||||
path = bundle_path.parent_path();
|
||||
}
|
||||
|
||||
if (untranslocated_url_ref) {
|
||||
CFRelease(untranslocated_url_ref);
|
||||
}
|
||||
CFRelease(bundle_url_ref);
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
|
||||
static auto UserPaths = [] {
|
||||
#if defined(__APPLE__) && defined(ENABLE_QT_GUI)
|
||||
#if defined(__APPLE__)
|
||||
// Set the current path to the directory containing the app bundle.
|
||||
if (const auto bundle_dir = GetBundleParentDirectory()) {
|
||||
std::filesystem::current_path(*bundle_dir);
|
||||
@@ -229,7 +222,6 @@ std::optional<fs::path> FindGameByID(const fs::path& dir, const std::string& gam
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_QT_GUI
|
||||
void PathToQString(QString& result, const std::filesystem::path& path) {
|
||||
#ifdef _WIN32
|
||||
result = QString::fromStdWString(path.wstring());
|
||||
@@ -245,6 +237,5 @@ std::filesystem::path PathFromQString(const QString& path) {
|
||||
return std::filesystem::path(path.toStdString());
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Common::FS
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#ifdef ENABLE_QT_GUI
|
||||
class QString; // to avoid including <QString> in this header
|
||||
#endif
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
@@ -99,7 +97,6 @@ constexpr auto LOG_FILE = "shad_log.txt";
|
||||
*/
|
||||
void SetUserPath(PathType user_path, const std::filesystem::path& new_path);
|
||||
|
||||
#ifdef ENABLE_QT_GUI
|
||||
/**
|
||||
* Converts an std::filesystem::path to a QString.
|
||||
* The native underlying string of a path is wstring on Windows and string on POSIX.
|
||||
@@ -116,7 +113,6 @@ void PathToQString(QString& result, const std::filesystem::path& path);
|
||||
* @param path The path to convert
|
||||
*/
|
||||
[[nodiscard]] std::filesystem::path PathFromQString(const QString& path);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Recursively searches for a game directory by its ID.
|
||||
|
||||
@@ -1,377 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
//
|
||||
// TODO: remove this file when jthread is supported by all compilation targets
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <version>
|
||||
|
||||
#ifdef __cpp_lib_jthread
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <stop_token>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename Condvar, typename Lock, typename Pred>
|
||||
void CondvarWait(Condvar& cv, std::unique_lock<Lock>& lk, std::stop_token token, Pred&& pred) {
|
||||
cv.wait(lk, token, std::forward<Pred>(pred));
|
||||
}
|
||||
|
||||
template <typename Rep, typename Period>
|
||||
bool StoppableTimedWait(std::stop_token token, const std::chrono::duration<Rep, Period>& rel_time) {
|
||||
std::condition_variable_any cv;
|
||||
std::mutex m;
|
||||
|
||||
// Perform the timed wait.
|
||||
std::unique_lock lk{m};
|
||||
return !cv.wait_for(lk, token, rel_time, [&] { return token.stop_requested(); });
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#else
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace std {
|
||||
namespace polyfill {
|
||||
|
||||
using stop_state_callback = size_t;
|
||||
|
||||
class stop_state {
|
||||
public:
|
||||
stop_state() = default;
|
||||
~stop_state() = default;
|
||||
|
||||
bool request_stop() {
|
||||
unique_lock lk{m_lock};
|
||||
|
||||
if (m_stop_requested) {
|
||||
// Already set, nothing to do.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mark stop requested.
|
||||
m_stop_requested = true;
|
||||
|
||||
while (!m_callbacks.empty()) {
|
||||
// Get an iterator to the first element.
|
||||
const auto it = m_callbacks.begin();
|
||||
|
||||
// Move the callback function out of the map.
|
||||
function<void()> f;
|
||||
swap(it->second, f);
|
||||
|
||||
// Erase the now-empty map element.
|
||||
m_callbacks.erase(it);
|
||||
|
||||
// Run the callback.
|
||||
if (f) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool stop_requested() const {
|
||||
unique_lock lk{m_lock};
|
||||
return m_stop_requested;
|
||||
}
|
||||
|
||||
stop_state_callback insert_callback(function<void()> f) {
|
||||
unique_lock lk{m_lock};
|
||||
|
||||
if (m_stop_requested) {
|
||||
// Stop already requested. Don't insert anything,
|
||||
// just run the callback synchronously.
|
||||
if (f) {
|
||||
f();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Insert the callback.
|
||||
stop_state_callback ret = ++m_next_callback;
|
||||
m_callbacks.emplace(ret, std::move(f));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void remove_callback(stop_state_callback cb) {
|
||||
unique_lock lk{m_lock};
|
||||
m_callbacks.erase(cb);
|
||||
}
|
||||
|
||||
private:
|
||||
mutable recursive_mutex m_lock;
|
||||
map<stop_state_callback, function<void()>> m_callbacks;
|
||||
stop_state_callback m_next_callback{0};
|
||||
bool m_stop_requested{false};
|
||||
};
|
||||
|
||||
} // namespace polyfill
|
||||
|
||||
class stop_token;
|
||||
class stop_source;
|
||||
struct nostopstate_t {
|
||||
explicit nostopstate_t() = default;
|
||||
};
|
||||
inline constexpr nostopstate_t nostopstate{};
|
||||
|
||||
template <class Callback>
|
||||
class stop_callback;
|
||||
|
||||
class stop_token {
|
||||
public:
|
||||
stop_token() noexcept = default;
|
||||
|
||||
stop_token(const stop_token&) noexcept = default;
|
||||
stop_token(stop_token&&) noexcept = default;
|
||||
stop_token& operator=(const stop_token&) noexcept = default;
|
||||
stop_token& operator=(stop_token&&) noexcept = default;
|
||||
~stop_token() = default;
|
||||
|
||||
void swap(stop_token& other) noexcept {
|
||||
m_stop_state.swap(other.m_stop_state);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool stop_requested() const noexcept {
|
||||
return m_stop_state && m_stop_state->stop_requested();
|
||||
}
|
||||
[[nodiscard]] bool stop_possible() const noexcept {
|
||||
return m_stop_state != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class stop_source;
|
||||
template <typename Callback>
|
||||
friend class stop_callback;
|
||||
stop_token(shared_ptr<polyfill::stop_state> stop_state) : m_stop_state(std::move(stop_state)) {}
|
||||
|
||||
private:
|
||||
shared_ptr<polyfill::stop_state> m_stop_state;
|
||||
};
|
||||
|
||||
class stop_source {
|
||||
public:
|
||||
stop_source() : m_stop_state(make_shared<polyfill::stop_state>()) {}
|
||||
explicit stop_source(nostopstate_t) noexcept {}
|
||||
|
||||
stop_source(const stop_source&) noexcept = default;
|
||||
stop_source(stop_source&&) noexcept = default;
|
||||
stop_source& operator=(const stop_source&) noexcept = default;
|
||||
stop_source& operator=(stop_source&&) noexcept = default;
|
||||
~stop_source() = default;
|
||||
void swap(stop_source& other) noexcept {
|
||||
m_stop_state.swap(other.m_stop_state);
|
||||
}
|
||||
|
||||
[[nodiscard]] stop_token get_token() const noexcept {
|
||||
return stop_token(m_stop_state);
|
||||
}
|
||||
[[nodiscard]] bool stop_possible() const noexcept {
|
||||
return m_stop_state != nullptr;
|
||||
}
|
||||
[[nodiscard]] bool stop_requested() const noexcept {
|
||||
return m_stop_state && m_stop_state->stop_requested();
|
||||
}
|
||||
bool request_stop() noexcept {
|
||||
return m_stop_state && m_stop_state->request_stop();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class jthread;
|
||||
explicit stop_source(shared_ptr<polyfill::stop_state> stop_state)
|
||||
: m_stop_state(std::move(stop_state)) {}
|
||||
|
||||
private:
|
||||
shared_ptr<polyfill::stop_state> m_stop_state;
|
||||
};
|
||||
|
||||
template <typename Callback>
|
||||
class stop_callback {
|
||||
static_assert(is_nothrow_destructible_v<Callback>);
|
||||
static_assert(is_invocable_v<Callback>);
|
||||
|
||||
public:
|
||||
using callback_type = Callback;
|
||||
|
||||
template <typename C>
|
||||
requires constructible_from<Callback, C>
|
||||
explicit stop_callback(const stop_token& st,
|
||||
C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
|
||||
: m_stop_state(st.m_stop_state) {
|
||||
if (m_stop_state) {
|
||||
m_callback = m_stop_state->insert_callback(std::move(cb));
|
||||
}
|
||||
}
|
||||
template <typename C>
|
||||
requires constructible_from<Callback, C>
|
||||
explicit stop_callback(stop_token&& st,
|
||||
C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
|
||||
: m_stop_state(std::move(st.m_stop_state)) {
|
||||
if (m_stop_state) {
|
||||
m_callback = m_stop_state->insert_callback(std::move(cb));
|
||||
}
|
||||
}
|
||||
~stop_callback() {
|
||||
if (m_stop_state && m_callback) {
|
||||
m_stop_state->remove_callback(m_callback);
|
||||
}
|
||||
}
|
||||
|
||||
stop_callback(const stop_callback&) = delete;
|
||||
stop_callback(stop_callback&&) = delete;
|
||||
stop_callback& operator=(const stop_callback&) = delete;
|
||||
stop_callback& operator=(stop_callback&&) = delete;
|
||||
|
||||
private:
|
||||
shared_ptr<polyfill::stop_state> m_stop_state;
|
||||
polyfill::stop_state_callback m_callback;
|
||||
};
|
||||
|
||||
template <typename Callback>
|
||||
stop_callback(stop_token, Callback) -> stop_callback<Callback>;
|
||||
|
||||
class jthread {
|
||||
public:
|
||||
using id = thread::id;
|
||||
using native_handle_type = thread::native_handle_type;
|
||||
|
||||
jthread() noexcept = default;
|
||||
|
||||
template <typename F, typename... Args,
|
||||
typename = enable_if_t<!is_same_v<remove_cvref_t<F>, jthread>>>
|
||||
explicit jthread(F&& f, Args&&... args)
|
||||
: m_stop_state(make_shared<polyfill::stop_state>()),
|
||||
m_thread(make_thread(std::forward<F>(f), std::forward<Args>(args)...)) {}
|
||||
|
||||
~jthread() {
|
||||
if (joinable()) {
|
||||
request_stop();
|
||||
join();
|
||||
}
|
||||
}
|
||||
|
||||
jthread(const jthread&) = delete;
|
||||
jthread(jthread&&) noexcept = default;
|
||||
jthread& operator=(const jthread&) = delete;
|
||||
|
||||
jthread& operator=(jthread&& other) noexcept {
|
||||
m_thread.swap(other.m_thread);
|
||||
m_stop_state.swap(other.m_stop_state);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(jthread& other) noexcept {
|
||||
m_thread.swap(other.m_thread);
|
||||
m_stop_state.swap(other.m_stop_state);
|
||||
}
|
||||
[[nodiscard]] bool joinable() const noexcept {
|
||||
return m_thread.joinable();
|
||||
}
|
||||
void join() {
|
||||
m_thread.join();
|
||||
}
|
||||
void detach() {
|
||||
m_thread.detach();
|
||||
m_stop_state.reset();
|
||||
}
|
||||
|
||||
[[nodiscard]] id get_id() const noexcept {
|
||||
return m_thread.get_id();
|
||||
}
|
||||
[[nodiscard]] native_handle_type native_handle() {
|
||||
return m_thread.native_handle();
|
||||
}
|
||||
[[nodiscard]] stop_source get_stop_source() noexcept {
|
||||
return stop_source(m_stop_state);
|
||||
}
|
||||
[[nodiscard]] stop_token get_stop_token() const noexcept {
|
||||
return stop_source(m_stop_state).get_token();
|
||||
}
|
||||
bool request_stop() noexcept {
|
||||
return get_stop_source().request_stop();
|
||||
}
|
||||
[[nodiscard]] static unsigned int hardware_concurrency() noexcept {
|
||||
return thread::hardware_concurrency();
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename F, typename... Args>
|
||||
thread make_thread(F&& f, Args&&... args) {
|
||||
if constexpr (is_invocable_v<decay_t<F>, stop_token, decay_t<Args>...>) {
|
||||
return thread(std::forward<F>(f), get_stop_token(), std::forward<Args>(args)...);
|
||||
} else {
|
||||
return thread(std::forward<F>(f), std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<polyfill::stop_state> m_stop_state;
|
||||
thread m_thread;
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename Condvar, typename Lock, typename Pred>
|
||||
void CondvarWait(Condvar& cv, std::unique_lock<Lock>& lk, std::stop_token token, Pred pred) {
|
||||
if (token.stop_requested()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::stop_callback callback(token, [&] {
|
||||
{
|
||||
std::scoped_lock lk2{*lk.mutex()};
|
||||
}
|
||||
cv.notify_all();
|
||||
});
|
||||
|
||||
cv.wait(lk, [&] { return pred() || token.stop_requested(); });
|
||||
}
|
||||
|
||||
template <typename Rep, typename Period>
|
||||
bool StoppableTimedWait(std::stop_token token, const std::chrono::duration<Rep, Period>& rel_time) {
|
||||
if (token.stop_requested()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool stop_requested = false;
|
||||
std::condition_variable cv;
|
||||
std::mutex m;
|
||||
|
||||
std::stop_callback cb(token, [&] {
|
||||
// Wake up the waiting thread.
|
||||
{
|
||||
std::scoped_lock lk{m};
|
||||
stop_requested = true;
|
||||
}
|
||||
cv.notify_one();
|
||||
});
|
||||
|
||||
// Perform the timed wait.
|
||||
std::unique_lock lk{m};
|
||||
return !cv.wait_for(lk, rel_time, [&] { return stop_requested; });
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#endif
|
||||
@@ -1,37 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <unordered_map>
|
||||
#include "common/assert.h"
|
||||
#include "common/recursive_lock.h"
|
||||
|
||||
namespace Common::Detail {
|
||||
|
||||
struct RecursiveLockState {
|
||||
RecursiveLockType type;
|
||||
int count;
|
||||
};
|
||||
|
||||
thread_local std::unordered_map<void*, RecursiveLockState> g_recursive_locks;
|
||||
|
||||
bool IncrementRecursiveLock(void* mutex, RecursiveLockType type) {
|
||||
auto& state = g_recursive_locks[mutex];
|
||||
if (state.count == 0) {
|
||||
ASSERT(state.type == RecursiveLockType::None);
|
||||
state.type = type;
|
||||
}
|
||||
ASSERT(state.type == type);
|
||||
return state.count++ == 0;
|
||||
}
|
||||
|
||||
bool DecrementRecursiveLock(void* mutex, RecursiveLockType type) {
|
||||
auto& state = g_recursive_locks[mutex];
|
||||
ASSERT(state.type == type && state.count > 0);
|
||||
if (--state.count == 0) {
|
||||
g_recursive_locks.erase(mutex);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Common::Detail
|
||||
@@ -1,67 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <shared_mutex>
|
||||
|
||||
namespace Common {
|
||||
|
||||
namespace Detail {
|
||||
|
||||
enum class RecursiveLockType { None, Shared, Exclusive };
|
||||
|
||||
bool IncrementRecursiveLock(void* mutex, RecursiveLockType type);
|
||||
bool DecrementRecursiveLock(void* mutex, RecursiveLockType type);
|
||||
|
||||
} // namespace Detail
|
||||
|
||||
template <typename MutexType>
|
||||
class RecursiveScopedLock {
|
||||
public:
|
||||
explicit RecursiveScopedLock(MutexType& mutex) : m_mutex(mutex), m_locked(false) {
|
||||
if (Detail::IncrementRecursiveLock(&m_mutex, Detail::RecursiveLockType::Exclusive)) {
|
||||
m_locked = true;
|
||||
m_lock.emplace(m_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
~RecursiveScopedLock() {
|
||||
Detail::DecrementRecursiveLock(&m_mutex, Detail::RecursiveLockType::Exclusive);
|
||||
if (m_locked) {
|
||||
m_lock.reset();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
MutexType& m_mutex;
|
||||
std::optional<std::unique_lock<MutexType>> m_lock;
|
||||
bool m_locked = false;
|
||||
};
|
||||
|
||||
template <typename MutexType>
|
||||
class RecursiveSharedLock {
|
||||
public:
|
||||
explicit RecursiveSharedLock(MutexType& mutex) : m_mutex(mutex), m_locked(false) {
|
||||
if (Detail::IncrementRecursiveLock(&m_mutex, Detail::RecursiveLockType::Shared)) {
|
||||
m_locked = true;
|
||||
m_lock.emplace(m_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
~RecursiveSharedLock() {
|
||||
Detail::DecrementRecursiveLock(&m_mutex, Detail::RecursiveLockType::Shared);
|
||||
if (m_locked) {
|
||||
m_lock.reset();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
MutexType& m_mutex;
|
||||
std::optional<std::shared_lock<MutexType>> m_lock;
|
||||
bool m_locked = false;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,79 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace detail {
|
||||
template <class F>
|
||||
class ScopeGuard {
|
||||
private:
|
||||
F f;
|
||||
bool active;
|
||||
|
||||
public:
|
||||
constexpr ScopeGuard(F f_) : f(std::move(f_)), active(true) {}
|
||||
constexpr ~ScopeGuard() {
|
||||
if (active) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
constexpr void Cancel() {
|
||||
active = false;
|
||||
}
|
||||
|
||||
constexpr ScopeGuard(ScopeGuard&& rhs) : f(std::move(rhs.f)), active(rhs.active) {
|
||||
rhs.Cancel();
|
||||
}
|
||||
|
||||
ScopeGuard& operator=(ScopeGuard&& rhs) = delete;
|
||||
};
|
||||
|
||||
template <class F>
|
||||
constexpr ScopeGuard<F> MakeScopeGuard(F f) {
|
||||
return ScopeGuard<F>(std::move(f));
|
||||
}
|
||||
|
||||
enum class ScopeGuardOnExit {};
|
||||
|
||||
template <typename F>
|
||||
constexpr ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f) {
|
||||
return ScopeGuard<F>(std::forward<F>(f));
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
#define CONCATENATE_IMPL(s1, s2) s1##s2
|
||||
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
|
||||
|
||||
#ifdef __COUNTER__
|
||||
#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __COUNTER__)
|
||||
#else
|
||||
#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __LINE__)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This macro is similar to SCOPE_EXIT, except the object is caller managed. This is intended to be
|
||||
* used when the caller might want to cancel the ScopeExit.
|
||||
*/
|
||||
#define SCOPE_GUARD detail::ScopeGuardOnExit() + [&]()
|
||||
|
||||
/**
|
||||
* This macro allows you to conveniently specify a block of code that will run on scope exit. Handy
|
||||
* for doing ad-hoc clean-up tasks in a function with multiple returns.
|
||||
*
|
||||
* Example usage:
|
||||
* \code
|
||||
* const int saved_val = g_foo;
|
||||
* g_foo = 55;
|
||||
* SCOPE_EXIT{ g_foo = saved_val; };
|
||||
*
|
||||
* if (Bar()) {
|
||||
* return 0;
|
||||
* } else {
|
||||
* return 20;
|
||||
* }
|
||||
* \endcode
|
||||
*/
|
||||
#define SCOPE_EXIT auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_) = SCOPE_GUARD
|
||||
@@ -1,180 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2012 SAURAV MOHAPATRA <mohaps@gmail.com>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
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<const uint8_t*>(start);
|
||||
const uint8_t* finish = static_cast<const uint8_t*>(end);
|
||||
while (begin != finish) {
|
||||
processByte(*begin);
|
||||
begin++;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
SHA1& processBytes(const void* const data, size_t len) {
|
||||
const uint8_t* block = static_cast<const uint8_t*>(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<unsigned char>((bitCount >> 24) & 0xFF));
|
||||
processByte(static_cast<unsigned char>((bitCount >> 16) & 0xFF));
|
||||
processByte(static_cast<unsigned char>((bitCount >> 8) & 0xFF));
|
||||
processByte(static_cast<unsigned char>((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
|
||||
@@ -1,46 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
namespace Common {
|
||||
|
||||
// Like std::shared_mutex, but reader has priority over writer.
|
||||
class SharedFirstMutex {
|
||||
public:
|
||||
void lock() {
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
cv.wait(lock, [this]() { return !writer_active && readers == 0; });
|
||||
writer_active = true;
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
writer_active = false;
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void lock_shared() {
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
cv.wait(lock, [this]() { return !writer_active; });
|
||||
++readers;
|
||||
}
|
||||
|
||||
void unlock_shared() {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
if (--readers == 0) {
|
||||
cv.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mtx;
|
||||
std::condition_variable cv;
|
||||
int readers = 0;
|
||||
bool writer_active = false;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,92 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/arch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/signal_context.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/ucontext.h>
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
void* GetXmmPointer(void* ctx, u8 index) {
|
||||
#if defined(_WIN32)
|
||||
#define CASE(index) \
|
||||
case index: \
|
||||
return (void*)(&((EXCEPTION_POINTERS*)ctx)->ContextRecord->Xmm##index.Low)
|
||||
#elif defined(__APPLE__)
|
||||
#define CASE(index) \
|
||||
case index: \
|
||||
return (void*)(&((ucontext_t*)ctx)->uc_mcontext->__fs.__fpu_xmm##index);
|
||||
#else
|
||||
#define CASE(index) \
|
||||
case index: \
|
||||
return (void*)(&((ucontext_t*)ctx)->uc_mcontext.fpregs->_xmm[index].element[0])
|
||||
#endif
|
||||
switch (index) {
|
||||
CASE(0);
|
||||
CASE(1);
|
||||
CASE(2);
|
||||
CASE(3);
|
||||
CASE(4);
|
||||
CASE(5);
|
||||
CASE(6);
|
||||
CASE(7);
|
||||
CASE(8);
|
||||
CASE(9);
|
||||
CASE(10);
|
||||
CASE(11);
|
||||
CASE(12);
|
||||
CASE(13);
|
||||
CASE(14);
|
||||
CASE(15);
|
||||
default: {
|
||||
UNREACHABLE_MSG("Invalid XMM register index: {}", index);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
#undef CASE
|
||||
}
|
||||
|
||||
void* GetRip(void* ctx) {
|
||||
#if defined(_WIN32)
|
||||
return (void*)((EXCEPTION_POINTERS*)ctx)->ContextRecord->Rip;
|
||||
#elif defined(__APPLE__)
|
||||
return (void*)((ucontext_t*)ctx)->uc_mcontext->__ss.__rip;
|
||||
#else
|
||||
return (void*)((ucontext_t*)ctx)->uc_mcontext.gregs[REG_RIP];
|
||||
#endif
|
||||
}
|
||||
|
||||
void IncrementRip(void* ctx, u64 length) {
|
||||
#if defined(_WIN32)
|
||||
((EXCEPTION_POINTERS*)ctx)->ContextRecord->Rip += length;
|
||||
#elif defined(__APPLE__)
|
||||
((ucontext_t*)ctx)->uc_mcontext->__ss.__rip += length;
|
||||
#else
|
||||
((ucontext_t*)ctx)->uc_mcontext.gregs[REG_RIP] += length;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsWriteError(void* ctx) {
|
||||
#if defined(_WIN32)
|
||||
return ((EXCEPTION_POINTERS*)ctx)->ExceptionRecord->ExceptionInformation[0] == 1;
|
||||
#elif defined(__APPLE__)
|
||||
#if defined(ARCH_X86_64)
|
||||
return ((ucontext_t*)ctx)->uc_mcontext->__es.__err & 0x2;
|
||||
#elif defined(ARCH_ARM64)
|
||||
return ((ucontext_t*)ctx)->uc_mcontext->__es.__esr & 0x40;
|
||||
#endif
|
||||
#else
|
||||
#if defined(ARCH_X86_64)
|
||||
return ((ucontext_t*)ctx)->uc_mcontext.gregs[REG_ERR] & 0x2;
|
||||
#else
|
||||
#error "Unsupported architecture"
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
} // namespace Common
|
||||
@@ -1,18 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
void* GetXmmPointer(void* ctx, u8 index);
|
||||
|
||||
void* GetRip(void* ctx);
|
||||
|
||||
void IncrementRip(void* ctx, u64 length);
|
||||
|
||||
bool IsWriteError(void* ctx);
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,15 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
template <size_t N, typename C = char>
|
||||
struct StringLiteral {
|
||||
static constexpr size_t len = N;
|
||||
|
||||
constexpr StringLiteral(const C (&str)[N]) {
|
||||
std::copy_n(str, N, value);
|
||||
}
|
||||
|
||||
C value[N]{};
|
||||
};
|
||||
@@ -1,86 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "common/string_util.h"
|
||||
#include "common/types.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
std::string ToLower(std::string_view input) {
|
||||
std::string str;
|
||||
str.resize(input.size());
|
||||
std::ranges::transform(input, str.begin(), tolower);
|
||||
return str;
|
||||
}
|
||||
|
||||
void ToLowerInPlace(std::string& str) {
|
||||
std::ranges::transform(str, str.begin(), tolower);
|
||||
}
|
||||
|
||||
std::vector<std::string> SplitString(const std::string& str, char delimiter) {
|
||||
std::istringstream iss(str);
|
||||
std::vector<std::string> output(1);
|
||||
|
||||
while (std::getline(iss, *output.rbegin(), delimiter)) {
|
||||
output.emplace_back();
|
||||
}
|
||||
|
||||
output.pop_back();
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string_view U8stringToString(std::u8string_view u8str) {
|
||||
return std::string_view{reinterpret_cast<const char*>(u8str.data()), u8str.size()};
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::wstring CPToUTF16(u32 code_page, std::string_view input) {
|
||||
const auto size =
|
||||
MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0);
|
||||
|
||||
if (size == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::wstring output(size, L'\0');
|
||||
|
||||
if (size != MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()),
|
||||
&output[0], static_cast<int>(output.size()))) {
|
||||
output.clear();
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string UTF16ToUTF8(std::wstring_view input) {
|
||||
const auto size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()),
|
||||
nullptr, 0, nullptr, nullptr);
|
||||
if (size == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string output(size, '\0');
|
||||
|
||||
if (size != WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()),
|
||||
&output[0], static_cast<int>(output.size()), nullptr,
|
||||
nullptr)) {
|
||||
output.clear();
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::wstring UTF8ToUTF16W(std::string_view input) {
|
||||
return CPToUTF16(CP_UTF8, input);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,26 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Common {
|
||||
|
||||
/// Make a string lowercase
|
||||
[[nodiscard]] std::string ToLower(std::string_view str);
|
||||
|
||||
void ToLowerInPlace(std::string& str);
|
||||
|
||||
std::vector<std::string> SplitString(const std::string& str, char delimiter);
|
||||
|
||||
std::string_view U8stringToString(std::u8string_view u8str);
|
||||
|
||||
#ifdef _WIN32
|
||||
[[nodiscard]] std::string UTF16ToUTF8(std::wstring_view input);
|
||||
[[nodiscard]] std::wstring UTF8ToUTF16W(std::string_view str);
|
||||
#endif
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,17 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
// support header file for libav
|
||||
|
||||
// The av_err2str macro in libavutil/error.h does not play nice with C++
|
||||
#ifdef av_err2str
|
||||
#undef av_err2str
|
||||
#include <string>
|
||||
av_always_inline std::string av_err2string(int errnum) {
|
||||
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
||||
return av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, errnum);
|
||||
}
|
||||
#define av_err2str(err) av_err2string(err).c_str()
|
||||
#endif // av_err2str
|
||||
@@ -1,240 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "common/error.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/thread.h"
|
||||
#include "ntapi.h"
|
||||
#ifdef __APPLE__
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_time.h>
|
||||
#include <pthread.h>
|
||||
#elif defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#include "common/string_util.h"
|
||||
#else
|
||||
#if defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
#include <pthread_np.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#include <sched.h>
|
||||
#endif
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#define cpu_set_t cpuset_t
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
void SetCurrentThreadRealtime(const std::chrono::nanoseconds period_ns) {
|
||||
// CPU time to grant.
|
||||
const std::chrono::nanoseconds computation_ns = period_ns / 2;
|
||||
|
||||
// Determine the timebase for converting time to ticks.
|
||||
struct mach_timebase_info timebase{};
|
||||
mach_timebase_info(&timebase);
|
||||
const auto ticks_per_ns =
|
||||
static_cast<double>(timebase.denom) / static_cast<double>(timebase.numer);
|
||||
|
||||
const auto period_ticks =
|
||||
static_cast<u32>(static_cast<double>(period_ns.count()) * ticks_per_ns);
|
||||
const auto computation_ticks =
|
||||
static_cast<u32>(static_cast<double>(computation_ns.count()) * ticks_per_ns);
|
||||
|
||||
thread_time_constraint_policy policy = {
|
||||
.period = period_ticks,
|
||||
.computation = computation_ticks,
|
||||
// Should not matter since preemptible is false, but needs to be >= computation regardless.
|
||||
.constraint = computation_ticks,
|
||||
.preemptible = false,
|
||||
};
|
||||
|
||||
int ret = thread_policy_set(
|
||||
pthread_mach_thread_np(pthread_self()), THREAD_TIME_CONSTRAINT_POLICY,
|
||||
reinterpret_cast<thread_policy_t>(&policy), THREAD_TIME_CONSTRAINT_POLICY_COUNT);
|
||||
if (ret != KERN_SUCCESS) {
|
||||
LOG_ERROR(Common, "Could not set thread to real-time with period {} ns: {}",
|
||||
period_ns.count(), ret);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void SetCurrentThreadRealtime(const std::chrono::nanoseconds period_ns) {
|
||||
// Not implemented
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
void SetCurrentThreadPriority(ThreadPriority new_priority) {
|
||||
auto handle = GetCurrentThread();
|
||||
int windows_priority = 0;
|
||||
switch (new_priority) {
|
||||
case ThreadPriority::Low:
|
||||
windows_priority = THREAD_PRIORITY_BELOW_NORMAL;
|
||||
break;
|
||||
case ThreadPriority::Normal:
|
||||
windows_priority = THREAD_PRIORITY_NORMAL;
|
||||
break;
|
||||
case ThreadPriority::High:
|
||||
windows_priority = THREAD_PRIORITY_ABOVE_NORMAL;
|
||||
break;
|
||||
case ThreadPriority::VeryHigh:
|
||||
windows_priority = THREAD_PRIORITY_HIGHEST;
|
||||
break;
|
||||
case ThreadPriority::Critical:
|
||||
windows_priority = THREAD_PRIORITY_TIME_CRITICAL;
|
||||
break;
|
||||
default:
|
||||
windows_priority = THREAD_PRIORITY_NORMAL;
|
||||
break;
|
||||
}
|
||||
SetThreadPriority(handle, windows_priority);
|
||||
}
|
||||
|
||||
bool AccurateSleep(const std::chrono::nanoseconds duration, std::chrono::nanoseconds* remaining,
|
||||
const bool interruptible) {
|
||||
const auto begin_sleep = std::chrono::high_resolution_clock::now();
|
||||
|
||||
LARGE_INTEGER interval{
|
||||
.QuadPart = -1 * (duration.count() / 100u),
|
||||
};
|
||||
HANDLE timer = ::CreateWaitableTimer(NULL, TRUE, NULL);
|
||||
SetWaitableTimer(timer, &interval, 0, NULL, NULL, 0);
|
||||
const auto ret = WaitForSingleObjectEx(timer, INFINITE, interruptible);
|
||||
::CloseHandle(timer);
|
||||
|
||||
if (remaining) {
|
||||
const auto end_sleep = std::chrono::high_resolution_clock::now();
|
||||
const auto sleep_time = end_sleep - begin_sleep;
|
||||
*remaining = duration > sleep_time ? duration - sleep_time : std::chrono::nanoseconds(0);
|
||||
}
|
||||
return ret == WAIT_OBJECT_0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void SetCurrentThreadPriority(ThreadPriority new_priority) {
|
||||
pthread_t this_thread = pthread_self();
|
||||
|
||||
const auto scheduling_type = SCHED_OTHER;
|
||||
s32 max_prio = sched_get_priority_max(scheduling_type);
|
||||
s32 min_prio = sched_get_priority_min(scheduling_type);
|
||||
u32 level = std::max(static_cast<u32>(new_priority) + 1, 4U);
|
||||
|
||||
struct sched_param params;
|
||||
if (max_prio > min_prio) {
|
||||
params.sched_priority = min_prio + ((max_prio - min_prio) * level) / 4;
|
||||
} else {
|
||||
params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4;
|
||||
}
|
||||
|
||||
pthread_setschedparam(this_thread, scheduling_type, ¶ms);
|
||||
}
|
||||
|
||||
bool AccurateSleep(const std::chrono::nanoseconds duration, std::chrono::nanoseconds* remaining,
|
||||
const bool interruptible) {
|
||||
timespec request = {
|
||||
.tv_sec = duration.count() / 1'000'000'000,
|
||||
.tv_nsec = duration.count() % 1'000'000'000,
|
||||
};
|
||||
timespec remain;
|
||||
int ret;
|
||||
while ((ret = nanosleep(&request, &remain)) < 0 && errno == EINTR) {
|
||||
if (interruptible) {
|
||||
break;
|
||||
}
|
||||
request = remain;
|
||||
}
|
||||
if (remaining) {
|
||||
*remaining = std::chrono::nanoseconds(remain.tv_sec * 1'000'000'000 + remain.tv_nsec);
|
||||
}
|
||||
return ret == 0 || errno != EINTR;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
// Sets the debugger-visible name of the current thread.
|
||||
void SetCurrentThreadName(const char* name) {
|
||||
SetThreadDescription(GetCurrentThread(), UTF8ToUTF16W(name).data());
|
||||
}
|
||||
|
||||
void SetThreadName(void* thread, const char* name) {
|
||||
SetThreadDescription(thread, UTF8ToUTF16W(name).data());
|
||||
}
|
||||
|
||||
#else // !MSVC_VER, so must be POSIX threads
|
||||
|
||||
// MinGW with the POSIX threading model does not support pthread_setname_np
|
||||
#if !defined(_WIN32) || defined(_MSC_VER)
|
||||
void SetCurrentThreadName(const char* name) {
|
||||
#ifdef __APPLE__
|
||||
pthread_setname_np(name);
|
||||
#elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
pthread_set_name_np(pthread_self(), name);
|
||||
#elif defined(__NetBSD__)
|
||||
pthread_setname_np(pthread_self(), "%s", (void*)name);
|
||||
#elif defined(__linux__)
|
||||
// Linux limits thread names to 15 characters and will outright reject any
|
||||
// attempt to set a longer name with ERANGE.
|
||||
std::string truncated(name, std::min(strlen(name), static_cast<std::size_t>(15)));
|
||||
if (int e = pthread_setname_np(pthread_self(), truncated.c_str())) {
|
||||
errno = e;
|
||||
LOG_ERROR(Common, "Failed to set thread name to '{}': {}", truncated, GetLastErrorMsg());
|
||||
}
|
||||
#else
|
||||
pthread_setname_np(pthread_self(), name);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetThreadName(void* thread, const char* name) {
|
||||
// TODO
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
void SetCurrentThreadName(const char*) {
|
||||
// Do Nothing on MinGW
|
||||
}
|
||||
|
||||
void SetThreadName(void* thread, const char* name) {
|
||||
// Do Nothing on MinGW
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
AccurateTimer::AccurateTimer(std::chrono::nanoseconds target_interval)
|
||||
: target_interval(target_interval) {}
|
||||
|
||||
void AccurateTimer::Start() {
|
||||
const auto begin_sleep = std::chrono::high_resolution_clock::now();
|
||||
if (total_wait.count() > 0) {
|
||||
AccurateSleep(total_wait, nullptr, false);
|
||||
}
|
||||
start_time = std::chrono::high_resolution_clock::now();
|
||||
total_wait -= std::chrono::duration_cast<std::chrono::nanoseconds>(start_time - begin_sleep);
|
||||
}
|
||||
|
||||
void AccurateTimer::End() {
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
total_wait +=
|
||||
target_interval - std::chrono::duration_cast<std::chrono::nanoseconds>(now - start_time);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,49 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
enum class ThreadPriority : u32 {
|
||||
Low = 0,
|
||||
Normal = 1,
|
||||
High = 2,
|
||||
VeryHigh = 3,
|
||||
Critical = 4,
|
||||
};
|
||||
|
||||
void SetCurrentThreadRealtime(std::chrono::nanoseconds period_ns);
|
||||
|
||||
void SetCurrentThreadPriority(ThreadPriority new_priority);
|
||||
|
||||
void SetCurrentThreadName(const char* name);
|
||||
|
||||
void SetThreadName(void* thread, const char* name);
|
||||
|
||||
bool AccurateSleep(std::chrono::nanoseconds duration, std::chrono::nanoseconds* remaining,
|
||||
bool interruptible);
|
||||
|
||||
class AccurateTimer {
|
||||
std::chrono::nanoseconds target_interval{};
|
||||
std::chrono::nanoseconds total_wait{};
|
||||
|
||||
std::chrono::high_resolution_clock::time_point start_time;
|
||||
|
||||
public:
|
||||
explicit AccurateTimer(std::chrono::nanoseconds target_interval);
|
||||
|
||||
void Start();
|
||||
|
||||
void End();
|
||||
|
||||
std::chrono::nanoseconds GetTotalWait() const {
|
||||
return total_wait;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,61 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace Common {
|
||||
|
||||
/// General purpose function wrapper similar to std::function.
|
||||
/// Unlike std::function, the captured values don't have to be copyable.
|
||||
/// This class can be moved but not copied.
|
||||
template <typename ResultType, typename... Args>
|
||||
class UniqueFunction {
|
||||
class CallableBase {
|
||||
public:
|
||||
virtual ~CallableBase() = default;
|
||||
virtual ResultType operator()(Args&&...) = 0;
|
||||
};
|
||||
|
||||
template <typename Functor>
|
||||
class Callable final : public CallableBase {
|
||||
public:
|
||||
Callable(Functor&& functor_) : functor{std::move(functor_)} {}
|
||||
~Callable() override = default;
|
||||
|
||||
ResultType operator()(Args&&... args) override {
|
||||
return functor(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
Functor functor;
|
||||
};
|
||||
|
||||
public:
|
||||
UniqueFunction() = default;
|
||||
|
||||
template <typename Functor>
|
||||
UniqueFunction(Functor&& functor)
|
||||
: callable{std::make_unique<Callable<Functor>>(std::move(functor))} {}
|
||||
|
||||
UniqueFunction& operator=(UniqueFunction&& rhs) noexcept = default;
|
||||
UniqueFunction(UniqueFunction&& rhs) noexcept = default;
|
||||
|
||||
UniqueFunction& operator=(const UniqueFunction&) = delete;
|
||||
UniqueFunction(const UniqueFunction&) = delete;
|
||||
|
||||
ResultType operator()(Args&&... args) const {
|
||||
return (*callable)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept {
|
||||
return static_cast<bool>(callable);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<CallableBase> callable;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,111 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#pragma once
|
||||
|
||||
#include <xmmintrin.h>
|
||||
#include "common/types.h"
|
||||
|
||||
#define VA_ARGS \
|
||||
uint64_t rdi, uint64_t rsi, uint64_t rdx, uint64_t rcx, uint64_t r8, uint64_t r9, \
|
||||
uint64_t overflow_arg_area, __m128 xmm0, __m128 xmm1, __m128 xmm2, __m128 xmm3, \
|
||||
__m128 xmm4, __m128 xmm5, __m128 xmm6, __m128 xmm7
|
||||
|
||||
#define VA_CTX(ctx) \
|
||||
alignas(16)::Common::VaCtx ctx{}; \
|
||||
(ctx).reg_save_area.gp[0] = rdi; \
|
||||
(ctx).reg_save_area.gp[1] = rsi; \
|
||||
(ctx).reg_save_area.gp[2] = rdx; \
|
||||
(ctx).reg_save_area.gp[3] = rcx; \
|
||||
(ctx).reg_save_area.gp[4] = r8; \
|
||||
(ctx).reg_save_area.gp[5] = r9; \
|
||||
(ctx).reg_save_area.fp[0] = xmm0; \
|
||||
(ctx).reg_save_area.fp[1] = xmm1; \
|
||||
(ctx).reg_save_area.fp[2] = xmm2; \
|
||||
(ctx).reg_save_area.fp[3] = xmm3; \
|
||||
(ctx).reg_save_area.fp[4] = xmm4; \
|
||||
(ctx).reg_save_area.fp[5] = xmm5; \
|
||||
(ctx).reg_save_area.fp[6] = xmm6; \
|
||||
(ctx).reg_save_area.fp[7] = xmm7; \
|
||||
(ctx).va_list.reg_save_area = &(ctx).reg_save_area; \
|
||||
(ctx).va_list.gp_offset = offsetof(::Common::VaRegSave, gp); \
|
||||
(ctx).va_list.fp_offset = offsetof(::Common::VaRegSave, fp); \
|
||||
(ctx).va_list.overflow_arg_area = &overflow_arg_area;
|
||||
|
||||
namespace Common {
|
||||
|
||||
// https://stackoverflow.com/questions/4958384/what-is-the-format-of-the-x86-64-va-list-structure
|
||||
|
||||
struct VaList {
|
||||
u32 gp_offset;
|
||||
u32 fp_offset;
|
||||
void* overflow_arg_area;
|
||||
void* reg_save_area;
|
||||
};
|
||||
|
||||
struct VaRegSave {
|
||||
u64 gp[6];
|
||||
__m128 fp[8];
|
||||
};
|
||||
|
||||
struct VaCtx {
|
||||
VaRegSave reg_save_area;
|
||||
VaList va_list;
|
||||
};
|
||||
|
||||
template <class T, uint32_t Size>
|
||||
T vaArgRegSaveAreaGp(VaList* l) {
|
||||
auto* addr = reinterpret_cast<T*>(static_cast<u8*>(l->reg_save_area) + l->gp_offset);
|
||||
l->gp_offset += Size;
|
||||
return *addr;
|
||||
}
|
||||
template <class T, u64 Align, u64 Size>
|
||||
T vaArgOverflowArgArea(VaList* l) {
|
||||
auto ptr = ((reinterpret_cast<u64>(l->overflow_arg_area) + (Align - 1)) & ~(Align - 1));
|
||||
auto* addr = reinterpret_cast<T*>(ptr);
|
||||
l->overflow_arg_area = reinterpret_cast<void*>(ptr + Size);
|
||||
return *addr;
|
||||
}
|
||||
|
||||
template <class T, uint32_t Size>
|
||||
T vaArgRegSaveAreaFp(VaList* l) {
|
||||
auto* addr = reinterpret_cast<T*>(static_cast<u8*>(l->reg_save_area) + l->fp_offset);
|
||||
l->fp_offset += Size;
|
||||
return *addr;
|
||||
}
|
||||
|
||||
inline int vaArgInteger(VaList* l) {
|
||||
if (l->gp_offset <= 40) {
|
||||
return vaArgRegSaveAreaGp<int, 8>(l);
|
||||
}
|
||||
return vaArgOverflowArgArea<int, 1, 8>(l);
|
||||
}
|
||||
|
||||
inline long long vaArgLongLong(VaList* l) {
|
||||
if (l->gp_offset <= 40) {
|
||||
return vaArgRegSaveAreaGp<long long, 8>(l);
|
||||
}
|
||||
return vaArgOverflowArgArea<long long, 1, 8>(l);
|
||||
}
|
||||
inline long vaArgLong(VaList* l) {
|
||||
if (l->gp_offset <= 40) {
|
||||
return vaArgRegSaveAreaGp<long, 8>(l);
|
||||
}
|
||||
return vaArgOverflowArgArea<long, 1, 8>(l);
|
||||
}
|
||||
|
||||
inline double vaArgDouble(VaList* l) {
|
||||
if (l->fp_offset <= 160) {
|
||||
return vaArgRegSaveAreaFp<double, 16>(l);
|
||||
}
|
||||
return vaArgOverflowArgArea<double, 1, 8>(l);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T* vaArgPtr(VaList* l) {
|
||||
if (l->gp_offset <= 40) {
|
||||
return vaArgRegSaveAreaGp<T*, 8>(l);
|
||||
}
|
||||
return vaArgOverflowArgArea<T*, 1, 8>(l);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,42 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// #include <imgui.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/singleton.h"
|
||||
#include "debug_state.h"
|
||||
|
||||
using namespace DebugStateType;
|
||||
|
||||
DebugStateImpl& DebugState = *Common::Singleton<DebugStateImpl>::Instance();
|
||||
|
||||
bool DebugStateType::showing_debug_menu_bar = false;
|
||||
|
||||
static ThreadID ThisThreadID() {
|
||||
#ifdef _WIN32
|
||||
return GetCurrentThreadId();
|
||||
#else
|
||||
return pthread_self();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void PauseThread(ThreadID id) {
|
||||
#ifdef _WIN32
|
||||
auto handle = OpenThread(THREAD_SUSPEND_RESUME, FALSE, id);
|
||||
SuspendThread(handle);
|
||||
CloseHandle(handle);
|
||||
#else
|
||||
pthread_kill(id, SIGUSR1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ResumeThread(ThreadID id) {
|
||||
#ifdef _WIN32
|
||||
auto handle = OpenThread(THREAD_SUSPEND_RESUME, FALSE, id);
|
||||
ResumeThread(handle);
|
||||
CloseHandle(handle);
|
||||
#else
|
||||
pthread_kill(id, SIGUSR1);
|
||||
#endif
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#include "common/types.h"
|
||||
// #include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <Windows.h>
|
||||
using ThreadID = DWORD;
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
using ThreadID = pthread_t;
|
||||
#endif
|
||||
|
||||
namespace Core::Devtools {
|
||||
class Layer;
|
||||
namespace Widget {
|
||||
class FrameGraph;
|
||||
class ShaderList;
|
||||
} // namespace Widget
|
||||
} // namespace Core::Devtools
|
||||
|
||||
namespace DebugStateType {
|
||||
|
||||
extern bool showing_debug_menu_bar;
|
||||
|
||||
class DebugStateImpl {
|
||||
friend class Core::Devtools::Layer;
|
||||
friend class Core::Devtools::Widget::FrameGraph;
|
||||
friend class Core::Devtools::Widget::ShaderList;
|
||||
|
||||
std::queue<std::string> debug_message_popup;
|
||||
|
||||
std::mutex guest_threads_mutex{};
|
||||
std::vector<ThreadID> guest_threads{};
|
||||
std::atomic_bool is_guest_threads_paused = false;
|
||||
u64 pause_time{};
|
||||
|
||||
std::atomic_int32_t flip_frame_count = 0;
|
||||
std::atomic_int32_t gnm_frame_count = 0;
|
||||
|
||||
s32 gnm_frame_dump_request_count = -1;
|
||||
std::unordered_map<size_t, std::string> waiting_reg_dumps_dbg;
|
||||
bool waiting_submit_pause = false;
|
||||
bool should_show_frame_dump = false;
|
||||
|
||||
std::shared_mutex frame_dump_list_mutex;
|
||||
|
||||
public:
|
||||
float Framerate = 1.0f / 60.0f;
|
||||
float FrameDeltaTime;
|
||||
|
||||
std::pair<u32, u32> game_resolution{};
|
||||
std::pair<u32, u32> output_resolution{};
|
||||
bool is_using_fsr{};
|
||||
|
||||
void ShowDebugMessage(std::string message) {
|
||||
if (message.empty()) {
|
||||
return;
|
||||
}
|
||||
debug_message_popup.push(std::move(message));
|
||||
}
|
||||
|
||||
bool& IsShowingDebugMenuBar() {
|
||||
return showing_debug_menu_bar;
|
||||
}
|
||||
|
||||
bool IsGuestThreadsPaused() const {
|
||||
return is_guest_threads_paused;
|
||||
}
|
||||
};
|
||||
} // namespace DebugStateType
|
||||
|
||||
extern DebugStateType::DebugStateImpl& DebugState;
|
||||
@@ -1,8 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
// Generic
|
||||
constexpr int ORBIS_OK = 0x00000000;
|
||||
constexpr int ORBIS_FAIL = 0xFFFFFFFF;
|
||||
@@ -1,7 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
#include "playgo_types.h"
|
||||
@@ -1,58 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
using OrbisPlayGoHandle = u32;
|
||||
using OrbisPlayGoChunkId = u16;
|
||||
using OrbisPlayGoEta = s64;
|
||||
using OrbisPlayGoLanguageMask = u64;
|
||||
|
||||
enum class OrbisPlayGoLocus : s8 {
|
||||
NotDownloaded = 0,
|
||||
LocalSlow = 2,
|
||||
LocalFast = 3,
|
||||
};
|
||||
|
||||
enum class OrbisPlayGoInstallSpeed : s32 {
|
||||
Suspended = 0,
|
||||
Trickle = 1,
|
||||
Full = 2,
|
||||
};
|
||||
|
||||
struct OrbisPlayGoInitParams {
|
||||
const void* bufAddr;
|
||||
u32 bufSize;
|
||||
u32 reserved;
|
||||
};
|
||||
|
||||
struct OrbisPlayGoToDo {
|
||||
OrbisPlayGoChunkId chunkId;
|
||||
OrbisPlayGoLocus locus;
|
||||
s8 reserved;
|
||||
};
|
||||
|
||||
struct OrbisPlayGoProgress {
|
||||
u64 progressSize;
|
||||
u64 totalSize;
|
||||
};
|
||||
|
||||
constexpr int ORBIS_PLAYGO_ERROR_UNKNOWN = -2135818239; /* 0x80B20001 */
|
||||
constexpr int ORBIS_PLAYGO_ERROR_FATAL = -2135818238; /* 0x80B20002 */
|
||||
constexpr int ORBIS_PLAYGO_ERROR_NO_MEMORY = -2135818237; /* 0x80B20003 */
|
||||
constexpr int ORBIS_PLAYGO_ERROR_INVALID_ARGUMENT = -2135818236; /* 0x80B20004 */
|
||||
constexpr int ORBIS_PLAYGO_ERROR_NOT_INITIALIZED = -2135818235; /* 0x80B20005 */
|
||||
constexpr int ORBIS_PLAYGO_ERROR_ALREADY_INITIALIZED = -2135818234; /* 0x80B20006 */
|
||||
constexpr int ORBIS_PLAYGO_ERROR_ALREADY_STARTED = -2135818233; /* 0x80B20007 */
|
||||
constexpr int ORBIS_PLAYGO_ERROR_NOT_STARTED = -2135818232; /* 0x80B20008 */
|
||||
constexpr int ORBIS_PLAYGO_ERROR_BAD_HANDLE = -2135818231; /* 0x80B20009 */
|
||||
constexpr int ORBIS_PLAYGO_ERROR_BAD_POINTER = -2135818230; /* 0x80B2000A */
|
||||
constexpr int ORBIS_PLAYGO_ERROR_BAD_SIZE = -2135818229; /* 0x80B2000B */
|
||||
constexpr int ORBIS_PLAYGO_ERROR_BAD_CHUNK_ID = -2135818228; /* 0x80B2000C */
|
||||
constexpr int ORBIS_PLAYGO_ERROR_BAD_SPEED = -2135818227; /* 0x80B2000D */
|
||||
constexpr int ORBIS_PLAYGO_ERROR_NOT_SUPPORT_PLAYGO = -2135818226; /* 0x80B2000E */
|
||||
constexpr int ORBIS_PLAYGO_ERROR_EPERM = -2135818225; /* 0x80B2000F */
|
||||
constexpr int ORBIS_PLAYGO_ERROR_BAD_LOCUS = -2135818224; /* 0x80B20010 */
|
||||
constexpr int ORBIS_PLAYGO_ERROR_NEED_DATA_DISC = -2135818223; /* 0x80B20011 */
|
||||
1042
src/core/memory.cpp
1042
src/core/memory.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,303 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include "common/enum.h"
|
||||
#include "common/singleton.h"
|
||||
#include "common/types.h"
|
||||
#include "core/address_space.h"
|
||||
#include "core/libraries/kernel/memory.h"
|
||||
|
||||
namespace Vulkan {
|
||||
class Rasterizer;
|
||||
}
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
struct OrbisQueryInfo;
|
||||
}
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
class MemoryMapViewer;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
enum class MemoryProt : u32 {
|
||||
NoAccess = 0,
|
||||
CpuRead = 1,
|
||||
CpuReadWrite = 2,
|
||||
CpuExec = 4,
|
||||
GpuRead = 16,
|
||||
GpuWrite = 32,
|
||||
GpuReadWrite = 48,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(MemoryProt)
|
||||
|
||||
enum class MemoryMapFlags : u32 {
|
||||
NoFlags = 0,
|
||||
Shared = 1,
|
||||
Private = 2,
|
||||
Fixed = 0x10,
|
||||
NoOverwrite = 0x0080,
|
||||
NoSync = 0x800,
|
||||
NoCore = 0x20000,
|
||||
NoCoalesce = 0x400000,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(MemoryMapFlags)
|
||||
|
||||
enum class VMAType : u32 {
|
||||
Free = 0,
|
||||
Reserved = 1,
|
||||
Direct = 2,
|
||||
Flexible = 3,
|
||||
Pooled = 4,
|
||||
PoolReserved = 5,
|
||||
Stack = 6,
|
||||
Code = 7,
|
||||
File = 8,
|
||||
};
|
||||
|
||||
struct DirectMemoryArea {
|
||||
PAddr base = 0;
|
||||
u64 size = 0;
|
||||
s32 memory_type = 0;
|
||||
bool is_pooled = false;
|
||||
bool is_free = true;
|
||||
|
||||
PAddr GetEnd() const {
|
||||
return base + size;
|
||||
}
|
||||
|
||||
bool CanMergeWith(const DirectMemoryArea& next) const {
|
||||
if (base + size != next.base) {
|
||||
return false;
|
||||
}
|
||||
if (memory_type != next.memory_type) {
|
||||
return false;
|
||||
}
|
||||
if (is_free != next.is_free) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct VirtualMemoryArea {
|
||||
VAddr base = 0;
|
||||
u64 size = 0;
|
||||
PAddr phys_base = 0;
|
||||
VMAType type = VMAType::Free;
|
||||
MemoryProt prot = MemoryProt::NoAccess;
|
||||
bool disallow_merge = false;
|
||||
std::string name = "";
|
||||
uintptr_t fd = 0;
|
||||
bool is_exec = false;
|
||||
|
||||
bool Contains(VAddr addr, u64 size) const {
|
||||
return addr >= base && (addr + size) <= (base + this->size);
|
||||
}
|
||||
|
||||
bool IsFree() const noexcept {
|
||||
return type == VMAType::Free;
|
||||
}
|
||||
|
||||
bool IsMapped() const noexcept {
|
||||
return type != VMAType::Free && type != VMAType::Reserved && type != VMAType::PoolReserved;
|
||||
}
|
||||
|
||||
bool CanMergeWith(const VirtualMemoryArea& next) const {
|
||||
if (disallow_merge || next.disallow_merge) {
|
||||
return false;
|
||||
}
|
||||
if (base + size != next.base) {
|
||||
return false;
|
||||
}
|
||||
if (type == VMAType::Direct && phys_base + size != next.phys_base) {
|
||||
return false;
|
||||
}
|
||||
if (prot != next.prot || type != next.type) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class MemoryManager {
|
||||
using DMemMap = std::map<PAddr, DirectMemoryArea>;
|
||||
using DMemHandle = DMemMap::iterator;
|
||||
|
||||
using VMAMap = std::map<VAddr, VirtualMemoryArea>;
|
||||
using VMAHandle = VMAMap::iterator;
|
||||
|
||||
public:
|
||||
explicit MemoryManager();
|
||||
~MemoryManager();
|
||||
|
||||
void SetRasterizer(Vulkan::Rasterizer* rasterizer_) {
|
||||
rasterizer = rasterizer_;
|
||||
}
|
||||
|
||||
AddressSpace& GetAddressSpace() {
|
||||
return impl;
|
||||
}
|
||||
|
||||
u64 GetTotalDirectSize() const {
|
||||
return total_direct_size;
|
||||
}
|
||||
|
||||
u64 GetTotalFlexibleSize() const {
|
||||
return total_flexible_size;
|
||||
}
|
||||
|
||||
u64 GetAvailableFlexibleSize() const {
|
||||
return total_flexible_size - flexible_usage;
|
||||
}
|
||||
|
||||
VAddr SystemReservedVirtualBase() noexcept {
|
||||
return impl.SystemReservedVirtualBase();
|
||||
}
|
||||
|
||||
bool IsValidGpuMapping(VAddr virtual_addr, u64 size) {
|
||||
// The PS4's GPU can only handle 40 bit addresses.
|
||||
const VAddr max_gpu_address{0x10000000000};
|
||||
return virtual_addr + size < max_gpu_address;
|
||||
}
|
||||
|
||||
bool IsValidAddress(const void* addr) const noexcept {
|
||||
const VAddr virtual_addr = reinterpret_cast<VAddr>(addr);
|
||||
const auto end_it = std::prev(vma_map.end());
|
||||
const VAddr end_addr = end_it->first + end_it->second.size;
|
||||
return virtual_addr >= vma_map.begin()->first && virtual_addr < end_addr;
|
||||
}
|
||||
|
||||
u64 ClampRangeSize(VAddr virtual_addr, u64 size);
|
||||
|
||||
void SetPrtArea(u32 id, VAddr address, u64 size);
|
||||
|
||||
void CopySparseMemory(VAddr source, u8* dest, u64 size);
|
||||
|
||||
bool TryWriteBacking(void* address, const void* data, u32 num_bytes);
|
||||
|
||||
void SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1, bool use_extended_mem2);
|
||||
|
||||
PAddr PoolExpand(PAddr search_start, PAddr search_end, u64 size, u64 alignment);
|
||||
|
||||
PAddr Allocate(PAddr search_start, PAddr search_end, u64 size, u64 alignment, s32 memory_type);
|
||||
|
||||
void Free(PAddr phys_addr, u64 size);
|
||||
|
||||
s32 PoolCommit(VAddr virtual_addr, u64 size, MemoryProt prot);
|
||||
|
||||
s32 MapMemory(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot,
|
||||
MemoryMapFlags flags, VMAType type, std::string_view name = "anon",
|
||||
bool is_exec = false, PAddr phys_addr = -1, u64 alignment = 0);
|
||||
|
||||
s32 MapFile(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot,
|
||||
MemoryMapFlags flags, s32 fd, s64 phys_addr);
|
||||
|
||||
s32 PoolDecommit(VAddr virtual_addr, u64 size);
|
||||
|
||||
s32 UnmapMemory(VAddr virtual_addr, u64 size);
|
||||
|
||||
s32 QueryProtection(VAddr addr, void** start, void** end, u32* prot);
|
||||
|
||||
s32 Protect(VAddr addr, u64 size, MemoryProt prot);
|
||||
|
||||
s64 ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, u64 size, MemoryProt prot);
|
||||
|
||||
s32 VirtualQuery(VAddr addr, s32 flags, ::Libraries::Kernel::OrbisVirtualQueryInfo* info);
|
||||
|
||||
s32 DirectMemoryQuery(PAddr addr, bool find_next,
|
||||
::Libraries::Kernel::OrbisQueryInfo* out_info);
|
||||
|
||||
s32 DirectQueryAvailable(PAddr search_start, PAddr search_end, u64 alignment,
|
||||
PAddr* phys_addr_out, u64* size_out);
|
||||
|
||||
s32 GetDirectMemoryType(PAddr addr, s32* directMemoryTypeOut, void** directMemoryStartOut,
|
||||
void** directMemoryEndOut);
|
||||
|
||||
s32 IsStack(VAddr addr, void** start, void** end);
|
||||
|
||||
s32 SetDirectMemoryType(s64 phys_addr, s32 memory_type);
|
||||
|
||||
void NameVirtualRange(VAddr virtual_addr, u64 size, std::string_view name);
|
||||
|
||||
void InvalidateMemory(VAddr addr, u64 size) const;
|
||||
|
||||
private:
|
||||
VMAHandle FindVMA(VAddr target) {
|
||||
return std::prev(vma_map.upper_bound(target));
|
||||
}
|
||||
|
||||
DMemHandle FindDmemArea(PAddr target) {
|
||||
return std::prev(dmem_map.upper_bound(target));
|
||||
}
|
||||
|
||||
template <typename Handle>
|
||||
Handle MergeAdjacent(auto& handle_map, Handle iter) {
|
||||
const auto next_vma = std::next(iter);
|
||||
if (next_vma != handle_map.end() && iter->second.CanMergeWith(next_vma->second)) {
|
||||
iter->second.size += next_vma->second.size;
|
||||
handle_map.erase(next_vma);
|
||||
}
|
||||
|
||||
if (iter != handle_map.begin()) {
|
||||
auto prev_vma = std::prev(iter);
|
||||
if (prev_vma->second.CanMergeWith(iter->second)) {
|
||||
prev_vma->second.size += iter->second.size;
|
||||
handle_map.erase(iter);
|
||||
iter = prev_vma;
|
||||
}
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
VAddr SearchFree(VAddr virtual_addr, u64 size, u32 alignment = 0);
|
||||
|
||||
VMAHandle CarveVMA(VAddr virtual_addr, u64 size);
|
||||
|
||||
DMemHandle CarveDmemArea(PAddr addr, u64 size);
|
||||
|
||||
VMAHandle Split(VMAHandle vma_handle, u64 offset_in_vma);
|
||||
|
||||
DMemHandle Split(DMemHandle dmem_handle, u64 offset_in_area);
|
||||
|
||||
u64 UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma_base, u64 size);
|
||||
|
||||
s32 UnmapMemoryImpl(VAddr virtual_addr, u64 size);
|
||||
|
||||
private:
|
||||
AddressSpace impl;
|
||||
DMemMap dmem_map;
|
||||
VMAMap vma_map;
|
||||
std::mutex mutex;
|
||||
u64 total_direct_size{};
|
||||
u64 total_flexible_size{};
|
||||
u64 flexible_usage{};
|
||||
u64 pool_budget{};
|
||||
Vulkan::Rasterizer* rasterizer{};
|
||||
|
||||
struct PrtArea {
|
||||
VAddr start;
|
||||
VAddr end;
|
||||
bool mapped;
|
||||
|
||||
bool Overlaps(VAddr test_address, u64 test_size) const {
|
||||
const VAddr overlap_end = test_address + test_size;
|
||||
return start < overlap_end && test_address < end;
|
||||
}
|
||||
};
|
||||
std::array<PrtArea, 3> prt_areas{};
|
||||
|
||||
friend class ::Core::Devtools::Widget::MemoryMapViewer;
|
||||
};
|
||||
|
||||
using Memory = Common::Singleton<MemoryManager>;
|
||||
|
||||
} // namespace Core
|
||||
@@ -1,237 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/types.h"
|
||||
#include "core/loader/elf.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
static constexpr size_t SCE_DBG_MAX_NAME_LENGTH = 256;
|
||||
static constexpr size_t SCE_DBG_MAX_SEGMENTS = 4;
|
||||
static constexpr size_t SCE_DBG_NUM_FINGERPRINT = 20;
|
||||
|
||||
struct OrbisKernelModuleSegmentInfo {
|
||||
VAddr address;
|
||||
u32 size;
|
||||
s32 prot;
|
||||
};
|
||||
|
||||
struct OrbisKernelModuleInfo {
|
||||
u64 st_size = sizeof(OrbisKernelModuleInfo);
|
||||
std::array<char, SCE_DBG_MAX_NAME_LENGTH> name;
|
||||
std::array<OrbisKernelModuleSegmentInfo, SCE_DBG_MAX_SEGMENTS> segments;
|
||||
u32 num_segments;
|
||||
std::array<u8, SCE_DBG_NUM_FINGERPRINT> fingerprint;
|
||||
};
|
||||
|
||||
struct OrbisKernelModuleInfoEx {
|
||||
u64 st_size = sizeof(OrbisKernelModuleInfoEx);
|
||||
std::array<char, SCE_DBG_MAX_NAME_LENGTH> name;
|
||||
s32 id;
|
||||
u32 tls_index;
|
||||
VAddr tls_init_addr;
|
||||
u32 tls_init_size;
|
||||
u32 tls_size;
|
||||
u32 tls_offset;
|
||||
u32 tls_align;
|
||||
VAddr init_proc_addr;
|
||||
VAddr fini_proc_addr;
|
||||
u64 reserved1;
|
||||
u64 reserved2;
|
||||
VAddr eh_frame_hdr_addr;
|
||||
VAddr eh_frame_addr;
|
||||
u32 eh_frame_hdr_size;
|
||||
u32 eh_frame_size;
|
||||
std::array<OrbisKernelModuleSegmentInfo, SCE_DBG_MAX_SEGMENTS> segments;
|
||||
u32 segment_count;
|
||||
};
|
||||
|
||||
struct ModuleInfo {
|
||||
bool operator==(const ModuleInfo& other) const {
|
||||
return name == other.name;
|
||||
}
|
||||
std::string name;
|
||||
union {
|
||||
u64 value;
|
||||
struct {
|
||||
u32 name_offset;
|
||||
u8 version_minor;
|
||||
u8 version_major;
|
||||
u16 id;
|
||||
};
|
||||
};
|
||||
std::string enc_id;
|
||||
};
|
||||
|
||||
struct LibraryInfo {
|
||||
bool operator==(const LibraryInfo& other) const {
|
||||
return version == other.version && name == other.name;
|
||||
}
|
||||
std::string name;
|
||||
union {
|
||||
u64 value;
|
||||
struct {
|
||||
u32 name_offset;
|
||||
u16 version;
|
||||
u16 id;
|
||||
};
|
||||
};
|
||||
std::string enc_id;
|
||||
};
|
||||
|
||||
struct ThreadLocalImage {
|
||||
u32 align;
|
||||
u32 image_size;
|
||||
u32 offset;
|
||||
u32 modid;
|
||||
VAddr image_virtual_addr;
|
||||
u32 init_image_size;
|
||||
};
|
||||
|
||||
struct DynamicModuleInfo {
|
||||
void* hash_table = nullptr;
|
||||
u64 hash_table_size = 0;
|
||||
|
||||
char* str_table = nullptr;
|
||||
u64 str_table_size = 0;
|
||||
|
||||
elf_symbol* symbol_table = nullptr;
|
||||
u64 symbol_table_total_size = 0;
|
||||
u64 symbol_table_entries_size = 0;
|
||||
|
||||
u64 init_virtual_addr = 0;
|
||||
u64 fini_virtual_addr = 0;
|
||||
u64 pltgot_virtual_addr = 0;
|
||||
u64 init_array_virtual_addr = 0;
|
||||
u64 fini_array_virtual_addr = 0;
|
||||
u64 preinit_array_virtual_addr = 0;
|
||||
u64 init_array_size = 0;
|
||||
u64 fini_array_size = 0;
|
||||
u64 preinit_array_size = 0;
|
||||
|
||||
elf_relocation* jmp_relocation_table = nullptr;
|
||||
u64 jmp_relocation_table_size = 0;
|
||||
s64 jmp_relocation_type = 0;
|
||||
|
||||
elf_relocation* relocation_table = nullptr;
|
||||
u64 relocation_table_size = 0;
|
||||
u64 relocation_table_entries_size = 0;
|
||||
|
||||
u64 debug = 0;
|
||||
u64 textrel = 0;
|
||||
u64 flags = 0;
|
||||
|
||||
std::vector<const char*> needed;
|
||||
std::vector<ModuleInfo> import_modules;
|
||||
std::vector<ModuleInfo> export_modules;
|
||||
std::vector<LibraryInfo> import_libs;
|
||||
std::vector<LibraryInfo> export_libs;
|
||||
|
||||
std::string filename;
|
||||
};
|
||||
|
||||
using ModuleFunc = int (*)(size_t, const void*);
|
||||
class MemoryManager;
|
||||
|
||||
class Module {
|
||||
public:
|
||||
explicit Module(Core::MemoryManager* memory, const std::filesystem::path& file,
|
||||
u32& max_tls_index);
|
||||
~Module();
|
||||
|
||||
VAddr GetBaseAddress() const noexcept {
|
||||
return base_virtual_addr;
|
||||
}
|
||||
|
||||
VAddr GetEntryAddress() const noexcept {
|
||||
return base_virtual_addr + elf.GetElfEntry();
|
||||
}
|
||||
|
||||
OrbisKernelModuleInfo GetModuleInfo() const noexcept {
|
||||
return info;
|
||||
}
|
||||
|
||||
bool IsValid() const noexcept {
|
||||
return base_virtual_addr != 0;
|
||||
}
|
||||
|
||||
bool IsSharedLib() const noexcept {
|
||||
return elf.IsSharedLib();
|
||||
}
|
||||
|
||||
template <typename T = VAddr>
|
||||
T GetProcParam() const noexcept {
|
||||
return reinterpret_cast<T>(proc_param_virtual_addr);
|
||||
}
|
||||
|
||||
std::span<const ModuleInfo> GetImportModules() const {
|
||||
return dynamic_info.import_modules;
|
||||
}
|
||||
|
||||
std::span<const ModuleInfo> GetExportModules() const {
|
||||
return dynamic_info.export_modules;
|
||||
}
|
||||
|
||||
std::span<const LibraryInfo> GetImportLibs() const {
|
||||
return dynamic_info.import_libs;
|
||||
}
|
||||
|
||||
std::span<const LibraryInfo> GetExportLibs() const {
|
||||
return dynamic_info.export_libs;
|
||||
}
|
||||
|
||||
void ForEachRelocation(auto&& func) {
|
||||
for (u32 i = 0; i < dynamic_info.relocation_table_size / sizeof(elf_relocation); i++) {
|
||||
func(&dynamic_info.relocation_table[i], i, false);
|
||||
}
|
||||
for (u32 i = 0; i < dynamic_info.jmp_relocation_table_size / sizeof(elf_relocation); i++) {
|
||||
func(&dynamic_info.jmp_relocation_table[i], i, true);
|
||||
}
|
||||
}
|
||||
|
||||
void SetRelaBit(u32 index) {
|
||||
rela_bits[index >> 3] |= (1 << (index & 7));
|
||||
}
|
||||
|
||||
bool TestRelaBit(u32 index) const {
|
||||
return (rela_bits[index >> 3] >> (index & 7)) & 1;
|
||||
}
|
||||
|
||||
s32 Start(u64 args, const void* argp, void* param);
|
||||
void LoadModuleToMemory(u32& max_tls_index);
|
||||
void LoadDynamicInfo();
|
||||
void LoadSymbols();
|
||||
|
||||
void* FindByName(std::string_view name);
|
||||
|
||||
OrbisKernelModuleInfoEx GetModuleInfoEx() const;
|
||||
const ModuleInfo* FindModule(std::string_view id);
|
||||
const LibraryInfo* FindLibrary(std::string_view id);
|
||||
|
||||
public:
|
||||
Core::MemoryManager* memory;
|
||||
std::filesystem::path file;
|
||||
std::string name;
|
||||
Loader::Elf elf;
|
||||
u64 aligned_base_size{};
|
||||
VAddr base_virtual_addr{};
|
||||
VAddr proc_param_virtual_addr{};
|
||||
VAddr eh_frame_hdr_addr{};
|
||||
VAddr eh_frame_addr{};
|
||||
u32 eh_frame_hdr_size{};
|
||||
u32 eh_frame_size{};
|
||||
DynamicModuleInfo dynamic_info{};
|
||||
std::vector<u8> m_dynamic;
|
||||
std::vector<u8> m_dynamic_data;
|
||||
// Loader::SymbolsResolver export_sym;
|
||||
// Loader::SymbolsResolver import_sym;
|
||||
ThreadLocalImage tls{};
|
||||
OrbisKernelModuleInfo info{};
|
||||
std::vector<u8> rela_bits;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
@@ -1,96 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/singleton.h"
|
||||
#include "common/types.h"
|
||||
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
|
||||
namespace Platform {
|
||||
|
||||
enum class InterruptId : u32 {
|
||||
Compute0RelMem = 0x00,
|
||||
Compute1RelMem = 0x01,
|
||||
Compute2RelMem = 0x02,
|
||||
Compute3RelMem = 0x03,
|
||||
Compute4RelMem = 0x04,
|
||||
Compute5RelMem = 0x05,
|
||||
Compute6RelMem = 0x06,
|
||||
GfxEop = 0x40,
|
||||
GfxFlip = 0x08,
|
||||
GpuIdle = 0x09,
|
||||
|
||||
InterruptIdMax = 0x40, ///< Max possible value (GfxEop)
|
||||
};
|
||||
|
||||
using IrqHandler = std::function<void(InterruptId)>;
|
||||
|
||||
struct IrqController {
|
||||
void RegisterOnce(InterruptId irq, IrqHandler handler) {
|
||||
ASSERT_MSG(static_cast<u32>(irq) <= static_cast<u32>(InterruptId::InterruptIdMax),
|
||||
"Invalid IRQ number");
|
||||
auto& ctx = irq_contexts.try_emplace(irq).first->second;
|
||||
std::unique_lock lock{ctx.m_lock};
|
||||
ctx.one_time_subscribers.emplace(handler);
|
||||
}
|
||||
|
||||
void Register(InterruptId irq, IrqHandler handler, void* uid) {
|
||||
ASSERT_MSG(static_cast<u32>(irq) <= static_cast<u32>(InterruptId::InterruptIdMax),
|
||||
"Invalid IRQ number");
|
||||
auto& ctx = irq_contexts.try_emplace(irq).first->second;
|
||||
|
||||
std::unique_lock lock{ctx.m_lock};
|
||||
ASSERT_MSG(ctx.persistent_handlers.find(uid) == ctx.persistent_handlers.cend(),
|
||||
"The handler is already registered!");
|
||||
ctx.persistent_handlers.emplace(uid, handler);
|
||||
}
|
||||
|
||||
void Unregister(InterruptId irq, void* uid) {
|
||||
ASSERT_MSG(static_cast<u32>(irq) <= static_cast<u32>(InterruptId::InterruptIdMax),
|
||||
"Invalid IRQ number");
|
||||
auto& ctx = irq_contexts.try_emplace(irq).first->second;
|
||||
std::unique_lock lock{ctx.m_lock};
|
||||
ctx.persistent_handlers.erase(uid);
|
||||
}
|
||||
|
||||
void Signal(InterruptId irq) {
|
||||
ASSERT_MSG(static_cast<u32>(irq) <= static_cast<u32>(InterruptId::InterruptIdMax),
|
||||
"Unexpected IRQ signaled");
|
||||
auto& ctx = irq_contexts.try_emplace(irq).first->second;
|
||||
std::unique_lock lock{ctx.m_lock};
|
||||
|
||||
LOG_TRACE(Core, "IRQ signaled: {}", magic_enum::enum_name(irq));
|
||||
|
||||
for (auto& [uid, h] : ctx.persistent_handlers) {
|
||||
h(irq);
|
||||
}
|
||||
|
||||
if (!ctx.one_time_subscribers.empty()) {
|
||||
const auto& h = ctx.one_time_subscribers.front();
|
||||
h(irq);
|
||||
|
||||
ctx.one_time_subscribers.pop();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct IrqContext {
|
||||
std::unordered_map<void*, IrqHandler> persistent_handlers{};
|
||||
std::queue<IrqHandler> one_time_subscribers{};
|
||||
std::mutex m_lock{};
|
||||
};
|
||||
std::unordered_map<InterruptId, IrqContext> irq_contexts{};
|
||||
};
|
||||
|
||||
using IrqC = Common::Singleton<IrqController>;
|
||||
|
||||
} // namespace Platform
|
||||
@@ -1,165 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/arch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/decoder.h"
|
||||
#include "common/signal_context.h"
|
||||
#include "core/signals.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <csignal>
|
||||
#include <pthread.h>
|
||||
#ifdef ARCH_X86_64
|
||||
#include <Zydis/Formatter.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace Core {
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
static LONG WINAPI SignalHandler(EXCEPTION_POINTERS* pExp) noexcept {
|
||||
const auto* signals = Signals::Instance();
|
||||
|
||||
bool handled = false;
|
||||
switch (pExp->ExceptionRecord->ExceptionCode) {
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
handled = signals->DispatchAccessViolation(
|
||||
pExp, reinterpret_cast<void*>(pExp->ExceptionRecord->ExceptionInformation[1]));
|
||||
break;
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||
handled = signals->DispatchIllegalInstruction(pExp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return handled ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static std::string GetThreadName() {
|
||||
char name[256];
|
||||
if (pthread_getname_np(pthread_self(), name, sizeof(name)) != 0) {
|
||||
return "<unknown name>";
|
||||
}
|
||||
return std::string{name};
|
||||
}
|
||||
|
||||
static std::string DisassembleInstruction(void* code_address) {
|
||||
char buffer[256] = "<unable to decode>";
|
||||
|
||||
#ifdef ARCH_X86_64
|
||||
ZydisDecodedInstruction instruction;
|
||||
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT];
|
||||
const auto status =
|
||||
Common::Decoder::Instance()->decodeInstruction(instruction, operands, code_address);
|
||||
if (ZYAN_SUCCESS(status)) {
|
||||
ZydisFormatter formatter;
|
||||
ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL);
|
||||
ZydisFormatterFormatInstruction(&formatter, &instruction, operands,
|
||||
instruction.operand_count_visible, buffer, sizeof(buffer),
|
||||
reinterpret_cast<u64>(code_address), ZYAN_NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void SignalHandler(int sig, siginfo_t* info, void* raw_context) {
|
||||
const auto* signals = Signals::Instance();
|
||||
|
||||
auto* code_address = Common::GetRip(raw_context);
|
||||
|
||||
switch (sig) {
|
||||
case SIGSEGV:
|
||||
case SIGBUS: {
|
||||
const bool is_write = Common::IsWriteError(raw_context);
|
||||
if (!signals->DispatchAccessViolation(raw_context, info->si_addr)) {
|
||||
UNREACHABLE_MSG(
|
||||
"Unhandled access violation in thread '{}' at code address {}: {} address {}",
|
||||
GetThreadName(), fmt::ptr(code_address), is_write ? "Write to" : "Read from",
|
||||
fmt::ptr(info->si_addr));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SIGILL:
|
||||
if (!signals->DispatchIllegalInstruction(raw_context)) {
|
||||
UNREACHABLE_MSG("Unhandled illegal instruction in thread '{}' at code address {}: {}",
|
||||
GetThreadName(), fmt::ptr(code_address),
|
||||
DisassembleInstruction(code_address));
|
||||
}
|
||||
break;
|
||||
case SIGUSR1: { // Sleep thread until signal is received
|
||||
sigset_t sigset;
|
||||
sigemptyset(&sigset);
|
||||
sigaddset(&sigset, SIGUSR1);
|
||||
sigwait(&sigset, &sig);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
SignalDispatch::SignalDispatch() {
|
||||
#if defined(_WIN32)
|
||||
ASSERT_MSG(handle = AddVectoredExceptionHandler(0, SignalHandler),
|
||||
"Failed to register exception handler.");
|
||||
#else
|
||||
struct sigaction action{};
|
||||
action.sa_sigaction = SignalHandler;
|
||||
action.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
sigemptyset(&action.sa_mask);
|
||||
|
||||
ASSERT_MSG(sigaction(SIGSEGV, &action, nullptr) == 0 &&
|
||||
sigaction(SIGBUS, &action, nullptr) == 0,
|
||||
"Failed to register access violation signal handler.");
|
||||
ASSERT_MSG(sigaction(SIGILL, &action, nullptr) == 0,
|
||||
"Failed to register illegal instruction signal handler.");
|
||||
ASSERT_MSG(sigaction(SIGUSR1, &action, nullptr) == 0,
|
||||
"Failed to register sleep signal handler.");
|
||||
#endif
|
||||
}
|
||||
|
||||
SignalDispatch::~SignalDispatch() {
|
||||
#if defined(_WIN32)
|
||||
ASSERT_MSG(RemoveVectoredExceptionHandler(handle), "Failed to remove exception handler.");
|
||||
#else
|
||||
struct sigaction action{};
|
||||
action.sa_handler = SIG_DFL;
|
||||
action.sa_flags = 0;
|
||||
sigemptyset(&action.sa_mask);
|
||||
|
||||
ASSERT_MSG(sigaction(SIGSEGV, &action, nullptr) == 0 &&
|
||||
sigaction(SIGBUS, &action, nullptr) == 0,
|
||||
"Failed to remove access violation signal handler.");
|
||||
ASSERT_MSG(sigaction(SIGILL, &action, nullptr) == 0,
|
||||
"Failed to remove illegal instruction signal handler.");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SignalDispatch::DispatchAccessViolation(void* context, void* fault_address) const {
|
||||
for (const auto& [handler, _] : access_violation_handlers) {
|
||||
if (handler(context, fault_address)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SignalDispatch::DispatchIllegalInstruction(void* context) const {
|
||||
for (const auto& [handler, _] : illegal_instruction_handlers) {
|
||||
if (handler(context)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
@@ -1,57 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include "common/singleton.h"
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
using AccessViolationHandler = bool (*)(void* context, void* fault_address);
|
||||
using IllegalInstructionHandler = bool (*)(void* context);
|
||||
|
||||
/// Receives OS signals and dispatches to the appropriate handlers.
|
||||
class SignalDispatch {
|
||||
public:
|
||||
SignalDispatch();
|
||||
~SignalDispatch();
|
||||
|
||||
/// Registers a handler for memory access violation signals.
|
||||
void RegisterAccessViolationHandler(const AccessViolationHandler& handler, u32 priority) {
|
||||
access_violation_handlers.emplace(handler, priority);
|
||||
}
|
||||
|
||||
/// Registers a handler for illegal instruction signals.
|
||||
void RegisterIllegalInstructionHandler(const IllegalInstructionHandler& handler, u32 priority) {
|
||||
illegal_instruction_handlers.emplace(handler, priority);
|
||||
}
|
||||
|
||||
/// Dispatches an access violation signal, returning whether it was successfully handled.
|
||||
bool DispatchAccessViolation(void* context, void* fault_address) const;
|
||||
|
||||
/// Dispatches an illegal instruction signal, returning whether it was successfully handled.
|
||||
bool DispatchIllegalInstruction(void* context) const;
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
struct HandlerEntry {
|
||||
T handler;
|
||||
u32 priority;
|
||||
|
||||
std::strong_ordering operator<=>(const HandlerEntry& right) const {
|
||||
return priority <=> right.priority;
|
||||
}
|
||||
};
|
||||
std::set<HandlerEntry<AccessViolationHandler>> access_violation_handlers;
|
||||
std::set<HandlerEntry<IllegalInstructionHandler>> illegal_instruction_handlers;
|
||||
|
||||
#ifdef _WIN32
|
||||
void* handle{};
|
||||
#endif
|
||||
};
|
||||
|
||||
using Signals = Common::Singleton<SignalDispatch>;
|
||||
|
||||
} // namespace Core
|
||||
@@ -1,150 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
#include "thread.h"
|
||||
#ifdef _WIN64
|
||||
#include <windows.h>
|
||||
#include "common/ntapi.h"
|
||||
#else
|
||||
#include <csignal>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
namespace Core {
|
||||
|
||||
static constexpr u32 ORBIS_MXCSR = 0x9fc0;
|
||||
static constexpr u32 ORBIS_FPUCW = 0x037f;
|
||||
|
||||
#ifdef _WIN64
|
||||
#define KGDT64_R3_DATA (0x28)
|
||||
#define KGDT64_R3_CODE (0x30)
|
||||
#define KGDT64_R3_CMTEB (0x50)
|
||||
#define RPL_MASK (0x03)
|
||||
#define EFLAGS_INTERRUPT_MASK (0x200)
|
||||
|
||||
void InitializeTeb(INITIAL_TEB* teb, const ::Libraries::Kernel::PthreadAttr* attr) {
|
||||
teb->StackBase = (void*)((u64)attr->stackaddr_attr + attr->stacksize_attr);
|
||||
teb->StackLimit = nullptr;
|
||||
teb->StackAllocationBase = attr->stackaddr_attr;
|
||||
}
|
||||
|
||||
void InitializeContext(CONTEXT* ctx, ThreadFunc func, void* arg,
|
||||
const ::Libraries::Kernel::PthreadAttr* attr) {
|
||||
/* Note: The stack has to be reversed */
|
||||
ctx->Rsp = (u64)attr->stackaddr_attr + attr->stacksize_attr;
|
||||
ctx->Rbp = (u64)attr->stackaddr_attr + attr->stacksize_attr;
|
||||
ctx->Rcx = (u64)arg;
|
||||
ctx->Rip = (u64)func;
|
||||
|
||||
ctx->SegGs = KGDT64_R3_DATA | RPL_MASK;
|
||||
ctx->SegEs = KGDT64_R3_DATA | RPL_MASK;
|
||||
ctx->SegDs = KGDT64_R3_DATA | RPL_MASK;
|
||||
ctx->SegCs = KGDT64_R3_CODE | RPL_MASK;
|
||||
ctx->SegSs = KGDT64_R3_DATA | RPL_MASK;
|
||||
ctx->SegFs = KGDT64_R3_CMTEB | RPL_MASK;
|
||||
|
||||
ctx->EFlags = 0x3000 | EFLAGS_INTERRUPT_MASK;
|
||||
|
||||
ctx->ContextFlags =
|
||||
CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT;
|
||||
}
|
||||
#endif
|
||||
|
||||
NativeThread::NativeThread() : native_handle{0} {}
|
||||
|
||||
NativeThread::~NativeThread() {}
|
||||
|
||||
int NativeThread::Create(ThreadFunc func, void* arg, const ::Libraries::Kernel::PthreadAttr* attr) {
|
||||
#ifndef _WIN64
|
||||
pthread_t* pthr = reinterpret_cast<pthread_t*>(&native_handle);
|
||||
pthread_attr_t pattr;
|
||||
pthread_attr_init(&pattr);
|
||||
pthread_attr_setstack(&pattr, attr->stackaddr_attr, attr->stacksize_attr);
|
||||
return pthread_create(pthr, &pattr, (PthreadFunc)func, arg);
|
||||
#else
|
||||
CLIENT_ID clientId{};
|
||||
INITIAL_TEB teb{};
|
||||
CONTEXT ctx{};
|
||||
|
||||
clientId.UniqueProcess = GetCurrentProcess();
|
||||
clientId.UniqueThread = GetCurrentThread();
|
||||
|
||||
InitializeTeb(&teb, attr);
|
||||
InitializeContext(&ctx, func, arg, attr);
|
||||
|
||||
return NtCreateThread(&native_handle, THREAD_ALL_ACCESS, nullptr, GetCurrentProcess(),
|
||||
&clientId, &ctx, &teb, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void NativeThread::Exit() {
|
||||
if (!native_handle) {
|
||||
return;
|
||||
}
|
||||
|
||||
tid = 0;
|
||||
|
||||
#ifdef _WIN64
|
||||
NtClose(native_handle);
|
||||
native_handle = nullptr;
|
||||
|
||||
/* The Windows kernel will free the stack
|
||||
given at thread creation via INITIAL_TEB
|
||||
(StackAllocationBase) upon thread termination.
|
||||
|
||||
In earlier Windows versions (NT4 to Windows Server 2003),
|
||||
you could get around this via disabling FreeStackOnTermination
|
||||
on the TEB. This has been removed since then.
|
||||
|
||||
To avoid this, we must forcefully set the TEB
|
||||
deallocation stack pointer to NULL so ZwFreeVirtualMemory fails
|
||||
in the kernel and our stack is not freed.
|
||||
*/
|
||||
auto* teb = reinterpret_cast<TEB*>(NtCurrentTeb());
|
||||
teb->DeallocationStack = nullptr;
|
||||
|
||||
NtTerminateThread(nullptr, 0);
|
||||
#else
|
||||
// Disable and free the signal stack.
|
||||
constexpr stack_t sig_stack = {
|
||||
.ss_flags = SS_DISABLE,
|
||||
};
|
||||
sigaltstack(&sig_stack, nullptr);
|
||||
|
||||
if (sig_stack_ptr) {
|
||||
free(sig_stack_ptr);
|
||||
sig_stack_ptr = nullptr;
|
||||
}
|
||||
|
||||
pthread_exit(nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void NativeThread::Initialize() {
|
||||
// Set MXCSR and FPUCW registers to the values used by Orbis.
|
||||
_mm_setcsr(ORBIS_MXCSR);
|
||||
asm volatile("fldcw %0" : : "m"(ORBIS_FPUCW));
|
||||
#if _WIN64
|
||||
tid = GetCurrentThreadId();
|
||||
#else
|
||||
tid = (u64)pthread_self();
|
||||
|
||||
// Set up an alternate signal handler stack to avoid overflowing small thread stacks.
|
||||
const size_t page_size = getpagesize();
|
||||
const size_t sig_stack_size = Common::AlignUp(std::max<size_t>(64_KB, MINSIGSTKSZ), page_size);
|
||||
ASSERT_MSG(posix_memalign(&sig_stack_ptr, page_size, sig_stack_size) == 0,
|
||||
"Failed to allocate signal stack: {}", errno);
|
||||
|
||||
stack_t sig_stack;
|
||||
sig_stack.ss_sp = sig_stack_ptr;
|
||||
sig_stack.ss_size = sig_stack_size;
|
||||
sig_stack.ss_flags = 0;
|
||||
ASSERT_MSG(sigaltstack(&sig_stack, nullptr) == 0, "Failed to set signal stack: {}", errno);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
@@ -1,45 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
struct PthreadAttr;
|
||||
} // namespace Libraries::Kernel
|
||||
|
||||
namespace Core {
|
||||
|
||||
using ThreadFunc = void (*)(void*);
|
||||
using PthreadFunc = void* (*)(void*);
|
||||
|
||||
class NativeThread {
|
||||
public:
|
||||
NativeThread();
|
||||
~NativeThread();
|
||||
|
||||
int Create(ThreadFunc func, void* arg, const ::Libraries::Kernel::PthreadAttr* attr);
|
||||
void Exit();
|
||||
|
||||
void Initialize();
|
||||
|
||||
uintptr_t GetHandle() {
|
||||
return reinterpret_cast<uintptr_t>(native_handle);
|
||||
}
|
||||
|
||||
u64 GetTid() {
|
||||
return tid;
|
||||
}
|
||||
|
||||
private:
|
||||
#ifdef _WIN64
|
||||
void* native_handle;
|
||||
#else
|
||||
uintptr_t native_handle;
|
||||
void* sig_stack_ptr;
|
||||
#endif
|
||||
u64 tid;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <QFileInfo>
|
||||
#include <QProcess>
|
||||
|
||||
#include "core/memory_patcher.h"
|
||||
#include "common/memory_patcher.h"
|
||||
|
||||
class IpcClient : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/memory_patcher.h"
|
||||
#include "qt_gui/game_install_dialog.h"
|
||||
#include "qt_gui/main_window.h"
|
||||
#ifdef _WIN32
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/memory_patcher.h"
|
||||
#include "common/path_util.h"
|
||||
#include "core/module.h"
|
||||
|
||||
CheatsPatches::CheatsPatches(const QString& gameName, const QString& gameSerial,
|
||||
const QString& gameVersion, const QString& gameSize,
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/path_util.h"
|
||||
#include "common/string_util.h"
|
||||
#include "game_list_frame.h"
|
||||
#include "game_list_utils.h"
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "common/config.h"
|
||||
#include "common/path_util.h"
|
||||
#include "game_info.h"
|
||||
#include "sdl_window.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QCloseEvent>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user