Merge branch 'shadps4-emu:main' into multikey

This commit is contained in:
tlarok 2025-05-10 11:31:26 +02:00 committed by GitHub
commit bdebf67293
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
123 changed files with 3837 additions and 838 deletions

View File

@ -30,7 +30,7 @@ jobs:
- name: Install - name: Install
run: | run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 main' sudo add-apt-repository 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main'
sudo apt update sudo apt update
sudo apt install clang-format-19 sudo apt install clang-format-19
- name: Build - name: Build
@ -205,12 +205,12 @@ jobs:
run: | run: |
mkdir upload mkdir upload
mv ${{github.workspace}}/build/shadps4 upload mv ${{github.workspace}}/build/shadps4 upload
cp ${{github.workspace}}/build/externals/MoltenVK/libMoltenVK.dylib upload mv ${{github.workspace}}/build/MoltenVK_icd.json upload
tar cf shadps4-macos-sdl.tar.gz -C upload . mv ${{github.workspace}}/build/libMoltenVK.dylib upload
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
name: shadps4-macos-sdl-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }} name: shadps4-macos-sdl-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
path: shadps4-macos-sdl.tar.gz path: upload/
macos-qt: macos-qt:
runs-on: macos-15 runs-on: macos-15
@ -281,8 +281,13 @@ jobs:
with: with:
submodules: recursive submodules: recursive
- name: Add LLVM repository
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo add-apt-repository 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main'
- name: Install dependencies - name: Install dependencies
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang-19 mold build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev
- name: Cache CMake Configuration - name: Cache CMake Configuration
uses: actions/cache@v4 uses: actions/cache@v4
@ -304,7 +309,7 @@ jobs:
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- name: Configure CMake - name: Configure CMake
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19 -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build - name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
@ -337,8 +342,13 @@ jobs:
with: with:
submodules: recursive submodules: recursive
- name: Add LLVM repository
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo add-apt-repository 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main'
- name: Install dependencies - name: Install dependencies
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang-19 mold build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev
- name: Cache CMake Configuration - name: Cache CMake Configuration
uses: actions/cache@v4 uses: actions/cache@v4
@ -360,7 +370,7 @@ jobs:
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- name: Configure CMake - name: Configure CMake
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19 -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=mold" -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build - name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
@ -385,7 +395,7 @@ jobs:
submodules: recursive submodules: recursive
- name: Install dependencies - name: Install dependencies
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 mold build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev
- name: Cache CMake Configuration - name: Cache CMake Configuration
uses: actions/cache@v4 uses: actions/cache@v4
@ -407,7 +417,7 @@ jobs:
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- name: Configure CMake - name: Configure CMake
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build - name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
@ -421,7 +431,7 @@ jobs:
submodules: recursive submodules: recursive
- name: Install dependencies - name: Install dependencies
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 mold build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev
- name: Cache CMake Configuration - name: Cache CMake Configuration
uses: actions/cache@v4 uses: actions/cache@v4
@ -443,7 +453,7 @@ jobs:
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- name: Configure CMake - name: Configure CMake
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=mold" -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build - name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)

2
.gitmodules vendored
View File

@ -97,7 +97,7 @@
shallow = true shallow = true
[submodule "externals/MoltenVK/SPIRV-Cross"] [submodule "externals/MoltenVK/SPIRV-Cross"]
path = externals/MoltenVK/SPIRV-Cross path = externals/MoltenVK/SPIRV-Cross
url = https://github.com/billhollings/SPIRV-Cross url = https://github.com/KhronosGroup/SPIRV-Cross
shallow = true shallow = true
[submodule "externals/MoltenVK/MoltenVK"] [submodule "externals/MoltenVK/MoltenVK"]
path = externals/MoltenVK/MoltenVK path = externals/MoltenVK/MoltenVK

72
CMakeLists.txt Executable file → Normal file
View File

@ -202,7 +202,7 @@ execute_process(
# Set Version # Set Version
set(EMULATOR_VERSION_MAJOR "0") set(EMULATOR_VERSION_MAJOR "0")
set(EMULATOR_VERSION_MINOR "7") set(EMULATOR_VERSION_MINOR "8")
set(EMULATOR_VERSION_PATCH "1") set(EMULATOR_VERSION_PATCH "1")
set_source_files_properties(src/shadps4.rc PROPERTIES COMPILE_DEFINITIONS "EMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR};EMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR};EMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH}") set_source_files_properties(src/shadps4.rc PROPERTIES COMPILE_DEFINITIONS "EMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR};EMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR};EMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH}")
@ -371,11 +371,19 @@ set(NETWORK_LIBS src/core/libraries/network/http.cpp
src/core/libraries/network/net_ctl_obj.cpp src/core/libraries/network/net_ctl_obj.cpp
src/core/libraries/network/net_ctl_obj.h src/core/libraries/network/net_ctl_obj.h
src/core/libraries/network/net_ctl_codes.h src/core/libraries/network/net_ctl_codes.h
src/core/libraries/network/net_util.cpp
src/core/libraries/network/net_util.h
src/core/libraries/network/net_error.h
src/core/libraries/network/net.h src/core/libraries/network/net.h
src/core/libraries/network/ssl.cpp src/core/libraries/network/ssl.cpp
src/core/libraries/network/ssl.h src/core/libraries/network/ssl.h
src/core/libraries/network/ssl2.cpp src/core/libraries/network/ssl2.cpp
src/core/libraries/network/ssl2.h src/core/libraries/network/ssl2.h
src/core/libraries/network/sys_net.cpp
src/core/libraries/network/sys_net.h
src/core/libraries/network/posix_sockets.cpp
src/core/libraries/network/p2p_sockets.cpp
src/core/libraries/network/sockets.h
) )
set(AVPLAYER_LIB src/core/libraries/avplayer/avplayer_common.cpp set(AVPLAYER_LIB src/core/libraries/avplayer/avplayer_common.cpp
@ -589,6 +597,8 @@ set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp
src/core/libraries/move/move.h src/core/libraries/move/move.h
src/core/libraries/ulobjmgr/ulobjmgr.cpp src/core/libraries/ulobjmgr/ulobjmgr.cpp
src/core/libraries/ulobjmgr/ulobjmgr.h src/core/libraries/ulobjmgr/ulobjmgr.h
src/core/libraries/signin_dialog/signindialog.cpp
src/core/libraries/signin_dialog/signindialog.h
) )
set(DEV_TOOLS src/core/devtools/layer.cpp set(DEV_TOOLS src/core/devtools/layer.cpp
@ -840,6 +850,7 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h
src/shader_recompiler/ir/passes/identity_removal_pass.cpp src/shader_recompiler/ir/passes/identity_removal_pass.cpp
src/shader_recompiler/ir/passes/ir_passes.h src/shader_recompiler/ir/passes/ir_passes.h
src/shader_recompiler/ir/passes/lower_buffer_format_to_raw.cpp src/shader_recompiler/ir/passes/lower_buffer_format_to_raw.cpp
src/shader_recompiler/ir/passes/lower_fp64_to_fp32.cpp
src/shader_recompiler/ir/passes/readlane_elimination_pass.cpp src/shader_recompiler/ir/passes/readlane_elimination_pass.cpp
src/shader_recompiler/ir/passes/resource_tracking_pass.cpp src/shader_recompiler/ir/passes/resource_tracking_pass.cpp
src/shader_recompiler/ir/passes/ring_access_elimination.cpp src/shader_recompiler/ir/passes/ring_access_elimination.cpp
@ -1083,34 +1094,43 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND ENABLE_USERFAULTFD)
endif() endif()
if (APPLE) if (APPLE)
if (ENABLE_QT_GUI) # Include MoltenVK, along with an ICD file so it can be found by the system Vulkan loader if used for loading layers.
# Include MoltenVK in the app bundle, along with an ICD file so it can be found by the system Vulkan loader if used for loading layers. if (ENABLE_QT_GUI)
set(MVK_ICD ${CMAKE_CURRENT_SOURCE_DIR}/externals/MoltenVK/MoltenVK_icd.json) set(MVK_BUNDLE_PATH "Resources/vulkan/icd.d")
target_sources(shadps4 PRIVATE ${MVK_ICD}) set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path/../${MVK_BUNDLE_PATH}")
set_source_files_properties(${MVK_ICD} PROPERTIES MACOSX_PACKAGE_LOCATION Resources/vulkan/icd.d) set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR}/shadps4.app/Contents/${MVK_BUNDLE_PATH})
else()
set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path")
set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR})
endif()
set(MVK_DYLIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/externals/MoltenVK/libMoltenVK.dylib) set(MVK_DYLIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/externals/MoltenVK/libMoltenVK.dylib)
set(MVK_DYLIB_DST ${CMAKE_CURRENT_BINARY_DIR}/shadps4.app/Contents/Frameworks/libMoltenVK.dylib) set(MVK_DYLIB_DST ${MVK_DST}/libMoltenVK.dylib)
add_custom_command( set(MVK_ICD_SRC ${CMAKE_CURRENT_SOURCE_DIR}/externals/MoltenVK/MoltenVK/MoltenVK/icd/MoltenVK_icd.json)
OUTPUT ${MVK_DYLIB_DST} set(MVK_ICD_DST ${MVK_DST}/MoltenVK_icd.json)
DEPENDS ${MVK_DYLIB_SRC}
COMMAND cmake -E copy ${MVK_DYLIB_SRC} ${MVK_DYLIB_DST})
add_custom_target(CopyMoltenVK DEPENDS ${MVK_DYLIB_DST})
add_dependencies(CopyMoltenVK MoltenVK)
add_dependencies(shadps4 CopyMoltenVK)
set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path/../Frameworks")
else()
# For non-bundled SDL build, just do a normal library link.
target_link_libraries(shadps4 PRIVATE MoltenVK)
endif()
if (ARCHITECTURE STREQUAL "x86_64") add_custom_command(
# Reserve system-managed memory space. OUTPUT ${MVK_DST}
target_link_options(shadps4 PRIVATE -Wl,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,SYSTEM_MANAGED,0x400000,-segaddr,SYSTEM_RESERVED,0x7FFFFC000,-image_base,0x20000000000) COMMAND ${CMAKE_COMMAND} -E make_directory ${MVK_DST})
endif() add_custom_command(
OUTPUT ${MVK_ICD_DST}
DEPENDS ${MVK_ICD_SRC} ${MVK_DST}
COMMAND ${CMAKE_COMMAND} -E copy ${MVK_ICD_SRC} ${MVK_ICD_DST})
add_custom_command(
OUTPUT ${MVK_DYLIB_DST}
DEPENDS ${MVK_DYLIB_SRC} ${MVK_DST}
COMMAND ${CMAKE_COMMAND} -E copy ${MVK_DYLIB_SRC} ${MVK_DYLIB_DST})
add_custom_target(CopyMoltenVK DEPENDS ${MVK_ICD_DST} ${MVK_DYLIB_DST})
add_dependencies(CopyMoltenVK MoltenVK)
add_dependencies(shadps4 CopyMoltenVK)
# Replacement for std::chrono::time_zone if (ARCHITECTURE STREQUAL "x86_64")
target_link_libraries(shadps4 PRIVATE date::date-tz) # Reserve system-managed memory space.
target_link_options(shadps4 PRIVATE -Wl,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,SYSTEM_MANAGED,0x400000,-segaddr,SYSTEM_RESERVED,0x7FFFFC000,-image_base,0x20000000000)
endif()
# Replacement for std::chrono::time_zone
target_link_libraries(shadps4 PRIVATE date::date-tz)
endif() endif()
if (NOT ENABLE_QT_GUI) if (NOT ENABLE_QT_GUI)

View File

@ -13,7 +13,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
<h1 align="center"> <h1 align="center">
<a href="https://discord.gg/bFJxfftGW6"> <a href="https://discord.gg/bFJxfftGW6">
<img src="https://img.shields.io/discord/1080089157554155590?color=5865F2&label=shadPS4 Discord&logo=Discord&logoColor=white" width="240"> <img src="https://img.shields.io/discord/1080089157554155590?color=5865F2&label=shadPS4%20Discord&logo=Discord&logoColor=white" width="275">
<a href="https://github.com/shadps4-emu/shadPS4/releases/latest"> <a href="https://github.com/shadps4-emu/shadPS4/releases/latest">
<img src="https://img.shields.io/github/downloads/shadps4-emu/shadPS4/total.svg" width="140"> <img src="https://img.shields.io/github/downloads/shadps4-emu/shadPS4/total.svg" width="140">
<a href="https://shadps4.net/"> <a href="https://shadps4.net/">

View File

@ -37,6 +37,9 @@
<category translate="no">Game</category> <category translate="no">Game</category>
</categories> </categories>
<releases> <releases>
<release version="0.8.0" date="2025-05-23">
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.8.0</url>
</release>
<release version="0.7.0" date="2025-03-23"> <release version="0.7.0" date="2025-03-23">
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.7.0</url> <url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.7.0</url>
</release> </release>

@ -1 +1 @@
Subproject commit 067fc6c85b02f37dfda58eeda49d8458e093ed60 Subproject commit 87a8e8b13d4ad8835367fea1ebad1896d0460946

View File

@ -1,8 +0,0 @@
{
"file_format_version": "1.0.0",
"ICD": {
"library_path": "../../../Frameworks/libMoltenVK.dylib",
"api_version": "1.2.0",
"is_portability_driver": true
}
}

@ -1 +1 @@
Subproject commit 185833a61cbe29ce3bfb5a499ffb3dfeaee3bbe7 Subproject commit 7918775748c5e2f5c40d9918ce68825035b5a1e1

2
externals/sirit vendored

@ -1 +1 @@
Subproject commit 427a42c9ed99b38204d9107bc3dc14e92458acf1 Subproject commit 09a1416ab1b59ddfebd2618412f118f2004f3b2c

View File

@ -131,9 +131,7 @@ namespace {
case SeekOrigin::End: case SeekOrigin::End:
return SEEK_END; return SEEK_END;
default: default:
LOG_ERROR(Common_Filesystem, "Unsupported origin {}, defaulting to SEEK_SET", UNREACHABLE_MSG("Impossible SeekOrigin {}", static_cast<u32>(origin));
static_cast<u32>(origin));
return SEEK_SET;
} }
} }

View File

@ -61,8 +61,6 @@ enum class SeekOrigin : u32 {
SetOrigin, // Seeks from the start of the file. SetOrigin, // Seeks from the start of the file.
CurrentPosition, // Seeks from the current file pointer position. CurrentPosition, // Seeks from the current file pointer position.
End, // Seeks from the end of the file. End, // Seeks from the end of the file.
SeekHole, // Seeks from the start of the next hole in the file.
SeekData, // Seeks from the start of the next non-hole region in the file.
}; };
class IOFile final { class IOFile final {

View File

@ -137,6 +137,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Lib, NpParty) \ SUB(Lib, NpParty) \
SUB(Lib, Zlib) \ SUB(Lib, Zlib) \
SUB(Lib, Hmd) \ SUB(Lib, Hmd) \
SUB(Lib, SigninDialog) \
CLS(Frontend) \ CLS(Frontend) \
CLS(Render) \ CLS(Render) \
SUB(Render, Vulkan) \ SUB(Render, Vulkan) \

View File

@ -104,6 +104,7 @@ enum class Class : u8 {
Lib_NpParty, ///< The LibSceNpParty implementation Lib_NpParty, ///< The LibSceNpParty implementation
Lib_Zlib, ///< The LibSceZlib implementation. Lib_Zlib, ///< The LibSceZlib implementation.
Lib_Hmd, ///< The LibSceHmd implementation. Lib_Hmd, ///< The LibSceHmd implementation.
Lib_SigninDialog, ///< The LibSigninDialog implementation.
Frontend, ///< Emulator UI Frontend, ///< Emulator UI
Render, ///< Video Core Render, ///< Video Core
Render_Vulkan, ///< Vulkan backend Render_Vulkan, ///< Vulkan backend

View File

@ -23,7 +23,7 @@
namespace MemoryPatcher { namespace MemoryPatcher {
uintptr_t g_eboot_address; EXPORT uintptr_t g_eboot_address;
uint64_t g_eboot_image_size; uint64_t g_eboot_image_size;
std::string g_game_serial; std::string g_game_serial;
std::string patchFile; std::string patchFile;
@ -169,7 +169,8 @@ void OnGameLoaded() {
if (type == "mask_jump32") if (type == "mask_jump32")
patchMask = MemoryPatcher::PatchMask::Mask_Jump32; patchMask = MemoryPatcher::PatchMask::Mask_Jump32;
if (type == "mask" || type == "mask_jump32" && !maskOffsetStr.empty()) { if ((type == "mask" || type == "mask_jump32") &&
!maskOffsetStr.empty()) {
maskOffsetValue = std::stoi(maskOffsetStr, 0, 10); maskOffsetValue = std::stoi(maskOffsetStr, 0, 10);
} }

View File

@ -6,9 +6,15 @@
#include <string> #include <string>
#include <vector> #include <vector>
#if defined(WIN32)
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __attribute__((visibility("default")))
#endif
namespace MemoryPatcher { namespace MemoryPatcher {
extern uintptr_t g_eboot_address; extern EXPORT uintptr_t g_eboot_address;
extern uint64_t g_eboot_image_size; extern uint64_t g_eboot_image_size;
extern std::string g_game_serial; extern std::string g_game_serial;
extern std::string patchFile; extern std::string patchFile;

View File

@ -60,7 +60,7 @@ static CFURLRef UntranslocateBundlePath(const CFURLRef bundle_path) {
return nullptr; return nullptr;
} }
static std::filesystem::path GetBundleParentDirectory() { static std::optional<std::filesystem::path> GetBundleParentDirectory() {
if (CFBundleRef bundle_ref = CFBundleGetMainBundle()) { if (CFBundleRef bundle_ref = CFBundleGetMainBundle()) {
if (CFURLRef bundle_url_ref = CFBundleCopyBundleURL(bundle_ref)) { if (CFURLRef bundle_url_ref = CFBundleCopyBundleURL(bundle_ref)) {
SCOPE_EXIT { SCOPE_EXIT {
@ -83,14 +83,16 @@ static std::filesystem::path GetBundleParentDirectory() {
} }
} }
} }
return std::filesystem::current_path(); return std::nullopt;
} }
#endif #endif
static auto UserPaths = [] { static auto UserPaths = [] {
#ifdef __APPLE__ #if defined(__APPLE__) && defined(ENABLE_QT_GUI)
// Set the current path to the directory containing the app bundle. // Set the current path to the directory containing the app bundle.
std::filesystem::current_path(GetBundleParentDirectory()); if (const auto bundle_dir = GetBundleParentDirectory()) {
std::filesystem::current_path(*bundle_dir);
}
#endif #endif
// Try the portable user directory first. // Try the portable user directory first.

View File

@ -19,8 +19,6 @@ enum class MemoryPermission : u32 {
}; };
DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission) DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission)
constexpr VAddr CODE_BASE_OFFSET = 0x100000000ULL;
constexpr VAddr SYSTEM_MANAGED_MIN = 0x00000400000ULL; constexpr VAddr SYSTEM_MANAGED_MIN = 0x00000400000ULL;
constexpr VAddr SYSTEM_MANAGED_MAX = 0x07FFFFBFFFULL; constexpr VAddr SYSTEM_MANAGED_MAX = 0x07FFFFBFFFULL;
constexpr VAddr SYSTEM_RESERVED_MIN = 0x07FFFFC000ULL; constexpr VAddr SYSTEM_RESERVED_MIN = 0x07FFFFC000ULL;

View File

@ -464,9 +464,8 @@ static std::pair<bool, u64> TryPatch(u8* code, PatchModule* module) {
if (needs_trampoline && instruction.length < 5) { if (needs_trampoline && instruction.length < 5) {
// Trampoline is needed but instruction is too short to patch. // Trampoline is needed but instruction is too short to patch.
// Return false and length to fall back to the illegal instruction handler, // Return false and length to signal to AOT compilation that this instruction
// or to signal to AOT compilation that this instruction should be skipped and // should be skipped and handled at runtime.
// handled at runtime.
return std::make_pair(false, instruction.length); return std::make_pair(false, instruction.length);
} }
@ -512,136 +511,137 @@ static std::pair<bool, u64> TryPatch(u8* code, PatchModule* module) {
#if defined(ARCH_X86_64) #if defined(ARCH_X86_64)
static bool Is4ByteExtrqOrInsertq(void* code_address) {
u8* bytes = (u8*)code_address;
if (bytes[0] == 0x66 && bytes[1] == 0x0F && bytes[2] == 0x79) {
return true; // extrq
} else if (bytes[0] == 0xF2 && bytes[1] == 0x0F && bytes[2] == 0x79) {
return true; // insertq
} else {
return false;
}
}
static bool TryExecuteIllegalInstruction(void* ctx, void* code_address) { static bool TryExecuteIllegalInstruction(void* ctx, void* code_address) {
ZydisDecodedInstruction instruction; // We need to decode the instruction to find out what it is. Normally we'd use a fully fleshed
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT]; // out decoder like Zydis, however Zydis does a bunch of stuff that impact performance that we
const auto status = // don't care about. We can get information about the instruction a lot faster by writing a mini
Common::Decoder::Instance()->decodeInstruction(instruction, operands, code_address); // decoder here, since we know it is definitely an extrq or an insertq. If for some reason we
// need to interpret more instructions in the future (I don't see why we would), we can revert
// to using Zydis.
ZydisMnemonic mnemonic;
u8* bytes = (u8*)code_address;
if (bytes[0] == 0x66) {
mnemonic = ZYDIS_MNEMONIC_EXTRQ;
} else if (bytes[0] == 0xF2) {
mnemonic = ZYDIS_MNEMONIC_INSERTQ;
} else {
ZydisDecodedInstruction instruction;
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT];
const auto status =
Common::Decoder::Instance()->decodeInstruction(instruction, operands, code_address);
LOG_ERROR(Core, "Unhandled illegal instruction at code address {}: {}",
fmt::ptr(code_address),
ZYAN_SUCCESS(status) ? ZydisMnemonicGetString(instruction.mnemonic)
: "Failed to decode");
return false;
}
switch (instruction.mnemonic) { ASSERT(bytes[1] == 0x0F && bytes[2] == 0x79);
// Note: It's guaranteed that there's no REX prefix in these instructions checked by
// Is4ByteExtrqOrInsertq
u8 modrm = bytes[3];
u8 rm = modrm & 0b111;
u8 reg = (modrm >> 3) & 0b111;
u8 mod = (modrm >> 6) & 0b11;
ASSERT(mod == 0b11); // Any instruction we interpret here uses reg/reg addressing only
int dstIndex = reg;
int srcIndex = rm;
switch (mnemonic) {
case ZYDIS_MNEMONIC_EXTRQ: { case ZYDIS_MNEMONIC_EXTRQ: {
bool immediateForm = operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE && const auto dst = Common::GetXmmPointer(ctx, dstIndex);
operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; const auto src = Common::GetXmmPointer(ctx, srcIndex);
if (immediateForm) {
LOG_CRITICAL(Core, "EXTRQ immediate form should have been patched at code address: {}", u64 lowQWordSrc;
fmt::ptr(code_address)); memcpy(&lowQWordSrc, src, sizeof(lowQWordSrc));
return false;
u64 lowQWordDst;
memcpy(&lowQWordDst, dst, sizeof(lowQWordDst));
u64 length = lowQWordSrc & 0x3F;
u64 mask;
if (length == 0) {
length = 64; // for the check below
mask = 0xFFFF'FFFF'FFFF'FFFF;
} else { } else {
ASSERT_MSG(operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && mask = (1ULL << length) - 1;
operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER &&
operands[0].reg.value >= ZYDIS_REGISTER_XMM0 &&
operands[0].reg.value <= ZYDIS_REGISTER_XMM15 &&
operands[1].reg.value >= ZYDIS_REGISTER_XMM0 &&
operands[1].reg.value <= ZYDIS_REGISTER_XMM15,
"Unexpected operand types for EXTRQ instruction");
const auto dstIndex = operands[0].reg.value - ZYDIS_REGISTER_XMM0;
const auto srcIndex = operands[1].reg.value - ZYDIS_REGISTER_XMM0;
const auto dst = Common::GetXmmPointer(ctx, dstIndex);
const auto src = Common::GetXmmPointer(ctx, srcIndex);
u64 lowQWordSrc;
memcpy(&lowQWordSrc, src, sizeof(lowQWordSrc));
u64 lowQWordDst;
memcpy(&lowQWordDst, dst, sizeof(lowQWordDst));
u64 length = lowQWordSrc & 0x3F;
u64 mask;
if (length == 0) {
length = 64; // for the check below
mask = 0xFFFF'FFFF'FFFF'FFFF;
} else {
mask = (1ULL << length) - 1;
}
u64 index = (lowQWordSrc >> 8) & 0x3F;
if (length + index > 64) {
// Undefined behavior if length + index is bigger than 64 according to the spec,
// we'll warn and continue execution.
LOG_TRACE(Core,
"extrq at {} with length {} and index {} is bigger than 64, "
"undefined behavior",
fmt::ptr(code_address), length, index);
}
lowQWordDst >>= index;
lowQWordDst &= mask;
memcpy(dst, &lowQWordDst, sizeof(lowQWordDst));
Common::IncrementRip(ctx, instruction.length);
return true;
} }
break;
u64 index = (lowQWordSrc >> 8) & 0x3F;
if (length + index > 64) {
// Undefined behavior if length + index is bigger than 64 according to the spec,
// we'll warn and continue execution.
LOG_TRACE(Core,
"extrq at {} with length {} and index {} is bigger than 64, "
"undefined behavior",
fmt::ptr(code_address), length, index);
}
lowQWordDst >>= index;
lowQWordDst &= mask;
memcpy(dst, &lowQWordDst, sizeof(lowQWordDst));
Common::IncrementRip(ctx, 4);
return true;
} }
case ZYDIS_MNEMONIC_INSERTQ: { case ZYDIS_MNEMONIC_INSERTQ: {
bool immediateForm = operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE && const auto dst = Common::GetXmmPointer(ctx, dstIndex);
operands[3].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; const auto src = Common::GetXmmPointer(ctx, srcIndex);
if (immediateForm) {
LOG_CRITICAL(Core, u64 lowQWordSrc, highQWordSrc;
"INSERTQ immediate form should have been patched at code address: {}", memcpy(&lowQWordSrc, src, sizeof(lowQWordSrc));
fmt::ptr(code_address)); memcpy(&highQWordSrc, (u8*)src + 8, sizeof(highQWordSrc));
return false;
u64 lowQWordDst;
memcpy(&lowQWordDst, dst, sizeof(lowQWordDst));
u64 length = highQWordSrc & 0x3F;
u64 mask;
if (length == 0) {
length = 64; // for the check below
mask = 0xFFFF'FFFF'FFFF'FFFF;
} else { } else {
ASSERT_MSG(operands[2].type == ZYDIS_OPERAND_TYPE_UNUSED && mask = (1ULL << length) - 1;
operands[3].type == ZYDIS_OPERAND_TYPE_UNUSED,
"operands 2 and 3 must be unused for register form.");
ASSERT_MSG(operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER,
"operands 0 and 1 must be registers.");
const auto dstIndex = operands[0].reg.value - ZYDIS_REGISTER_XMM0;
const auto srcIndex = operands[1].reg.value - ZYDIS_REGISTER_XMM0;
const auto dst = Common::GetXmmPointer(ctx, dstIndex);
const auto src = Common::GetXmmPointer(ctx, srcIndex);
u64 lowQWordSrc, highQWordSrc;
memcpy(&lowQWordSrc, src, sizeof(lowQWordSrc));
memcpy(&highQWordSrc, (u8*)src + 8, sizeof(highQWordSrc));
u64 lowQWordDst;
memcpy(&lowQWordDst, dst, sizeof(lowQWordDst));
u64 length = highQWordSrc & 0x3F;
u64 mask;
if (length == 0) {
length = 64; // for the check below
mask = 0xFFFF'FFFF'FFFF'FFFF;
} else {
mask = (1ULL << length) - 1;
}
u64 index = (highQWordSrc >> 8) & 0x3F;
if (length + index > 64) {
// Undefined behavior if length + index is bigger than 64 according to the spec,
// we'll warn and continue execution.
LOG_TRACE(Core,
"insertq at {} with length {} and index {} is bigger than 64, "
"undefined behavior",
fmt::ptr(code_address), length, index);
}
lowQWordSrc &= mask;
lowQWordDst &= ~(mask << index);
lowQWordDst |= lowQWordSrc << index;
memcpy(dst, &lowQWordDst, sizeof(lowQWordDst));
Common::IncrementRip(ctx, instruction.length);
return true;
} }
break;
u64 index = (highQWordSrc >> 8) & 0x3F;
if (length + index > 64) {
// Undefined behavior if length + index is bigger than 64 according to the spec,
// we'll warn and continue execution.
LOG_TRACE(Core,
"insertq at {} with length {} and index {} is bigger than 64, "
"undefined behavior",
fmt::ptr(code_address), length, index);
}
lowQWordSrc &= mask;
lowQWordDst &= ~(mask << index);
lowQWordDst |= lowQWordSrc << index;
memcpy(dst, &lowQWordDst, sizeof(lowQWordDst));
Common::IncrementRip(ctx, 4);
return true;
} }
default: { default: {
LOG_ERROR(Core, "Unhandled illegal instruction at code address {}: {}", UNREACHABLE();
fmt::ptr(code_address), ZydisMnemonicGetString(instruction.mnemonic));
return false;
} }
} }
@ -695,9 +695,22 @@ static bool PatchesAccessViolationHandler(void* context, void* /* fault_address
static bool PatchesIllegalInstructionHandler(void* context) { static bool PatchesIllegalInstructionHandler(void* context) {
void* code_address = Common::GetRip(context); void* code_address = Common::GetRip(context);
if (!TryPatchJit(code_address)) { if (Is4ByteExtrqOrInsertq(code_address)) {
// The instruction is not big enough for a relative jump, don't try to patch it and pass it
// to our illegal instruction interpreter directly
return TryExecuteIllegalInstruction(context, code_address); return TryExecuteIllegalInstruction(context, code_address);
} else {
if (!TryPatchJit(code_address)) {
ZydisDecodedInstruction instruction;
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT];
const auto status =
Common::Decoder::Instance()->decodeInstruction(instruction, operands, code_address);
LOG_ERROR(Core, "Failed to patch address {:x} -- mnemonic: {}", (u64)code_address,
ZYAN_SUCCESS(status) ? ZydisMnemonicGetString(instruction.mnemonic)
: "Failed to decode");
}
} }
return true; return true;
} }

View File

@ -40,6 +40,10 @@ public:
return ORBIS_KERNEL_ERROR_EBADF; return ORBIS_KERNEL_ERROR_EBADF;
} }
virtual size_t pwritev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual s64 lseek(s64 offset, int whence) { virtual s64 lseek(s64 offset, int whence) {
return ORBIS_KERNEL_ERROR_EBADF; return ORBIS_KERNEL_ERROR_EBADF;
} }

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <cstdlib> #include <cstdlib>
#include <ctime>
#include "common/logging/log.h" #include "common/logging/log.h"
#include "random_device.h" #include "random_device.h"

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <cstdlib> #include <cstdlib>
#include <ctime>
#include "common/logging/log.h" #include "common/logging/log.h"
#include "srandom_device.h" #include "srandom_device.h"

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <cstdlib> #include <cstdlib>
#include <ctime>
#include "common/logging/log.h" #include "common/logging/log.h"
#include "urandom_device.h" #include "urandom_device.h"

View File

@ -1,11 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "SDL3/SDL_log.h"
#include "layer.h" #include "layer.h"
#include <imgui.h> #include <imgui.h>
#include "SDL3/SDL_log.h"
#include "common/config.h" #include "common/config.h"
#include "common/singleton.h" #include "common/singleton.h"
#include "common/types.h" #include "common/types.h"

View File

@ -1,9 +1,14 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "options.h"
#include <memory>
#include <imgui.h> #include <imgui.h>
#include "options.h" #include "video_core/renderer_vulkan/vk_presenter.h"
extern std::unique_ptr<Vulkan::Presenter> presenter;
namespace Core::Devtools { namespace Core::Devtools {
@ -12,6 +17,7 @@ TOptions Options;
void LoadOptionsConfig(const char* line) { void LoadOptionsConfig(const char* line) {
char str[512]; char str[512];
int i; int i;
float f;
if (sscanf(line, "disassembler_cli_isa=%511[^\n]", str) == 1) { if (sscanf(line, "disassembler_cli_isa=%511[^\n]", str) == 1) {
Options.disassembler_cli_isa = str; Options.disassembler_cli_isa = str;
return; return;
@ -24,12 +30,26 @@ void LoadOptionsConfig(const char* line) {
Options.frame_dump_render_on_collapse = i != 0; Options.frame_dump_render_on_collapse = i != 0;
return; return;
} }
if (sscanf(line, "fsr_enabled=%d", &i) == 1) {
presenter->GetFsrSettingsRef().enable = i != 0;
return;
}
if (sscanf(line, "fsr_rcas_enabled=%d", &i) == 1) {
presenter->GetFsrSettingsRef().use_rcas = i != 0;
return;
}
if (sscanf(line, "fsr_rcas_attenuation=%f", &f) == 1) {
presenter->GetFsrSettingsRef().rcas_attenuation = f;
}
} }
void SerializeOptionsConfig(ImGuiTextBuffer* buf) { void SerializeOptionsConfig(ImGuiTextBuffer* buf) {
buf->appendf("disassembler_cli_isa=%s\n", Options.disassembler_cli_isa.c_str()); buf->appendf("disassembler_cli_isa=%s\n", Options.disassembler_cli_isa.c_str());
buf->appendf("disassembler_cli_spv=%s\n", Options.disassembler_cli_spv.c_str()); buf->appendf("disassembler_cli_spv=%s\n", Options.disassembler_cli_spv.c_str());
buf->appendf("frame_dump_render_on_collapse=%d\n", Options.frame_dump_render_on_collapse); buf->appendf("frame_dump_render_on_collapse=%d\n", Options.frame_dump_render_on_collapse);
buf->appendf("fsr_enabled=%d\n", presenter->GetFsrSettingsRef().enable);
buf->appendf("fsr_rcas_enabled=%d\n", presenter->GetFsrSettingsRef().use_rcas);
buf->appendf("fsr_rcas_attenuation=%f\n", presenter->GetFsrSettingsRef().rcas_attenuation);
} }
} // namespace Core::Devtools } // namespace Core::Devtools

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <cstdio> #include <cstdio>
#include <ctime>
#include <fmt/chrono.h> #include <fmt/chrono.h>
#include <imgui.h> #include <imgui.h>
#include <magic_enum/magic_enum.hpp> #include <magic_enum/magic_enum.hpp>

View File

@ -505,9 +505,10 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti(u32* cmdbuf, u32 size, u32 da
u32 flags) { u32 flags) {
LOG_TRACE(Lib_GnmDriver, "called"); LOG_TRACE(Lib_GnmDriver, "called");
if ((!sceKernelIsNeoMode() || !UseNeoCompatSequences) && !cmdbuf && (size == 16) && if ((!sceKernelIsNeoMode() || !UseNeoCompatSequences) && cmdbuf && (size == 16) &&
(shader_stage < ShaderStages::Max) && (vertex_sgpr_offset < 0x10u) && (vertex_sgpr_offset < 0x10u) && (instance_sgpr_offset < 0x10u) &&
(instance_sgpr_offset < 0x10u)) { (shader_stage == ShaderStages::Vs || shader_stage == ShaderStages::Es ||
shader_stage == ShaderStages::Ls)) {
cmdbuf = WriteHeader<PM4ItOpcode::Nop>(cmdbuf, 2); cmdbuf = WriteHeader<PM4ItOpcode::Nop>(cmdbuf, 2);
cmdbuf = WriteBody(cmdbuf, 0u); cmdbuf = WriteBody(cmdbuf, 0u);
@ -535,10 +536,33 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti(u32* cmdbuf, u32 size, u32 da
return -1; return -1;
} }
int PS4_SYSV_ABI sceGnmDrawIndexIndirectMulti() { int PS4_SYSV_ABI sceGnmDrawIndexIndirectMulti(u32* cmdbuf, u32 size, u32 data_offset, u32 max_count,
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); u32 shader_stage, u32 vertex_sgpr_offset,
UNREACHABLE(); u32 instance_sgpr_offset, u32 flags) {
return ORBIS_OK; LOG_TRACE(Lib_GnmDriver, "called");
if (cmdbuf && (size == 11) && (vertex_sgpr_offset < 0x10u) && (instance_sgpr_offset < 0x10u) &&
(shader_stage == ShaderStages::Vs || shader_stage == ShaderStages::Es ||
shader_stage == ShaderStages::Ls)) {
const auto predicate = flags & 1 ? PM4Predicate::PredEnable : PM4Predicate::PredDisable;
cmdbuf = WriteHeader<PM4ItOpcode::DrawIndexIndirectMulti>(
cmdbuf, 6, PM4ShaderType::ShaderGraphics, predicate);
const auto sgpr_offset = indirect_sgpr_offsets[shader_stage];
cmdbuf[0] = data_offset;
cmdbuf[1] = vertex_sgpr_offset == 0 ? 0 : (vertex_sgpr_offset & 0xffffu) + sgpr_offset;
cmdbuf[2] = instance_sgpr_offset == 0 ? 0 : (instance_sgpr_offset & 0xffffu) + sgpr_offset;
cmdbuf[3] = max_count;
cmdbuf[4] = sizeof(DrawIndexedIndirectArgs);
cmdbuf[5] = sceKernelIsNeoMode() ? flags & 0xe0000000u : 0;
cmdbuf += 6;
WriteTrailingNop<3>(cmdbuf);
return ORBIS_OK;
}
return -1;
} }
int PS4_SYSV_ABI sceGnmDrawIndexMultiInstanced() { int PS4_SYSV_ABI sceGnmDrawIndexMultiInstanced() {

View File

@ -51,7 +51,9 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti(u32* cmdbuf, u32 size, u32 da
u32 max_count, u64 count_addr, u32 shader_stage, u32 max_count, u64 count_addr, u32 shader_stage,
u32 vertex_sgpr_offset, u32 instance_sgpr_offset, u32 vertex_sgpr_offset, u32 instance_sgpr_offset,
u32 flags); u32 flags);
int PS4_SYSV_ABI sceGnmDrawIndexIndirectMulti(); int PS4_SYSV_ABI sceGnmDrawIndexIndirectMulti(u32* cmdbuf, u32 size, u32 data_offset, u32 max_count,
u32 shader_stage, u32 vertex_sgpr_offset,
u32 instance_sgpr_offset, u32 flags);
int PS4_SYSV_ABI sceGnmDrawIndexMultiInstanced(); int PS4_SYSV_ABI sceGnmDrawIndexMultiInstanced();
s32 PS4_SYSV_ABI sceGnmDrawIndexOffset(u32* cmdbuf, u32 size, u32 index_offset, u32 index_count, s32 PS4_SYSV_ABI sceGnmDrawIndexOffset(u32* cmdbuf, u32 size, u32 index_offset, u32 index_count,
u32 flags); u32 flags);

View File

@ -83,9 +83,35 @@ int PS4_SYSV_ABI sceImeDialogGetPanelPositionAndForm() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceImeDialogGetPanelSize() { Error PS4_SYSV_ABI sceImeDialogGetPanelSize(const OrbisImeDialogParam* param, u32* width,
LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); u32* height) {
return ORBIS_OK; LOG_INFO(Lib_ImeDialog, "called");
if (!width || !height) {
return Error::INVALID_ADDRESS;
}
switch (param->type) {
case OrbisImeType::Default:
case OrbisImeType::BasicLatin:
case OrbisImeType::Url:
case OrbisImeType::Mail:
*width = 500; // original: 793
if (True(param->option & OrbisImeDialogOption::Multiline)) {
*height = 300; // original: 576
} else {
*height = 150; // original: 476
}
break;
case OrbisImeType::Number:
*width = 370;
*height = 470;
break;
default:
LOG_ERROR(Lib_ImeDialog, "Unknown OrbisImeType: {}", (u32)param->type);
return Error::INVALID_PARAM;
}
return Error::OK;
} }
int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended() { int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended() {

View File

@ -13,7 +13,7 @@ class SymbolsResolver;
namespace Libraries::ImeDialog { namespace Libraries::ImeDialog {
constexpr u32 ORBIS_IME_DIALOG_MAX_TEXT_LENGTH = 0x78; constexpr u32 ORBIS_IME_DIALOG_MAX_TEXT_LENGTH = 2048;
enum class Error : u32 { enum class Error : u32 {
OK = 0x0, OK = 0x0,
@ -155,7 +155,8 @@ Error PS4_SYSV_ABI sceImeDialogForceClose();
Error PS4_SYSV_ABI sceImeDialogForTestFunction(); Error PS4_SYSV_ABI sceImeDialogForTestFunction();
int PS4_SYSV_ABI sceImeDialogGetCurrentStarState(); int PS4_SYSV_ABI sceImeDialogGetCurrentStarState();
int PS4_SYSV_ABI sceImeDialogGetPanelPositionAndForm(); int PS4_SYSV_ABI sceImeDialogGetPanelPositionAndForm();
int PS4_SYSV_ABI sceImeDialogGetPanelSize(); Error PS4_SYSV_ABI sceImeDialogGetPanelSize(const OrbisImeDialogParam* param, u32* width,
u32* height);
int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended(); int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended();
Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result); Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result);
OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus(); OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus();

View File

@ -61,6 +61,18 @@ struct SceKernelEvent {
void* udata = nullptr; /* opaque user data identifier */ void* udata = nullptr; /* opaque user data identifier */
}; };
struct OrbisVideoOutEventHint {
u64 event_id : 8;
u64 video_id : 8;
u64 flip_arg : 48;
};
struct OrbisVideoOutEventData {
u64 time : 12;
u64 count : 4;
u64 flip_arg : 48;
};
struct EqueueEvent { struct EqueueEvent {
SceKernelEvent event; SceKernelEvent event;
void* data = nullptr; void* data = nullptr;
@ -84,19 +96,18 @@ struct EqueueEvent {
void TriggerDisplay(void* data) { void TriggerDisplay(void* data) {
is_triggered = true; is_triggered = true;
auto hint = reinterpret_cast<u64>(data); if (data != nullptr) {
if (hint != 0) { auto event_data = static_cast<OrbisVideoOutEventData>(event.data);
auto hint_h = static_cast<u32>(hint >> 8) & 0xFFFFFF; auto event_hint_raw = reinterpret_cast<u64>(data);
auto ident_h = static_cast<u32>(event.ident >> 40); auto event_hint = static_cast<OrbisVideoOutEventHint>(event_hint_raw);
if ((static_cast<u32>(hint) & 0xFF) == event.ident && event.ident != 0xFE && if (event_hint.event_id == event.ident && event.ident != 0xfe) {
((hint_h ^ ident_h) & 0xFF) == 0) {
auto time = Common::FencedRDTSC(); auto time = Common::FencedRDTSC();
auto mask = 0xF000; auto counter = event_data.count;
if ((static_cast<u32>(event.data) & 0xF000) != 0xF000) { if (counter != 0xf) {
mask = (static_cast<u32>(event.data) + 0x1000) & 0xF000; counter++;
} }
event.data = (mask | static_cast<u64>(static_cast<u32>(time) & 0xFFF) | event.data =
(hint & 0xFFFFFFFFFFFF0000)); (time & 0xfff) | (counter << 0xc) | (event_hint_raw & 0xffffffffffff0000);
} }
} }
} }

View File

@ -67,10 +67,16 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) {
bool write = (flags & 0x3) == ORBIS_KERNEL_O_WRONLY; bool write = (flags & 0x3) == ORBIS_KERNEL_O_WRONLY;
bool rdwr = (flags & 0x3) == ORBIS_KERNEL_O_RDWR; bool rdwr = (flags & 0x3) == ORBIS_KERNEL_O_RDWR;
if (!read && !write && !rdwr) {
// Start by checking for invalid flags.
*__Error() = POSIX_EINVAL;
return -1;
}
bool nonblock = (flags & ORBIS_KERNEL_O_NONBLOCK) != 0; bool nonblock = (flags & ORBIS_KERNEL_O_NONBLOCK) != 0;
bool append = (flags & ORBIS_KERNEL_O_APPEND) != 0; bool append = (flags & ORBIS_KERNEL_O_APPEND) != 0;
bool fsync = (flags & ORBIS_KERNEL_O_FSYNC) != 0; // Flags fsync and sync behave the same
bool sync = (flags & ORBIS_KERNEL_O_SYNC) != 0; bool sync = (flags & ORBIS_KERNEL_O_SYNC) != 0 || (flags & ORBIS_KERNEL_O_FSYNC) != 0;
bool create = (flags & ORBIS_KERNEL_O_CREAT) != 0; bool create = (flags & ORBIS_KERNEL_O_CREAT) != 0;
bool truncate = (flags & ORBIS_KERNEL_O_TRUNC) != 0; bool truncate = (flags & ORBIS_KERNEL_O_TRUNC) != 0;
bool excl = (flags & ORBIS_KERNEL_O_EXCL) != 0; bool excl = (flags & ORBIS_KERNEL_O_EXCL) != 0;
@ -78,6 +84,10 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) {
bool direct = (flags & ORBIS_KERNEL_O_DIRECT) != 0; bool direct = (flags & ORBIS_KERNEL_O_DIRECT) != 0;
bool directory = (flags & ORBIS_KERNEL_O_DIRECTORY) != 0; bool directory = (flags & ORBIS_KERNEL_O_DIRECTORY) != 0;
if (sync || direct || dsync || nonblock) {
LOG_WARNING(Kernel_Fs, "flags {:#x} not fully handled", flags);
}
std::string_view path{raw_path}; std::string_view path{raw_path};
u32 handle = h->CreateHandle(); u32 handle = h->CreateHandle();
auto* file = h->GetFile(handle); auto* file = h->GetFile(handle);
@ -94,84 +104,127 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) {
} }
} }
if (directory) { bool read_only = false;
file->type = Core::FileSys::FileType::Directory; file->m_guest_name = path;
file->m_guest_name = path; file->m_host_name = mnt->GetHostPath(file->m_guest_name, &read_only);
file->m_host_name = mnt->GetHostPath(file->m_guest_name); bool exists = std::filesystem::exists(file->m_host_name);
if (!std::filesystem::is_directory(file->m_host_name)) { // directory doesn't exist s32 e = 0;
if (create) {
if (excl && exists) {
// Error if file exists
h->DeleteHandle(handle); h->DeleteHandle(handle);
*__Error() = POSIX_ENOENT; *__Error() = POSIX_EEXIST;
return -1;
}
if (read_only) {
// Can't create files in a read only directory
h->DeleteHandle(handle);
*__Error() = POSIX_EROFS;
return -1;
}
// Create a file if it doesn't exist
Common::FS::IOFile out(file->m_host_name, Common::FS::FileAccessMode::Write);
} else if (!exists) {
// If we're not creating a file, and it doesn't exist, return ENOENT
h->DeleteHandle(handle);
*__Error() = POSIX_ENOENT;
return -1;
}
if (std::filesystem::is_directory(file->m_host_name) || directory) {
// Directories can be opened even if the directory flag isn't set.
// In these cases, error behavior is identical to the directory code path.
directory = true;
}
if (directory) {
if (!std::filesystem::is_directory(file->m_host_name)) {
// If the opened file is not a directory, return ENOTDIR.
// This will trigger when create & directory is specified, this is expected.
h->DeleteHandle(handle);
*__Error() = POSIX_ENOTDIR;
return -1;
}
file->type = Core::FileSys::FileType::Directory;
// Populate directory contents
mnt->IterateDirectory(file->m_guest_name,
[&file](const auto& ent_path, const auto ent_is_file) {
auto& dir_entry = file->dirents.emplace_back();
dir_entry.name = ent_path.filename().string();
dir_entry.isFile = ent_is_file;
});
file->dirents_index = 0;
if (read) {
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Read);
} else if (write || rdwr) {
// Cannot open directories with any type of write access
h->DeleteHandle(handle);
*__Error() = POSIX_EISDIR;
return -1;
}
if (e == EACCES) {
// Hack to bypass some platform limitations, ignore the error and continue as normal.
LOG_WARNING(Kernel_Fs, "Opening directories is not fully supported on this platform");
e = 0;
}
if (truncate) {
// Cannot open directories with truncate
h->DeleteHandle(handle);
*__Error() = POSIX_EISDIR;
return -1; return -1;
} else {
if (create) {
return handle; // dir already exists
} else {
mnt->IterateDirectory(file->m_guest_name,
[&file](const auto& ent_path, const auto ent_is_file) {
auto& dir_entry = file->dirents.emplace_back();
dir_entry.name = ent_path.filename().string();
dir_entry.isFile = ent_is_file;
});
file->dirents_index = 0;
}
} }
} else { } else {
file->m_guest_name = path; file->type = Core::FileSys::FileType::Regular;
file->m_host_name = mnt->GetHostPath(file->m_guest_name);
bool exists = std::filesystem::exists(file->m_host_name);
int e = 0;
if (create) { if (truncate && read_only) {
if (excl && exists) { // Can't open files with truncate flag in a read only directory
// Error if file exists
h->DeleteHandle(handle);
*__Error() = POSIX_EEXIST;
return -1;
}
// Create file if it doesn't exist
Common::FS::IOFile out(file->m_host_name, Common::FS::FileAccessMode::Write);
} else if (!exists) {
// File to open doesn't exist, return ENOENT
h->DeleteHandle(handle); h->DeleteHandle(handle);
*__Error() = POSIX_ENOENT; *__Error() = POSIX_EROFS;
return -1; return -1;
} else if (truncate) {
// Open the file as read-write so we can truncate regardless of flags.
// Since open starts by closing the file, this won't interfere with later open calls.
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite);
if (e == 0) {
// If the file was opened successfully, reduce size to 0
file->f.SetSize(0);
}
} }
if (read) { if (read) {
// Read only // Read only
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Read); e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Read);
} else if (read_only) {
// Can't open files with write/read-write access in a read only directory
h->DeleteHandle(handle);
*__Error() = POSIX_EROFS;
return -1;
} else if (append) {
// Append can be specified with rdwr or write, but we treat it as a separate mode.
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append);
} else if (write) { } else if (write) {
// Write only // Write only
if (append) { e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write);
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append);
} else {
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write);
}
} else if (rdwr) { } else if (rdwr) {
// Read and write // Read and write
if (append) { e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite);
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append);
} else {
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite);
}
} else {
// Invalid flags
*__Error() = POSIX_EINVAL;
return -1;
}
if (truncate && e == 0) {
// If the file was opened successfully and truncate was enabled, reduce size to 0
file->f.SetSize(0);
}
if (e != 0) {
// Open failed in platform-specific code, errno needs to be converted.
h->DeleteHandle(handle);
SetPosixErrno(e);
return -1;
} }
} }
if (e != 0) {
// Open failed in platform-specific code, errno needs to be converted.
h->DeleteHandle(handle);
SetPosixErrno(e);
return -1;
}
file->is_opened = true; file->is_opened = true;
return handle; return handle;
} }
@ -365,10 +418,10 @@ s64 PS4_SYSV_ABI posix_lseek(s32 fd, s64 offset, s32 whence) {
origin = Common::FS::SeekOrigin::CurrentPosition; origin = Common::FS::SeekOrigin::CurrentPosition;
} else if (whence == 2) { } else if (whence == 2) {
origin = Common::FS::SeekOrigin::End; origin = Common::FS::SeekOrigin::End;
} else if (whence == 3) { } else if (whence == 3 || whence == 4) {
origin = Common::FS::SeekOrigin::SeekHole; // whence parameter belongs to an unsupported POSIX extension
} else if (whence == 4) { *__Error() = POSIX_ENOTTY;
origin = Common::FS::SeekOrigin::SeekData; return -1;
} else { } else {
// whence parameter is invalid // whence parameter is invalid
*__Error() = POSIX_EINVAL; *__Error() = POSIX_EINVAL;
@ -486,13 +539,13 @@ s32 PS4_SYSV_ABI posix_rmdir(const char* path) {
const std::filesystem::path dir_name = mnt->GetHostPath(path, &ro); const std::filesystem::path dir_name = mnt->GetHostPath(path, &ro);
if (dir_name.empty() || !std::filesystem::is_directory(dir_name)) { if (ro) {
*__Error() = POSIX_ENOTDIR; *__Error() = POSIX_EROFS;
return -1; return -1;
} }
if (ro) { if (dir_name.empty() || !std::filesystem::is_directory(dir_name)) {
*__Error() = POSIX_EROFS; *__Error() = POSIX_ENOTDIR;
return -1; return -1;
} }
@ -523,8 +576,7 @@ s32 PS4_SYSV_ABI sceKernelRmdir(const char* path) {
s32 PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) { s32 PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) {
LOG_INFO(Kernel_Fs, "(PARTIAL) path = {}", path); LOG_INFO(Kernel_Fs, "(PARTIAL) path = {}", path);
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance(); auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
bool ro = false; const auto path_name = mnt->GetHostPath(path);
const auto path_name = mnt->GetHostPath(path, &ro);
std::memset(sb, 0, sizeof(OrbisKernelStat)); std::memset(sb, 0, sizeof(OrbisKernelStat));
const bool is_dir = std::filesystem::is_directory(path_name); const bool is_dir = std::filesystem::is_directory(path_name);
const bool is_file = std::filesystem::is_regular_file(path_name); const bool is_file = std::filesystem::is_regular_file(path_name);
@ -545,9 +597,6 @@ s32 PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) {
sb->st_blocks = (sb->st_size + 511) / 512; sb->st_blocks = (sb->st_size + 511) / 512;
// TODO incomplete // TODO incomplete
} }
if (ro) {
sb->st_mode &= ~0000555u;
}
return ORBIS_OK; return ORBIS_OK;
} }
@ -877,7 +926,7 @@ s32 PS4_SYSV_ABI sceKernelGetdirentries(s32 fd, char* buf, s32 nbytes, s64* base
return result; return result;
} }
s64 PS4_SYSV_ABI posix_pwrite(s32 fd, void* buf, size_t nbytes, s64 offset) { s64 PS4_SYSV_ABI posix_pwritev(s32 fd, const SceKernelIovec* iov, s32 iovcnt, s64 offset) {
if (offset < 0) { if (offset < 0) {
*__Error() = POSIX_EINVAL; *__Error() = POSIX_EINVAL;
return -1; return -1;
@ -893,7 +942,7 @@ s64 PS4_SYSV_ABI posix_pwrite(s32 fd, void* buf, size_t nbytes, s64 offset) {
std::scoped_lock lk{file->m_mutex}; std::scoped_lock lk{file->m_mutex};
if (file->type == Core::FileSys::FileType::Device) { if (file->type == Core::FileSys::FileType::Device) {
s64 result = file->device->pwrite(buf, nbytes, offset); s64 result = file->device->pwritev(iov, iovcnt, offset);
if (result < 0) { if (result < 0) {
ErrSceToPosix(result); ErrSceToPosix(result);
return -1; return -1;
@ -908,7 +957,16 @@ s64 PS4_SYSV_ABI posix_pwrite(s32 fd, void* buf, size_t nbytes, s64 offset) {
*__Error() = POSIX_EIO; *__Error() = POSIX_EIO;
return -1; return -1;
} }
return file->f.WriteRaw<u8>(buf, nbytes); size_t total_written = 0;
for (int i = 0; i < iovcnt; i++) {
total_written += file->f.WriteRaw<u8>(iov[i].iov_base, iov[i].iov_len);
}
return total_written;
}
s64 PS4_SYSV_ABI posix_pwrite(s32 fd, void* buf, size_t nbytes, s64 offset) {
SceKernelIovec iovec{buf, nbytes};
return posix_pwritev(fd, &iovec, 1, offset);
} }
s64 PS4_SYSV_ABI sceKernelPwrite(s32 fd, void* buf, size_t nbytes, s64 offset) { s64 PS4_SYSV_ABI sceKernelPwrite(s32 fd, void* buf, size_t nbytes, s64 offset) {
@ -920,6 +978,15 @@ s64 PS4_SYSV_ABI sceKernelPwrite(s32 fd, void* buf, size_t nbytes, s64 offset) {
return result; return result;
} }
s64 PS4_SYSV_ABI sceKernelPwritev(s32 fd, const SceKernelIovec* iov, s32 iovcnt, s64 offset) {
s64 result = posix_pwritev(fd, iov, iovcnt, offset);
if (result < 0) {
LOG_ERROR(Kernel_Fs, "error = {}", *__Error());
return ErrnoToSceKernelError(*__Error());
}
return result;
}
s32 PS4_SYSV_ABI posix_unlink(const char* path) { s32 PS4_SYSV_ABI posix_unlink(const char* path) {
if (path == nullptr) { if (path == nullptr) {
*__Error() = POSIX_EINVAL; *__Error() = POSIX_EINVAL;
@ -1017,7 +1084,10 @@ void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("sfKygSjIbI8", "libkernel", 1, "libkernel", 1, 1, getdirentries); LIB_FUNCTION("sfKygSjIbI8", "libkernel", 1, "libkernel", 1, 1, getdirentries);
LIB_FUNCTION("taRWhTJFTgE", "libkernel", 1, "libkernel", 1, 1, sceKernelGetdirentries); LIB_FUNCTION("taRWhTJFTgE", "libkernel", 1, "libkernel", 1, 1, sceKernelGetdirentries);
LIB_FUNCTION("C2kJ-byS5rM", "libkernel", 1, "libkernel", 1, 1, posix_pwrite); LIB_FUNCTION("C2kJ-byS5rM", "libkernel", 1, "libkernel", 1, 1, posix_pwrite);
LIB_FUNCTION("FCcmRZhWtOk", "libScePosix", 1, "libkernel", 1, 1, posix_pwritev);
LIB_FUNCTION("FCcmRZhWtOk", "libkernel", 1, "libkernel", 1, 1, posix_pwritev);
LIB_FUNCTION("nKWi-N2HBV4", "libkernel", 1, "libkernel", 1, 1, sceKernelPwrite); LIB_FUNCTION("nKWi-N2HBV4", "libkernel", 1, "libkernel", 1, 1, sceKernelPwrite);
LIB_FUNCTION("mBd4AfLP+u8", "libkernel", 1, "libkernel", 1, 1, sceKernelPwritev);
LIB_FUNCTION("AUXVxWeJU-A", "libkernel", 1, "libkernel", 1, 1, sceKernelUnlink); LIB_FUNCTION("AUXVxWeJU-A", "libkernel", 1, "libkernel", 1, 1, sceKernelUnlink);
} }

View File

@ -24,6 +24,7 @@
#include "core/libraries/kernel/threads/exception.h" #include "core/libraries/kernel/threads/exception.h"
#include "core/libraries/kernel/time.h" #include "core/libraries/kernel/time.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
#include "core/libraries/network/sys_net.h"
#ifdef _WIN64 #ifdef _WIN64
#include <Rpc.h> #include <Rpc.h>
@ -196,10 +197,6 @@ const char* PS4_SYSV_ABI sceKernelGetFsSandboxRandomWord() {
return path; return path;
} }
int PS4_SYSV_ABI posix_connect() {
return -1;
}
int PS4_SYSV_ABI _sigprocmask() { int PS4_SYSV_ABI _sigprocmask() {
return ORBIS_OK; return ORBIS_OK;
} }
@ -225,7 +222,6 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard); LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard);
LIB_FUNCTION("PfccT7qURYE", "libkernel", 1, "libkernel", 1, 1, kernel_ioctl); LIB_FUNCTION("PfccT7qURYE", "libkernel", 1, "libkernel", 1, 1, kernel_ioctl);
LIB_FUNCTION("JGfTMBOdUJo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetFsSandboxRandomWord); LIB_FUNCTION("JGfTMBOdUJo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetFsSandboxRandomWord);
LIB_FUNCTION("XVL8So3QJUk", "libkernel", 1, "libkernel", 1, 1, posix_connect);
LIB_FUNCTION("6xVpy0Fdq+I", "libkernel", 1, "libkernel", 1, 1, _sigprocmask); LIB_FUNCTION("6xVpy0Fdq+I", "libkernel", 1, "libkernel", 1, 1, _sigprocmask);
LIB_FUNCTION("Xjoosiw+XPI", "libkernel", 1, "libkernel", 1, 1, sceKernelUuidCreate); LIB_FUNCTION("Xjoosiw+XPI", "libkernel", 1, "libkernel", 1, 1, sceKernelUuidCreate);
LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail); LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail);
@ -234,6 +230,25 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("k+AXqu2-eBc", "libScePosix", 1, "libkernel", 1, 1, posix_getpagesize); LIB_FUNCTION("k+AXqu2-eBc", "libScePosix", 1, "libkernel", 1, 1, posix_getpagesize);
LIB_FUNCTION("NWtTN10cJzE", "libSceLibcInternalExt", 1, "libSceLibcInternal", 1, 1, LIB_FUNCTION("NWtTN10cJzE", "libSceLibcInternalExt", 1, "libSceLibcInternal", 1, 1,
sceLibcHeapGetTraceInfo); sceLibcHeapGetTraceInfo);
// network
LIB_FUNCTION("XVL8So3QJUk", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_connect);
LIB_FUNCTION("TU-d9PfIHPM", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_socketex);
LIB_FUNCTION("KuOmgKoqCdY", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_bind);
LIB_FUNCTION("pxnCmagrtao", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_listen);
LIB_FUNCTION("3e+4Iv7IJ8U", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_accept);
LIB_FUNCTION("TU-d9PfIHPM", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_socket);
LIB_FUNCTION("oBr313PppNE", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_sendto);
LIB_FUNCTION("lUk6wrGXyMw", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_recvfrom);
LIB_FUNCTION("fFxGkxF2bVo", "libScePosix", 1, "libkernel", 1, 1,
Libraries::Net::sys_setsockopt);
LIB_FUNCTION("RenI1lL1WFk", "libScePosix", 1, "libkernel", 1, 1,
Libraries::Net::sys_getsockname);
LIB_FUNCTION("KuOmgKoqCdY", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_bind);
LIB_FUNCTION("5jRCs2axtr4", "libScePosix", 1, "libkernel", 1, 1,
Libraries::Net::sceNetInetNtop); // TODO fix it to sys_ ...
LIB_FUNCTION("4n51s0zEf0c", "libScePosix", 1, "libkernel", 1, 1,
Libraries::Net::sceNetInetPton); // TODO fix it to sys_ ...
} }
} // namespace Libraries::Kernel } // namespace Libraries::Kernel

View File

@ -18,6 +18,7 @@ namespace Libraries::Kernel {
void ErrSceToPosix(int result); void ErrSceToPosix(int result);
int ErrnoToSceKernelError(int e); int ErrnoToSceKernelError(int e);
void SetPosixErrno(int e); void SetPosixErrno(int e);
int* PS4_SYSV_ABI __Error();
template <StringLiteral name, class F, F f> template <StringLiteral name, class F, F f>
struct WrapperImpl; struct WrapperImpl;

View File

@ -126,9 +126,6 @@ s32 PS4_SYSV_ABI sceKernelAvailableDirectMemorySize(u64 searchStart, u64 searchE
s32 PS4_SYSV_ABI sceKernelVirtualQuery(const void* addr, int flags, OrbisVirtualQueryInfo* info, s32 PS4_SYSV_ABI sceKernelVirtualQuery(const void* addr, int flags, OrbisVirtualQueryInfo* info,
size_t infoSize) { size_t infoSize) {
LOG_INFO(Kernel_Vmm, "called addr = {}, flags = {:#x}", fmt::ptr(addr), flags); LOG_INFO(Kernel_Vmm, "called addr = {}, flags = {:#x}", fmt::ptr(addr), flags);
if (!addr) {
return ORBIS_KERNEL_ERROR_EACCES;
}
auto* memory = Core::Memory::Instance(); auto* memory = Core::Memory::Instance();
return memory->VirtualQuery(std::bit_cast<VAddr>(addr), flags, info); return memory->VirtualQuery(std::bit_cast<VAddr>(addr), flags, info);
} }
@ -136,7 +133,6 @@ s32 PS4_SYSV_ABI sceKernelVirtualQuery(const void* addr, int flags, OrbisVirtual
s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u64 alignment) { s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u64 alignment) {
LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}, flags = {:#x}, alignment = {:#x}", LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}, flags = {:#x}, alignment = {:#x}",
fmt::ptr(*addr), len, flags, alignment); fmt::ptr(*addr), len, flags, alignment);
if (addr == nullptr) { if (addr == nullptr) {
LOG_ERROR(Kernel_Vmm, "Address is invalid!"); LOG_ERROR(Kernel_Vmm, "Address is invalid!");
return ORBIS_KERNEL_ERROR_EINVAL; return ORBIS_KERNEL_ERROR_EINVAL;
@ -155,9 +151,12 @@ s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u
auto* memory = Core::Memory::Instance(); auto* memory = Core::Memory::Instance();
const VAddr in_addr = reinterpret_cast<VAddr>(*addr); const VAddr in_addr = reinterpret_cast<VAddr>(*addr);
const auto map_flags = static_cast<Core::MemoryMapFlags>(flags); const auto map_flags = static_cast<Core::MemoryMapFlags>(flags);
memory->Reserve(addr, in_addr, len, map_flags, alignment);
return ORBIS_OK; s32 result = memory->Reserve(addr, in_addr, len, map_flags, alignment);
if (result == 0) {
LOG_INFO(Kernel_Vmm, "out_addr = {}", fmt::ptr(*addr));
}
return result;
} }
int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, int flags, int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, int flags,
@ -172,10 +171,12 @@ int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, i
LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 16KB aligned!"); LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 16KB aligned!");
return ORBIS_KERNEL_ERROR_EINVAL; return ORBIS_KERNEL_ERROR_EINVAL;
} }
if (!Common::Is16KBAligned(directMemoryStart)) { if (!Common::Is16KBAligned(directMemoryStart)) {
LOG_ERROR(Kernel_Vmm, "Start address is not 16KB aligned!"); LOG_ERROR(Kernel_Vmm, "Start address is not 16KB aligned!");
return ORBIS_KERNEL_ERROR_EINVAL; return ORBIS_KERNEL_ERROR_EINVAL;
} }
if (alignment != 0) { if (alignment != 0) {
if ((!std::has_single_bit(alignment) && !Common::Is16KBAligned(alignment))) { if ((!std::has_single_bit(alignment) && !Common::Is16KBAligned(alignment))) {
LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!"); LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!");
@ -183,14 +184,19 @@ int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, i
} }
} }
if (std::strlen(name) >= ORBIS_KERNEL_MAXIMUM_NAME_LENGTH) {
LOG_ERROR(Kernel_Vmm, "name exceeds 32 bytes!");
return ORBIS_KERNEL_ERROR_ENAMETOOLONG;
}
const VAddr in_addr = reinterpret_cast<VAddr>(*addr); const VAddr in_addr = reinterpret_cast<VAddr>(*addr);
const auto mem_prot = static_cast<Core::MemoryProt>(prot); const auto mem_prot = static_cast<Core::MemoryProt>(prot);
const auto map_flags = static_cast<Core::MemoryMapFlags>(flags); const auto map_flags = static_cast<Core::MemoryMapFlags>(flags);
auto* memory = Core::Memory::Instance(); auto* memory = Core::Memory::Instance();
const auto ret = const auto ret =
memory->MapMemory(addr, in_addr, len, mem_prot, map_flags, Core::VMAType::Direct, "", false, memory->MapMemory(addr, in_addr, len, mem_prot, map_flags, Core::VMAType::Direct, name,
directMemoryStart, alignment); false, directMemoryStart, alignment);
LOG_INFO(Kernel_Vmm, "out_addr = {}", fmt::ptr(*addr)); LOG_INFO(Kernel_Vmm, "out_addr = {}", fmt::ptr(*addr));
return ret; return ret;
@ -199,7 +205,8 @@ int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, i
int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int flags, int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int flags,
s64 directMemoryStart, u64 alignment) { s64 directMemoryStart, u64 alignment) {
LOG_INFO(Kernel_Vmm, "called, redirected to sceKernelMapNamedDirectMemory"); LOG_INFO(Kernel_Vmm, "called, redirected to sceKernelMapNamedDirectMemory");
return sceKernelMapNamedDirectMemory(addr, len, prot, flags, directMemoryStart, alignment, ""); return sceKernelMapNamedDirectMemory(addr, len, prot, flags, directMemoryStart, alignment,
"anon");
} }
s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t len, int prot, s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t len, int prot,
@ -210,17 +217,16 @@ s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t
return ORBIS_KERNEL_ERROR_EINVAL; return ORBIS_KERNEL_ERROR_EINVAL;
} }
static constexpr size_t MaxNameSize = 32;
if (std::strlen(name) > MaxNameSize) {
LOG_ERROR(Kernel_Vmm, "name exceeds 32 bytes!");
return ORBIS_KERNEL_ERROR_ENAMETOOLONG;
}
if (name == nullptr) { if (name == nullptr) {
LOG_ERROR(Kernel_Vmm, "name is invalid!"); LOG_ERROR(Kernel_Vmm, "name is invalid!");
return ORBIS_KERNEL_ERROR_EFAULT; return ORBIS_KERNEL_ERROR_EFAULT;
} }
if (std::strlen(name) >= ORBIS_KERNEL_MAXIMUM_NAME_LENGTH) {
LOG_ERROR(Kernel_Vmm, "name exceeds 32 bytes!");
return ORBIS_KERNEL_ERROR_ENAMETOOLONG;
}
const VAddr in_addr = reinterpret_cast<VAddr>(*addr_in_out); const VAddr in_addr = reinterpret_cast<VAddr>(*addr_in_out);
const auto mem_prot = static_cast<Core::MemoryProt>(prot); const auto mem_prot = static_cast<Core::MemoryProt>(prot);
const auto map_flags = static_cast<Core::MemoryMapFlags>(flags); const auto map_flags = static_cast<Core::MemoryMapFlags>(flags);
@ -236,7 +242,7 @@ s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t
s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len, int prot, s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len, int prot,
int flags) { int flags) {
return sceKernelMapNamedFlexibleMemory(addr_in_out, len, prot, flags, ""); return sceKernelMapNamedFlexibleMemory(addr_in_out, len, prot, flags, "anon");
} }
int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** end, u32* prot) { int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** end, u32* prot) {
@ -304,7 +310,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_MAP_DIRECT: { case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_MAP_DIRECT: {
result = sceKernelMapNamedDirectMemory(&entries[i].start, entries[i].length, result = sceKernelMapNamedDirectMemory(&entries[i].start, entries[i].length,
entries[i].protection, flags, entries[i].protection, flags,
static_cast<s64>(entries[i].offset), 0, ""); static_cast<s64>(entries[i].offset), 0, "anon");
LOG_INFO(Kernel_Vmm, LOG_INFO(Kernel_Vmm,
"entry = {}, operation = {}, len = {:#x}, offset = {:#x}, type = {}, " "entry = {}, operation = {}, len = {:#x}, offset = {:#x}, type = {}, "
"result = {}", "result = {}",
@ -326,7 +332,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
} }
case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_MAP_FLEXIBLE: { case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_MAP_FLEXIBLE: {
result = sceKernelMapNamedFlexibleMemory(&entries[i].start, entries[i].length, result = sceKernelMapNamedFlexibleMemory(&entries[i].start, entries[i].length,
entries[i].protection, flags, ""); entries[i].protection, flags, "anon");
LOG_INFO(Kernel_Vmm, LOG_INFO(Kernel_Vmm,
"entry = {}, operation = {}, len = {:#x}, type = {}, " "entry = {}, operation = {}, len = {:#x}, type = {}, "
"result = {}", "result = {}",
@ -356,16 +362,16 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
} }
s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, const char* name) { s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, const char* name) {
static constexpr size_t MaxNameSize = 32;
if (std::strlen(name) > MaxNameSize) {
LOG_ERROR(Kernel_Vmm, "name exceeds 32 bytes!");
return ORBIS_KERNEL_ERROR_ENAMETOOLONG;
}
if (name == nullptr) { if (name == nullptr) {
LOG_ERROR(Kernel_Vmm, "name is invalid!"); LOG_ERROR(Kernel_Vmm, "name is invalid!");
return ORBIS_KERNEL_ERROR_EFAULT; return ORBIS_KERNEL_ERROR_EFAULT;
} }
if (std::strlen(name) >= ORBIS_KERNEL_MAXIMUM_NAME_LENGTH) {
LOG_ERROR(Kernel_Vmm, "name exceeds 32 bytes!");
return ORBIS_KERNEL_ERROR_ENAMETOOLONG;
}
auto* memory = Core::Memory::Instance(); auto* memory = Core::Memory::Instance();
memory->NameVirtualRange(std::bit_cast<VAddr>(addr), len, name); memory->NameVirtualRange(std::bit_cast<VAddr>(addr), len, name);
return ORBIS_OK; return ORBIS_OK;

View File

@ -47,6 +47,8 @@ enum MemoryOpTypes : u32 {
ORBIS_KERNEL_MAP_OP_TYPE_PROTECT = 4 ORBIS_KERNEL_MAP_OP_TYPE_PROTECT = 4
}; };
constexpr u32 ORBIS_KERNEL_MAXIMUM_NAME_LENGTH = 32;
struct OrbisQueryInfo { struct OrbisQueryInfo {
uintptr_t start; uintptr_t start;
uintptr_t end; uintptr_t end;
@ -59,14 +61,12 @@ struct OrbisVirtualQueryInfo {
size_t offset; size_t offset;
s32 protection; s32 protection;
s32 memory_type; s32 memory_type;
union { u32 is_flexible : 1;
BitField<0, 1, u32> is_flexible; u32 is_direct : 1;
BitField<1, 1, u32> is_direct; u32 is_stack : 1;
BitField<2, 1, u32> is_stack; u32 is_pooled : 1;
BitField<3, 1, u32> is_pooled; u32 is_committed : 1;
BitField<4, 1, u32> is_committed; char name[ORBIS_KERNEL_MAXIMUM_NAME_LENGTH];
};
std::array<char, 32> name;
}; };
struct OrbisKernelBatchMapEntry { struct OrbisKernelBatchMapEntry {

View File

@ -127,6 +127,62 @@ int PS4_SYSV_ABI sceKernelGetModuleInfoFromAddr(VAddr addr, int flags,
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceKernelGetModuleInfo(s32 handle, Core::OrbisKernelModuleInfo* info) {
if (info == nullptr) {
return ORBIS_KERNEL_ERROR_EFAULT;
}
if (info->st_size != sizeof(Core::OrbisKernelModuleInfo)) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
auto* linker = Common::Singleton<Core::Linker>::Instance();
auto* module = linker->GetModule(handle);
if (module == nullptr) {
return ORBIS_KERNEL_ERROR_ESRCH;
}
*info = module->GetModuleInfo();
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceKernelGetModuleInfoInternal(s32 handle, Core::OrbisKernelModuleInfoEx* info) {
if (info == nullptr) {
return ORBIS_KERNEL_ERROR_EFAULT;
}
if (info->st_size != sizeof(Core::OrbisKernelModuleInfoEx)) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
auto* linker = Common::Singleton<Core::Linker>::Instance();
auto* module = linker->GetModule(handle);
if (module == nullptr) {
return ORBIS_KERNEL_ERROR_ESRCH;
}
*info = module->GetModuleInfoEx();
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceKernelGetModuleList(s32* handles, u64 num_array, u64* out_count) {
if (handles == nullptr || out_count == nullptr) {
return ORBIS_KERNEL_ERROR_EFAULT;
}
auto* linker = Common::Singleton<Core::Linker>::Instance();
u64 count = 0;
auto* module = linker->GetModule(count);
while (module != nullptr && count < num_array) {
handles[count] = count;
count++;
module = linker->GetModule(count);
}
if (count == num_array && module != nullptr) {
return ORBIS_KERNEL_ERROR_ENOMEM;
}
*out_count = count;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI exit(s32 status) { s32 PS4_SYSV_ABI exit(s32 status) {
UNREACHABLE_MSG("Exiting with status code {}", status); UNREACHABLE_MSG("Exiting with status code {}", status);
return 0; return 0;
@ -141,6 +197,9 @@ void RegisterProcess(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("LwG8g3niqwA", "libkernel", 1, "libkernel", 1, 1, sceKernelDlsym); LIB_FUNCTION("LwG8g3niqwA", "libkernel", 1, "libkernel", 1, 1, sceKernelDlsym);
LIB_FUNCTION("RpQJJVKTiFM", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfoForUnwind); LIB_FUNCTION("RpQJJVKTiFM", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfoForUnwind);
LIB_FUNCTION("f7KBOafysXo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfoFromAddr); LIB_FUNCTION("f7KBOafysXo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfoFromAddr);
LIB_FUNCTION("kUpgrXIrz7Q", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfo);
LIB_FUNCTION("HZO7xOos4xc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfoInternal);
LIB_FUNCTION("IuxnUuXk6Bg", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleList);
LIB_FUNCTION("6Z83sYWFlA8", "libkernel", 1, "libkernel", 1, 1, exit); LIB_FUNCTION("6Z83sYWFlA8", "libkernel", 1, "libkernel", 1, 1, exit);
} }

View File

@ -289,7 +289,12 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt
/* Create thread */ /* Create thread */
new_thread->native_thr = Core::NativeThread(); new_thread->native_thr = Core::NativeThread();
int ret = new_thread->native_thr.Create(RunThread, new_thread, &new_thread->attr); int ret = new_thread->native_thr.Create(RunThread, new_thread, &new_thread->attr);
ASSERT_MSG(ret == 0, "Failed to create thread with error {}", ret); ASSERT_MSG(ret == 0, "Failed to create thread with error {}", ret);
if (attr != nullptr && *attr != nullptr && (*attr)->cpuset != nullptr) {
new_thread->SetAffinity((*attr)->cpuset);
}
if (ret) { if (ret) {
*thread = nullptr; *thread = nullptr;
} }
@ -521,6 +526,69 @@ int PS4_SYSV_ABI posix_pthread_setcancelstate(PthreadCancelState state,
return 0; return 0;
} }
int Pthread::SetAffinity(const Cpuset* cpuset) {
const auto processor_count = std::thread::hardware_concurrency();
if (processor_count < 8) {
return 0;
}
if (cpuset == nullptr) {
return POSIX_EINVAL;
}
u64 mask = cpuset->bits;
uintptr_t handle = native_thr.GetHandle();
if (handle == 0) {
return POSIX_ESRCH;
}
// We don't use this currently because some games gets performance problems
// when applying affinity even on strong hardware
/*
#ifdef _WIN64
DWORD_PTR affinity_mask = static_cast<DWORD_PTR>(mask);
if (!SetThreadAffinityMask(reinterpret_cast<HANDLE>(handle), affinity_mask)) {
return POSIX_EINVAL;
}
#elif defined(__linux__)
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
u64 mask = cpuset->bits;
for (int cpu = 0; cpu < std::min(64, CPU_SETSIZE); ++cpu) {
if (mask & (1ULL << cpu)) {
CPU_SET(cpu, &cpu_set);
}
}
int result =
pthread_setaffinity_np(static_cast<pthread_t>(handle), sizeof(cpu_set_t), &cpu_set);
if (result != 0) {
return POSIX_EINVAL;
}
#endif
*/
return 0;
}
int PS4_SYSV_ABI posix_pthread_setaffinity_np(PthreadT thread, size_t cpusetsize,
const Cpuset* cpusetp) {
if (thread == nullptr || cpusetp == nullptr) {
return POSIX_EINVAL;
}
thread->attr.cpusetsize = cpusetsize;
return thread->SetAffinity(cpusetp);
}
int PS4_SYSV_ABI scePthreadSetaffinity(PthreadT thread, const Cpuset mask) {
int result = posix_pthread_setaffinity_np(thread, 0x10, &mask);
if (result != 0) {
return ErrnoToSceKernelError(result);
}
return 0;
}
void RegisterThread(Core::Loader::SymbolsResolver* sym) { void RegisterThread(Core::Loader::SymbolsResolver* sym) {
// Posix // Posix
LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once); LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once);
@ -544,6 +612,7 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("Z4QosVuAsA0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_once); LIB_FUNCTION("Z4QosVuAsA0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_once);
LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self); LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self);
LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_create); LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_create);
LIB_FUNCTION("5KWrg7-ZqvE", "libkernel", 1, "libkernel", 1, 1, posix_pthread_setaffinity_np);
// Orbis // Orbis
LIB_FUNCTION("14bOACANTBo", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_once)); LIB_FUNCTION("14bOACANTBo", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_once));
@ -566,6 +635,7 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("W0Hpm2X0uPE", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_setprio)); LIB_FUNCTION("W0Hpm2X0uPE", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_setprio));
LIB_FUNCTION("rNhWz+lvOMU", "libkernel", 1, "libkernel", 1, 1, _sceKernelSetThreadDtors); LIB_FUNCTION("rNhWz+lvOMU", "libkernel", 1, "libkernel", 1, 1, _sceKernelSetThreadDtors);
LIB_FUNCTION("6XG4B33N09g", "libkernel", 1, "libkernel", 1, 1, sched_yield); LIB_FUNCTION("6XG4B33N09g", "libkernel", 1, "libkernel", 1, 1, sched_yield);
LIB_FUNCTION("bt3CTBKmGyI", "libkernel", 1, "libkernel", 1, 1, scePthreadSetaffinity)
} }
} // namespace Libraries::Kernel } // namespace Libraries::Kernel

View File

@ -332,6 +332,8 @@ struct Pthread {
return true; return true;
} }
} }
int SetAffinity(const Cpuset* cpuset);
}; };
using PthreadT = Pthread*; using PthreadT = Pthread*;

View File

@ -56,7 +56,6 @@
#include <stdio.h> #include <stdio.h>
#include <cstdarg> #include <cstdarg>
#include <cstdbool>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>

View File

@ -45,6 +45,7 @@
#include "core/libraries/save_data/savedata.h" #include "core/libraries/save_data/savedata.h"
#include "core/libraries/screenshot/screenshot.h" #include "core/libraries/screenshot/screenshot.h"
#include "core/libraries/share_play/shareplay.h" #include "core/libraries/share_play/shareplay.h"
#include "core/libraries/signin_dialog/signindialog.h"
#include "core/libraries/system/commondialog.h" #include "core/libraries/system/commondialog.h"
#include "core/libraries/system/msgdialog.h" #include "core/libraries/system/msgdialog.h"
#include "core/libraries/system/posix.h" #include "core/libraries/system/posix.h"
@ -120,6 +121,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
Libraries::Hmd::RegisterlibSceHmd(sym); Libraries::Hmd::RegisterlibSceHmd(sym);
Libraries::DiscMap::RegisterlibSceDiscMap(sym); Libraries::DiscMap::RegisterlibSceDiscMap(sym);
Libraries::Ulobjmgr::RegisterlibSceUlobjmgr(sym); Libraries::Ulobjmgr::RegisterlibSceUlobjmgr(sym);
Libraries::SigninDialog::RegisterlibSceSigninDialog(sym);
} }
} // namespace Libraries } // namespace Libraries

View File

@ -10,16 +10,24 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#endif #endif
#include <core/libraries/kernel/kernel.h>
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/singleton.h"
#include "core/libraries/error_codes.h" #include "core/libraries/error_codes.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
#include "core/libraries/network/net.h" #include "core/libraries/network/net.h"
#include "net_error.h"
#include "net_util.h"
#include "netctl.h"
#include "sys_net.h"
namespace Libraries::Net { namespace Libraries::Net {
static thread_local int32_t net_errno = 0; static thread_local int32_t net_errno = 0;
static bool g_isNetInitialized = true; // TODO init it properly
int PS4_SYSV_ABI in6addr_any() { int PS4_SYSV_ABI in6addr_any() {
LOG_ERROR(Lib_Net, "(STUBBED) called"); LOG_ERROR(Lib_Net, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
@ -61,8 +69,45 @@ int PS4_SYSV_ABI sce_net_in6addr_nodelocal_allnodes() {
} }
OrbisNetId PS4_SYSV_ABI sceNetAccept(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen) { OrbisNetId PS4_SYSV_ABI sceNetAccept(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); if (!g_isNetInitialized) {
return ORBIS_OK; return ORBIS_NET_ERROR_ENOTINIT;
}
int result;
int err;
int positiveErr;
do {
result = sys_accept(s, addr, paddrlen);
if (result >= 0) {
return result; // Success
}
err = *Libraries::Kernel::__Error(); // Standard errno
// Convert to positive error for comparison
int positiveErr = (err < 0) ? -err : err;
if ((positiveErr & 0xfff0000) != 0) {
// Unknown/fatal error range
*sceNetErrnoLoc() = ORBIS_NET_ERETURN;
return -positiveErr;
}
// Retry if interrupted
} while (positiveErr == ORBIS_NET_EINTR);
if (positiveErr == ORBIS_NET_EADDRINUSE) {
result = -ORBIS_NET_EBADF;
} else if (positiveErr == ORBIS_NET_EALREADY) {
result = -ORBIS_NET_EINTR;
} else {
result = -positiveErr;
}
*sceNetErrnoLoc() = -result;
return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code
} }
int PS4_SYSV_ABI sceNetAddrConfig6GetInfo() { int PS4_SYSV_ABI sceNetAddrConfig6GetInfo() {
@ -121,8 +166,45 @@ int PS4_SYSV_ABI sceNetBandwidthControlSetPolicy() {
} }
int PS4_SYSV_ABI sceNetBind(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen) { int PS4_SYSV_ABI sceNetBind(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); if (!g_isNetInitialized) {
return ORBIS_OK; return ORBIS_NET_ERROR_ENOTINIT;
}
int result;
int err;
int positiveErr;
do {
result = sys_bind(s, addr, addrlen);
if (result >= 0) {
return result; // Success
}
err = *Libraries::Kernel::__Error(); // Standard errno
// Convert to positive error for comparison
int positiveErr = (err < 0) ? -err : err;
if ((positiveErr & 0xfff0000) != 0) {
// Unknown/fatal error range
*sceNetErrnoLoc() = ORBIS_NET_ERETURN;
return -positiveErr;
}
// Retry if interrupted
} while (positiveErr == ORBIS_NET_EINTR);
if (positiveErr == ORBIS_NET_EADDRINUSE) {
result = -ORBIS_NET_EBADF;
} else if (positiveErr == ORBIS_NET_EALREADY) {
result = -ORBIS_NET_EINTR;
} else {
result = -positiveErr;
}
*sceNetErrnoLoc() = -result;
return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code
} }
int PS4_SYSV_ABI sceNetClearDnsCache() { int PS4_SYSV_ABI sceNetClearDnsCache() {
@ -465,9 +547,46 @@ int PS4_SYSV_ABI sceNetConfigWlanSetDeviceConfig() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceNetConnect() { int PS4_SYSV_ABI sceNetConnect(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); if (!g_isNetInitialized) {
return ORBIS_OK; return ORBIS_NET_ERROR_ENOTINIT;
}
int result;
int err;
int positiveErr;
do {
result = sys_connect(s, addr, addrlen);
if (result >= 0) {
return result; // Success
}
err = *Libraries::Kernel::__Error(); // Standard errno
// Convert to positive error for comparison
int positiveErr = (err < 0) ? -err : err;
if ((positiveErr & 0xfff0000) != 0) {
// Unknown/fatal error range
*sceNetErrnoLoc() = ORBIS_NET_ERETURN;
return -positiveErr;
}
// Retry if interrupted
} while (positiveErr == ORBIS_NET_EINTR);
if (positiveErr == ORBIS_NET_EADDRINUSE) {
result = -ORBIS_NET_EBADF;
} else if (positiveErr == ORBIS_NET_EALREADY) {
result = -ORBIS_NET_EINTR;
} else {
result = -positiveErr;
}
*sceNetErrnoLoc() = -result;
return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code
} }
int PS4_SYSV_ABI sceNetControl() { int PS4_SYSV_ABI sceNetControl() {
@ -640,8 +759,15 @@ int PS4_SYSV_ABI sceNetGetIfnameNumList() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceNetGetMacAddress() { int PS4_SYSV_ABI sceNetGetMacAddress(Libraries::NetCtl::OrbisNetEtherAddr* addr, int flags) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); if (addr == nullptr) {
LOG_ERROR(Lib_Net, "addr is null!");
return ORBIS_NET_EINVAL;
}
auto* netinfo = Common::Singleton<NetUtil::NetUtilInternal>::Instance();
netinfo->RetrieveEthernetAddr();
memcpy(addr->data, netinfo->GetEthernetAddr().data(), 6);
return ORBIS_OK; return ORBIS_OK;
} }
@ -655,9 +781,46 @@ int PS4_SYSV_ABI sceNetGetNameToIndex() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceNetGetpeername() { int PS4_SYSV_ABI sceNetGetpeername(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); if (!g_isNetInitialized) {
return ORBIS_OK; return ORBIS_NET_ERROR_ENOTINIT;
}
int result;
int err;
int positiveErr;
do {
result = sys_getpeername(s, addr, paddrlen);
if (result >= 0) {
return result; // Success
}
err = *Libraries::Kernel::__Error(); // Standard errno
// Convert to positive error for comparison
int positiveErr = (err < 0) ? -err : err;
if ((positiveErr & 0xfff0000) != 0) {
// Unknown/fatal error range
*sceNetErrnoLoc() = ORBIS_NET_ERETURN;
return -positiveErr;
}
// Retry if interrupted
} while (positiveErr == ORBIS_NET_EINTR);
if (positiveErr == ORBIS_NET_EADDRINUSE) {
result = -ORBIS_NET_EBADF;
} else if (positiveErr == ORBIS_NET_EALREADY) {
result = -ORBIS_NET_EINTR;
} else {
result = -positiveErr;
}
*sceNetErrnoLoc() = -result;
return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code
} }
int PS4_SYSV_ABI sceNetGetRandom() { int PS4_SYSV_ABI sceNetGetRandom() {
@ -681,13 +844,87 @@ int PS4_SYSV_ABI sceNetGetSockInfo6() {
} }
int PS4_SYSV_ABI sceNetGetsockname(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen) { int PS4_SYSV_ABI sceNetGetsockname(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); if (!g_isNetInitialized) {
return ORBIS_OK; return ORBIS_NET_ERROR_ENOTINIT;
}
int result;
int err;
int positiveErr;
do {
result = sys_getsockname(s, addr, paddrlen);
if (result >= 0) {
return result; // Success
}
err = *Libraries::Kernel::__Error(); // Standard errno
// Convert to positive error for comparison
int positiveErr = (err < 0) ? -err : err;
if ((positiveErr & 0xfff0000) != 0) {
// Unknown/fatal error range
*sceNetErrnoLoc() = ORBIS_NET_ERETURN;
return -positiveErr;
}
// Retry if interrupted
} while (positiveErr == ORBIS_NET_EINTR);
if (positiveErr == ORBIS_NET_EADDRINUSE) {
result = -ORBIS_NET_EBADF;
} else if (positiveErr == ORBIS_NET_EALREADY) {
result = -ORBIS_NET_EINTR;
} else {
result = -positiveErr;
}
*sceNetErrnoLoc() = -result;
return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code
} }
int PS4_SYSV_ABI sceNetGetsockopt(OrbisNetId s, int level, int optname, void* optval, u32* optlen) { int PS4_SYSV_ABI sceNetGetsockopt(OrbisNetId s, int level, int optname, void* optval, u32* optlen) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); if (!g_isNetInitialized) {
return ORBIS_OK; return ORBIS_NET_ERROR_ENOTINIT;
}
int result;
int err;
int positiveErr;
do {
result = sys_getsockopt(s, level, optname, optval, optlen);
if (result >= 0) {
return result; // Success
}
err = *Libraries::Kernel::__Error(); // Standard errno
// Convert to positive error for comparison
int positiveErr = (err < 0) ? -err : err;
if ((positiveErr & 0xfff0000) != 0) {
// Unknown/fatal error range
*sceNetErrnoLoc() = ORBIS_NET_ERETURN;
return -positiveErr;
}
// Retry if interrupted
} while (positiveErr == ORBIS_NET_EINTR);
if (positiveErr == ORBIS_NET_EADDRINUSE) {
result = -ORBIS_NET_EBADF;
} else if (positiveErr == ORBIS_NET_EALREADY) {
result = -ORBIS_NET_EINTR;
} else {
result = -positiveErr;
}
*sceNetErrnoLoc() = -result;
return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code
} }
int PS4_SYSV_ABI sceNetGetStatisticsInfo() { int PS4_SYSV_ABI sceNetGetStatisticsInfo() {
@ -781,9 +1018,46 @@ int PS4_SYSV_ABI sceNetIoctl() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceNetListen() { int PS4_SYSV_ABI sceNetListen(OrbisNetId s, int backlog) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); if (!g_isNetInitialized) {
return ORBIS_OK; return ORBIS_NET_ERROR_ENOTINIT;
}
int result;
int err;
int positiveErr;
do {
result = sys_listen(s, backlog);
if (result >= 0) {
return result; // Success
}
err = *Libraries::Kernel::__Error(); // Standard errno
// Convert to positive error for comparison
int positiveErr = (err < 0) ? -err : err;
if ((positiveErr & 0xfff0000) != 0) {
// Unknown/fatal error range
*sceNetErrnoLoc() = ORBIS_NET_ERETURN;
return -positiveErr;
}
// Retry if interrupted
} while (positiveErr == ORBIS_NET_EINTR);
if (positiveErr == ORBIS_NET_EADDRINUSE) {
result = -ORBIS_NET_EBADF;
} else if (positiveErr == ORBIS_NET_EALREADY) {
result = -ORBIS_NET_EINTR;
} else {
result = -positiveErr;
}
*sceNetErrnoLoc() = -result;
return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code
} }
int PS4_SYSV_ABI sceNetMemoryAllocate() { int PS4_SYSV_ABI sceNetMemoryAllocate() {
@ -829,20 +1103,131 @@ int PS4_SYSV_ABI sceNetPppoeStop() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceNetRecv() { int PS4_SYSV_ABI sceNetRecv(OrbisNetId s, void* buf, u64 len, int flags) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); if (!g_isNetInitialized) {
return ORBIS_OK; return ORBIS_NET_ERROR_ENOTINIT;
}
int result;
int err;
int positiveErr;
do {
result = sys_recvfrom(s, buf, len, flags | 0x40000000, nullptr, 0);
if (result >= 0) {
return result; // Success
}
err = *Libraries::Kernel::__Error(); // Standard errno
// Convert to positive error for comparison
int positiveErr = (err < 0) ? -err : err;
if ((positiveErr & 0xfff0000) != 0) {
// Unknown/fatal error range
*sceNetErrnoLoc() = ORBIS_NET_ERETURN;
return -positiveErr;
}
// Retry if interrupted
} while (positiveErr == ORBIS_NET_EINTR);
if (positiveErr == ORBIS_NET_EADDRINUSE) {
result = -ORBIS_NET_EBADF;
} else if (positiveErr == ORBIS_NET_EALREADY) {
result = -ORBIS_NET_EINTR;
} else {
result = -positiveErr;
}
*sceNetErrnoLoc() = -result;
return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code
} }
int PS4_SYSV_ABI sceNetRecvfrom(OrbisNetId s, void* buf, size_t len, int flags, int PS4_SYSV_ABI sceNetRecvfrom(OrbisNetId s, void* buf, u64 len, int flags, OrbisNetSockaddr* addr,
OrbisNetSockaddr* addr, u32* paddrlen) { u32* paddrlen) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); if (!g_isNetInitialized) {
return ORBIS_OK; return ORBIS_NET_ERROR_ENOTINIT;
}
int result;
int err;
int positiveErr;
do {
result = sys_recvfrom(s, buf, len, flags | 0x40000000, addr, paddrlen);
if (result >= 0) {
return result; // Success
}
err = *Libraries::Kernel::__Error(); // Standard errno
// Convert to positive error for comparison
int positiveErr = (err < 0) ? -err : err;
if ((positiveErr & 0xfff0000) != 0) {
// Unknown/fatal error range
*sceNetErrnoLoc() = ORBIS_NET_ERETURN;
return -positiveErr;
}
// Retry if interrupted
} while (positiveErr == ORBIS_NET_EINTR);
if (positiveErr == ORBIS_NET_EADDRINUSE) {
result = -ORBIS_NET_EBADF;
} else if (positiveErr == ORBIS_NET_EALREADY) {
result = -ORBIS_NET_EINTR;
} else {
result = -positiveErr;
}
*sceNetErrnoLoc() = -result;
return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code
} }
int PS4_SYSV_ABI sceNetRecvmsg() { int PS4_SYSV_ABI sceNetRecvmsg(OrbisNetId s, OrbisNetMsghdr* msg, int flags) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); if (!g_isNetInitialized) {
return ORBIS_OK; return ORBIS_NET_ERROR_ENOTINIT;
}
int result;
int err;
int positiveErr;
do {
result = sys_recvmsg(s, msg, flags | 0x40000000);
if (result >= 0) {
return result; // Success
}
err = *Libraries::Kernel::__Error(); // Standard errno
// Convert to positive error for comparison
int positiveErr = (err < 0) ? -err : err;
if ((positiveErr & 0xfff0000) != 0) {
// Unknown/fatal error range
*sceNetErrnoLoc() = ORBIS_NET_ERETURN;
return -positiveErr;
}
// Retry if interrupted
} while (positiveErr == ORBIS_NET_EINTR);
if (positiveErr == ORBIS_NET_EADDRINUSE) {
result = -ORBIS_NET_EBADF;
} else if (positiveErr == ORBIS_NET_EALREADY) {
result = -ORBIS_NET_EINTR;
} else {
result = -positiveErr;
}
*sceNetErrnoLoc() = -result;
return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code
} }
int PS4_SYSV_ABI sceNetResolverAbort() { int PS4_SYSV_ABI sceNetResolverAbort() {
@ -915,19 +1300,131 @@ int PS4_SYSV_ABI sceNetResolverStartNtoaMultipleRecordsEx() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceNetSend() { int PS4_SYSV_ABI sceNetSend(OrbisNetId s, const void* buf, u64 len, int flags) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); if (!g_isNetInitialized) {
return ORBIS_OK; return ORBIS_NET_ERROR_ENOTINIT;
}
int result;
int err;
int positiveErr;
do {
result = sys_sendto(s, buf, len, flags | 0x40020000, nullptr, 0);
if (result >= 0) {
return result; // Success
}
err = *Libraries::Kernel::__Error(); // Standard errno
// Convert to positive error for comparison
int positiveErr = (err < 0) ? -err : err;
if ((positiveErr & 0xfff0000) != 0) {
// Unknown/fatal error range
*sceNetErrnoLoc() = ORBIS_NET_ERETURN;
return -positiveErr;
}
// Retry if interrupted
} while (positiveErr == ORBIS_NET_EINTR);
if (positiveErr == ORBIS_NET_EADDRINUSE) {
result = -ORBIS_NET_EBADF;
} else if (positiveErr == ORBIS_NET_EALREADY) {
result = -ORBIS_NET_EINTR;
} else {
result = -positiveErr;
}
*sceNetErrnoLoc() = -result;
return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code
} }
int PS4_SYSV_ABI sceNetSendmsg() { int PS4_SYSV_ABI sceNetSendmsg(OrbisNetId s, const OrbisNetMsghdr* msg, int flags) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); if (!g_isNetInitialized) {
return ORBIS_OK; return ORBIS_NET_ERROR_ENOTINIT;
}
int result;
int err;
int positiveErr;
do {
result = sys_sendmsg(s, msg, flags | 0x40020000);
if (result >= 0) {
return result; // Success
}
err = *Libraries::Kernel::__Error(); // Standard errno
// Convert to positive error for comparison
int positiveErr = (err < 0) ? -err : err;
if ((positiveErr & 0xfff0000) != 0) {
// Unknown/fatal error range
*sceNetErrnoLoc() = ORBIS_NET_ERETURN;
return -positiveErr;
}
// Retry if interrupted
} while (positiveErr == ORBIS_NET_EINTR);
if (positiveErr == ORBIS_NET_EADDRINUSE) {
result = -ORBIS_NET_EBADF;
} else if (positiveErr == ORBIS_NET_EALREADY) {
result = -ORBIS_NET_EINTR;
} else {
result = -positiveErr;
}
*sceNetErrnoLoc() = -result;
return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code
} }
int PS4_SYSV_ABI sceNetSendto() { int PS4_SYSV_ABI sceNetSendto(OrbisNetId s, const void* buf, u64 len, int flags,
LOG_ERROR(Lib_Net, "(STUBBED) called"); const OrbisNetSockaddr* addr, u32 addrlen) {
return ORBIS_OK; if (!g_isNetInitialized) {
return ORBIS_NET_ERROR_ENOTINIT;
}
int result;
int err;
int positiveErr;
do {
result = sys_sendto(s, buf, len, flags | 0x40020000, addr, addrlen);
if (result >= 0) {
return result; // Success
}
err = *Libraries::Kernel::__Error(); // Standard errno
// Convert to positive error for comparison
int positiveErr = (err < 0) ? -err : err;
if ((positiveErr & 0xfff0000) != 0) {
// Unknown/fatal error range
*sceNetErrnoLoc() = ORBIS_NET_ERETURN;
return -positiveErr;
}
// Retry if interrupted
} while (positiveErr == ORBIS_NET_EINTR);
if (positiveErr == ORBIS_NET_EADDRINUSE) {
result = -ORBIS_NET_EBADF;
} else if (positiveErr == ORBIS_NET_EALREADY) {
result = -ORBIS_NET_EINTR;
} else {
result = -positiveErr;
}
*sceNetErrnoLoc() = -result;
return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code
} }
int PS4_SYSV_ABI sceNetSetDns6Info() { int PS4_SYSV_ABI sceNetSetDns6Info() {
@ -950,9 +1447,47 @@ int PS4_SYSV_ABI sceNetSetDnsInfoToKernel() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceNetSetsockopt() { int PS4_SYSV_ABI sceNetSetsockopt(OrbisNetId s, int level, int optname, const void* optval,
LOG_ERROR(Lib_Net, "(STUBBED) called"); u32 optlen) {
return ORBIS_OK; if (!g_isNetInitialized) {
return ORBIS_NET_ERROR_ENOTINIT;
}
int result;
int err;
int positiveErr;
do {
result = sys_setsockopt(s, level, optname, optval, optlen);
if (result >= 0) {
return result; // Success
}
err = *Libraries::Kernel::__Error(); // Standard errno
// Convert to positive error for comparison
int positiveErr = (err < 0) ? -err : err;
if ((positiveErr & 0xfff0000) != 0) {
// Unknown/fatal error range
*sceNetErrnoLoc() = ORBIS_NET_ERETURN;
return -positiveErr;
}
// Retry if interrupted
} while (positiveErr == ORBIS_NET_EINTR);
if (positiveErr == ORBIS_NET_EADDRINUSE) {
result = -ORBIS_NET_EBADF;
} else if (positiveErr == ORBIS_NET_EALREADY) {
result = -ORBIS_NET_EINTR;
} else {
result = -positiveErr;
}
*sceNetErrnoLoc() = -result;
return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code
} }
int PS4_SYSV_ABI sceNetShowIfconfig() { int PS4_SYSV_ABI sceNetShowIfconfig() {
@ -1035,24 +1570,172 @@ int PS4_SYSV_ABI sceNetShowRouteWithMemory() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceNetShutdown() { int PS4_SYSV_ABI sceNetShutdown(OrbisNetId s, int how) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); if (!g_isNetInitialized) {
return ORBIS_OK; return ORBIS_NET_ERROR_ENOTINIT;
}
int result;
int err;
int positiveErr;
do {
result = sys_shutdown(s, how);
if (result >= 0) {
return result; // Success
}
err = *Libraries::Kernel::__Error(); // Standard errno
// Convert to positive error for comparison
int positiveErr = (err < 0) ? -err : err;
if ((positiveErr & 0xfff0000) != 0) {
// Unknown/fatal error range
*sceNetErrnoLoc() = ORBIS_NET_ERETURN;
return -positiveErr;
}
// Retry if interrupted
} while (positiveErr == ORBIS_NET_EINTR);
if (positiveErr == ORBIS_NET_EADDRINUSE) {
result = -ORBIS_NET_EBADF;
} else if (positiveErr == ORBIS_NET_EALREADY) {
result = -ORBIS_NET_EINTR;
} else {
result = -positiveErr;
}
*sceNetErrnoLoc() = -result;
return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code
} }
int PS4_SYSV_ABI sceNetSocket(const char* name, int family, int type, int protocol) { OrbisNetId PS4_SYSV_ABI sceNetSocket(const char* name, int family, int type, int protocol) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); if (!g_isNetInitialized) {
return ORBIS_OK; return ORBIS_NET_ERROR_ENOTINIT;
}
int result;
int err;
int positiveErr;
do {
result = sys_socketex(name, family, type, protocol);
if (result >= 0) {
return result; // Success
}
err = *Libraries::Kernel::__Error(); // Standard errno
// Convert to positive error for comparison
int positiveErr = (err < 0) ? -err : err;
if ((positiveErr & 0xfff0000) != 0) {
// Unknown/fatal error range
*sceNetErrnoLoc() = ORBIS_NET_ERETURN;
return -positiveErr;
}
// Retry if interrupted
} while (positiveErr == ORBIS_NET_EINTR);
if (positiveErr == ORBIS_NET_EADDRINUSE) {
result = -ORBIS_NET_EBADF;
} else if (positiveErr == ORBIS_NET_EALREADY) {
result = -ORBIS_NET_EINTR;
} else {
result = -positiveErr;
}
*sceNetErrnoLoc() = -result;
return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code
} }
int PS4_SYSV_ABI sceNetSocketAbort() { int PS4_SYSV_ABI sceNetSocketAbort(OrbisNetId s, int flags) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); if (!g_isNetInitialized) {
return ORBIS_OK; return ORBIS_NET_ERROR_ENOTINIT;
}
int result;
int err;
int positiveErr;
do {
result = sys_netabort(s, flags);
if (result >= 0) {
return result; // Success
}
err = *Libraries::Kernel::__Error(); // Standard errno
// Convert to positive error for comparison
int positiveErr = (err < 0) ? -err : err;
if ((positiveErr & 0xfff0000) != 0) {
// Unknown/fatal error range
*sceNetErrnoLoc() = ORBIS_NET_ERETURN;
return -positiveErr;
}
// Retry if interrupted
} while (positiveErr == ORBIS_NET_EINTR);
if (positiveErr == ORBIS_NET_EADDRINUSE) {
result = -ORBIS_NET_EBADF;
} else if (positiveErr == ORBIS_NET_EALREADY) {
result = -ORBIS_NET_EINTR;
} else {
result = -positiveErr;
}
*sceNetErrnoLoc() = -result;
return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code
} }
int PS4_SYSV_ABI sceNetSocketClose() { int PS4_SYSV_ABI sceNetSocketClose(OrbisNetId s) {
LOG_ERROR(Lib_Net, "(STUBBED) called"); if (!g_isNetInitialized) {
return ORBIS_OK; return ORBIS_NET_ERROR_ENOTINIT;
}
int result;
int err;
int positiveErr;
do {
result = sys_socketclose(s);
if (result >= 0) {
return result; // Success
}
err = *Libraries::Kernel::__Error(); // Standard errno
// Convert to positive error for comparison
int positiveErr = (err < 0) ? -err : err;
if ((positiveErr & 0xfff0000) != 0) {
// Unknown/fatal error range
*sceNetErrnoLoc() = ORBIS_NET_ERETURN;
return -positiveErr;
}
// Retry if interrupted
} while (positiveErr == ORBIS_NET_EINTR);
if (positiveErr == ORBIS_NET_EADDRINUSE) {
result = -ORBIS_NET_EBADF;
} else if (positiveErr == ORBIS_NET_EALREADY) {
result = -ORBIS_NET_EINTR;
} else {
result = -positiveErr;
}
*sceNetErrnoLoc() = -result;
return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code
} }
int PS4_SYSV_ABI sceNetSyncCreate() { int PS4_SYSV_ABI sceNetSyncCreate() {

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include "common/types.h" #include "common/types.h"
#include "netctl.h"
namespace Core::Loader { namespace Core::Loader {
class SymbolsResolver; class SymbolsResolver;
@ -19,6 +20,63 @@ class SymbolsResolver;
namespace Libraries::Net { namespace Libraries::Net {
enum OrbisNetSocketType : u32 {
ORBIS_NET_SOCK_STREAM = 1,
ORBIS_NET_SOCK_DGRAM = 2,
ORBIS_NET_SOCK_RAW = 3,
ORBIS_NET_SOCK_DGRAM_P2P = 6,
ORBIS_NET_SOCK_STREAM_P2P = 10
};
enum OrbisNetProtocol : u32 {
ORBIS_NET_IPPROTO_IP = 0,
ORBIS_NET_IPPROTO_ICMP = 1,
ORBIS_NET_IPPROTO_IGMP = 2,
ORBIS_NET_IPPROTO_TCP = 6,
ORBIS_NET_IPPROTO_UDP = 17,
ORBIS_NET_SOL_SOCKET = 0xFFFF
};
enum OrbisNetSocketOption : u32 {
/* IP */
ORBIS_NET_IP_HDRINCL = 2,
ORBIS_NET_IP_TOS = 3,
ORBIS_NET_IP_TTL = 4,
ORBIS_NET_IP_MULTICAST_IF = 9,
ORBIS_NET_IP_MULTICAST_TTL = 10,
ORBIS_NET_IP_MULTICAST_LOOP = 11,
ORBIS_NET_IP_ADD_MEMBERSHIP = 12,
ORBIS_NET_IP_DROP_MEMBERSHIP = 13,
ORBIS_NET_IP_TTLCHK = 23,
ORBIS_NET_IP_MAXTTL = 24,
/* TCP */
ORBIS_NET_TCP_NODELAY = 1,
ORBIS_NET_TCP_MAXSEG = 2,
ORBIS_NET_TCP_MSS_TO_ADVERTISE = 3,
/* SOCKET */
ORBIS_NET_SO_REUSEADDR = 0x00000004,
ORBIS_NET_SO_KEEPALIVE = 0x00000008,
ORBIS_NET_SO_BROADCAST = 0x00000020,
ORBIS_NET_SO_LINGER = 0x00000080,
ORBIS_NET_SO_REUSEPORT = 0x00000200,
ORBIS_NET_SO_ONESBCAST = 0x00010000,
ORBIS_NET_SO_USECRYPTO = 0x00020000,
ORBIS_NET_SO_USESIGNATURE = 0x00040000,
ORBIS_NET_SO_SNDBUF = 0x1001,
ORBIS_NET_SO_RCVBUF = 0x1002,
ORBIS_NET_SO_ERROR = 0x1007,
ORBIS_NET_SO_TYPE = 0x1008,
ORBIS_NET_SO_SNDTIMEO = 0x1105,
ORBIS_NET_SO_RCVTIMEO = 0x1106,
ORBIS_NET_SO_ERROR_EX = 0x1107,
ORBIS_NET_SO_ACCEPTTIMEO = 0x1108,
ORBIS_NET_SO_CONNECTTIMEO = 0x1109,
ORBIS_NET_SO_NBIO = 0x1200,
ORBIS_NET_SO_POLICY = 0x1201,
ORBIS_NET_SO_NAME = 0x1202,
ORBIS_NET_SO_PRIORITY = 0x1203
};
using OrbisNetId = s32; using OrbisNetId = s32;
struct OrbisNetSockaddr { struct OrbisNetSockaddr {
@ -27,6 +85,30 @@ struct OrbisNetSockaddr {
char sa_data[14]; char sa_data[14];
}; };
struct OrbisNetSockaddrIn {
u8 sin_len;
u8 sin_family;
u16 sin_port;
u32 sin_addr;
u16 sin_vport;
char sin_zero[6];
};
struct OrbisNetIovec {
void* iov_base;
u64 iov_len;
};
struct OrbisNetMsghdr {
void* msg_name;
u32 msg_namelen;
OrbisNetIovec* msg_iov;
int msg_iovlen;
void* msg_control;
u32 msg_controllen;
int msg_flags;
};
int PS4_SYSV_ABI in6addr_any(); int PS4_SYSV_ABI in6addr_any();
int PS4_SYSV_ABI in6addr_loopback(); int PS4_SYSV_ABI in6addr_loopback();
int PS4_SYSV_ABI sce_net_dummy(); int PS4_SYSV_ABI sce_net_dummy();
@ -116,7 +198,7 @@ int PS4_SYSV_ABI sceNetConfigWlanInfraLeave();
int PS4_SYSV_ABI sceNetConfigWlanInfraScanJoin(); int PS4_SYSV_ABI sceNetConfigWlanInfraScanJoin();
int PS4_SYSV_ABI sceNetConfigWlanScan(); int PS4_SYSV_ABI sceNetConfigWlanScan();
int PS4_SYSV_ABI sceNetConfigWlanSetDeviceConfig(); int PS4_SYSV_ABI sceNetConfigWlanSetDeviceConfig();
int PS4_SYSV_ABI sceNetConnect(); int PS4_SYSV_ABI sceNetConnect(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen);
int PS4_SYSV_ABI sceNetControl(); int PS4_SYSV_ABI sceNetControl();
int PS4_SYSV_ABI sceNetDhcpdStart(); int PS4_SYSV_ABI sceNetDhcpdStart();
int PS4_SYSV_ABI sceNetDhcpdStop(); int PS4_SYSV_ABI sceNetDhcpdStop();
@ -151,10 +233,10 @@ int PS4_SYSV_ABI sceNetGetIfList();
int PS4_SYSV_ABI sceNetGetIfListOnce(); int PS4_SYSV_ABI sceNetGetIfListOnce();
int PS4_SYSV_ABI sceNetGetIfName(); int PS4_SYSV_ABI sceNetGetIfName();
int PS4_SYSV_ABI sceNetGetIfnameNumList(); int PS4_SYSV_ABI sceNetGetIfnameNumList();
int PS4_SYSV_ABI sceNetGetMacAddress(); int PS4_SYSV_ABI sceNetGetMacAddress(Libraries::NetCtl::OrbisNetEtherAddr* addr, int flags);
int PS4_SYSV_ABI sceNetGetMemoryPoolStats(); int PS4_SYSV_ABI sceNetGetMemoryPoolStats();
int PS4_SYSV_ABI sceNetGetNameToIndex(); int PS4_SYSV_ABI sceNetGetNameToIndex();
int PS4_SYSV_ABI sceNetGetpeername(); int PS4_SYSV_ABI sceNetGetpeername(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen);
int PS4_SYSV_ABI sceNetGetRandom(); int PS4_SYSV_ABI sceNetGetRandom();
int PS4_SYSV_ABI sceNetGetRouteInfo(); int PS4_SYSV_ABI sceNetGetRouteInfo();
int PS4_SYSV_ABI sceNetGetSockInfo(); int PS4_SYSV_ABI sceNetGetSockInfo();
@ -177,7 +259,7 @@ int PS4_SYSV_ABI sceNetInfoDumpStop();
int PS4_SYSV_ABI sceNetInit(); int PS4_SYSV_ABI sceNetInit();
int PS4_SYSV_ABI sceNetInitParam(); int PS4_SYSV_ABI sceNetInitParam();
int PS4_SYSV_ABI sceNetIoctl(); int PS4_SYSV_ABI sceNetIoctl();
int PS4_SYSV_ABI sceNetListen(); int PS4_SYSV_ABI sceNetListen(OrbisNetId s, int backlog);
int PS4_SYSV_ABI sceNetMemoryAllocate(); int PS4_SYSV_ABI sceNetMemoryAllocate();
int PS4_SYSV_ABI sceNetMemoryFree(); int PS4_SYSV_ABI sceNetMemoryFree();
u32 PS4_SYSV_ABI sceNetNtohl(u32 net32); u32 PS4_SYSV_ABI sceNetNtohl(u32 net32);
@ -187,10 +269,10 @@ int PS4_SYSV_ABI sceNetPoolCreate(const char* name, int size, int flags);
int PS4_SYSV_ABI sceNetPoolDestroy(); int PS4_SYSV_ABI sceNetPoolDestroy();
int PS4_SYSV_ABI sceNetPppoeStart(); int PS4_SYSV_ABI sceNetPppoeStart();
int PS4_SYSV_ABI sceNetPppoeStop(); int PS4_SYSV_ABI sceNetPppoeStop();
int PS4_SYSV_ABI sceNetRecv(); int PS4_SYSV_ABI sceNetRecv(OrbisNetId s, void* buf, u64 len, int flags);
int PS4_SYSV_ABI sceNetRecvfrom(OrbisNetId s, void* buf, size_t len, int flags, int PS4_SYSV_ABI sceNetRecvfrom(OrbisNetId s, void* buf, u64 len, int flags, OrbisNetSockaddr* addr,
OrbisNetSockaddr* addr, u32* paddrlen); u32* paddrlen);
int PS4_SYSV_ABI sceNetRecvmsg(); int PS4_SYSV_ABI sceNetRecvmsg(OrbisNetId s, OrbisNetMsghdr* msg, int flags);
int PS4_SYSV_ABI sceNetResolverAbort(); int PS4_SYSV_ABI sceNetResolverAbort();
int PS4_SYSV_ABI sceNetResolverConnect(); int PS4_SYSV_ABI sceNetResolverConnect();
int PS4_SYSV_ABI sceNetResolverConnectAbort(); int PS4_SYSV_ABI sceNetResolverConnectAbort();
@ -205,14 +287,16 @@ int PS4_SYSV_ABI sceNetResolverStartNtoa();
int PS4_SYSV_ABI sceNetResolverStartNtoa6(); int PS4_SYSV_ABI sceNetResolverStartNtoa6();
int PS4_SYSV_ABI sceNetResolverStartNtoaMultipleRecords(); int PS4_SYSV_ABI sceNetResolverStartNtoaMultipleRecords();
int PS4_SYSV_ABI sceNetResolverStartNtoaMultipleRecordsEx(); int PS4_SYSV_ABI sceNetResolverStartNtoaMultipleRecordsEx();
int PS4_SYSV_ABI sceNetSend(); int PS4_SYSV_ABI sceNetSend(OrbisNetId s, const void* buf, u64 len, int flags);
int PS4_SYSV_ABI sceNetSendmsg(); int PS4_SYSV_ABI sceNetSendmsg(OrbisNetId s, const OrbisNetMsghdr* msg, int flags);
int PS4_SYSV_ABI sceNetSendto(); int PS4_SYSV_ABI sceNetSendto(OrbisNetId s, const void* buf, u64 len, int flags,
const OrbisNetSockaddr* addr, u32 addrlen);
int PS4_SYSV_ABI sceNetSetDns6Info(); int PS4_SYSV_ABI sceNetSetDns6Info();
int PS4_SYSV_ABI sceNetSetDns6InfoToKernel(); int PS4_SYSV_ABI sceNetSetDns6InfoToKernel();
int PS4_SYSV_ABI sceNetSetDnsInfo(); int PS4_SYSV_ABI sceNetSetDnsInfo();
int PS4_SYSV_ABI sceNetSetDnsInfoToKernel(); int PS4_SYSV_ABI sceNetSetDnsInfoToKernel();
int PS4_SYSV_ABI sceNetSetsockopt(); int PS4_SYSV_ABI sceNetSetsockopt(OrbisNetId s, int level, int optname, const void* optval,
u32 optlen);
int PS4_SYSV_ABI sceNetShowIfconfig(); int PS4_SYSV_ABI sceNetShowIfconfig();
int PS4_SYSV_ABI sceNetShowIfconfigForBuffer(); int PS4_SYSV_ABI sceNetShowIfconfigForBuffer();
int PS4_SYSV_ABI sceNetShowIfconfigWithMemory(); int PS4_SYSV_ABI sceNetShowIfconfigWithMemory();
@ -229,10 +313,10 @@ int PS4_SYSV_ABI sceNetShowRoute6ForBuffer();
int PS4_SYSV_ABI sceNetShowRoute6WithMemory(); int PS4_SYSV_ABI sceNetShowRoute6WithMemory();
int PS4_SYSV_ABI sceNetShowRouteForBuffer(); int PS4_SYSV_ABI sceNetShowRouteForBuffer();
int PS4_SYSV_ABI sceNetShowRouteWithMemory(); int PS4_SYSV_ABI sceNetShowRouteWithMemory();
int PS4_SYSV_ABI sceNetShutdown(); int PS4_SYSV_ABI sceNetShutdown(OrbisNetId s, int how);
int PS4_SYSV_ABI sceNetSocket(const char* name, int family, int type, int protocol); OrbisNetId PS4_SYSV_ABI sceNetSocket(const char* name, int family, int type, int protocol);
int PS4_SYSV_ABI sceNetSocketAbort(); int PS4_SYSV_ABI sceNetSocketAbort(OrbisNetId s, int flags);
int PS4_SYSV_ABI sceNetSocketClose(); int PS4_SYSV_ABI sceNetSocketClose(OrbisNetId s);
int PS4_SYSV_ABI sceNetSyncCreate(); int PS4_SYSV_ABI sceNetSyncCreate();
int PS4_SYSV_ABI sceNetSyncDestroy(); int PS4_SYSV_ABI sceNetSyncDestroy();
int PS4_SYSV_ABI sceNetSyncGet(); int PS4_SYSV_ABI sceNetSyncGet();

View File

@ -0,0 +1,162 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
// net errno codes
constexpr int ORBIS_NET_EPERM = 1;
constexpr int ORBIS_NET_ENOENT = 2;
constexpr int ORBIS_NET_EINTR = 4;
constexpr int ORBIS_NET_EBADF = 9;
constexpr int ORBIS_NET_EACCES = 13;
constexpr int ORBIS_NET_EFAULT = 14;
constexpr int ORBIS_NET_ENOTBLK = 15;
constexpr int ORBIS_NET_EBUSY = 16;
constexpr int ORBIS_NET_EEXIST = 17;
constexpr int ORBIS_NET_ENODEV = 19;
constexpr int ORBIS_NET_EINVAL = 22;
constexpr int ORBIS_NET_EMFILE = 24;
constexpr int ORBIS_NET_ENOSPC = 28;
constexpr int ORBIS_NET_EPIPE = 32;
constexpr int ORBIS_NET_EAGAIN = 35;
constexpr int ORBIS_NET_EWOULDBLOCK = 35;
constexpr int ORBIS_NET_EINPROGRESS = 36;
constexpr int ORBIS_NET_EALREADY = 37;
constexpr int ORBIS_NET_ENOTSOCK = 38;
constexpr int ORBIS_NET_EDESTADDRREQ = 39;
constexpr int ORBIS_NET_EMSGSIZE = 40;
constexpr int ORBIS_NET_EPROTOTYPE = 41;
constexpr int ORBIS_NET_ENOPROTOOPT = 42;
constexpr int ORBIS_NET_EPROTONOSUPPORT = 43;
constexpr int ORBIS_NET_EOPNOTSUPP = 45;
constexpr int ORBIS_NET_EAFNOSUPPORT = 47;
constexpr int ORBIS_NET_EADDRINUSE = 48;
constexpr int ORBIS_NET_EADDRNOTAVAIL = 49;
constexpr int ORBIS_NET_ENETDOWN = 50;
constexpr int ORBIS_NET_ENETUNREACH = 51;
constexpr int ORBIS_NET_ENETRESET = 52;
constexpr int ORBIS_NET_ECONNABORTED = 53;
constexpr int ORBIS_NET_ECONNRESET = 54;
constexpr int ORBIS_NET_EISCONN = 56;
constexpr int ORBIS_NET_ENOTCONN = 57;
constexpr int ORBIS_NET_ETOOMANYREFS = 59;
constexpr int ORBIS_NET_ETIMEDOUT = 60;
constexpr int ORBIS_NET_ECONNREFUSED = 61;
constexpr int ORBIS_NET_ELOOP = 62;
constexpr int ORBIS_NET_ENAMETOOLONG = 63;
constexpr int ORBIS_NET_EHOSTDOWN = 64;
constexpr int ORBIS_NET_EHOSTUNREACH = 65;
constexpr int ORBIS_NET_ENOTEMPTY = 66;
constexpr int ORBIS_NET_EPROCUNAVAIL = 76;
constexpr int ORBIS_NET_EPROTO = 92;
constexpr int ORBIS_NET_EADHOC = 160;
constexpr int ORBIS_NET_EINACTIVEDISABLED = 163;
constexpr int ORBIS_NET_ENODATA = 164;
constexpr int ORBIS_NET_EDESC = 165;
constexpr int ORBIS_NET_EDESCTIMEDOUT = 166;
constexpr int ORBIS_NET_ENOTINIT = 200;
constexpr int ORBIS_NET_ENOLIBMEM = 201;
constexpr int ORBIS_NET_ECALLBACK = 203;
constexpr int ORBIS_NET_EINTERNAL = 204;
constexpr int ORBIS_NET_ERETURN = 205;
constexpr int ORBIS_NET_ENOALLOCMEM = 206;
// errno for dns resolver
constexpr int ORBIS_NET_RESOLVER_EINTERNAL = 220;
constexpr int ORBIS_NET_RESOLVER_EBUSY = 221;
constexpr int ORBIS_NET_RESOLVER_ENOSPACE = 222;
constexpr int ORBIS_NET_RESOLVER_EPACKET = 223;
constexpr int ORBIS_NET_RESOLVER_ENODNS = 225;
constexpr int ORBIS_NET_RESOLVER_ETIMEDOUT = 226;
constexpr int ORBIS_NET_RESOLVER_ENOSUPPORT = 227;
constexpr int ORBIS_NET_RESOLVER_EFORMAT = 228;
constexpr int ORBIS_NET_RESOLVER_ESERVERFAILURE = 229;
constexpr int ORBIS_NET_RESOLVER_ENOHOST = 230;
constexpr int ORBIS_NET_RESOLVER_ENOTIMPLEMENTED = 231;
constexpr int ORBIS_NET_RESOLVER_ESERVERREFUSED = 232;
constexpr int ORBIS_NET_RESOLVER_ENORECORD = 233;
constexpr int ORBIS_NET_RESOLVER_EALIGNMENT = 234;
// common errno
constexpr int ORBIS_NET_ENOMEM = 12;
constexpr int ORBIS_NET_ENOBUFS = 55;
// error codes
constexpr int ORBIS_NET_ERROR_BASE = 0x80410100; // not existed used for calculation
constexpr int ORBIS_NET_ERROR_EPERM = 0x80410101;
constexpr int ORBIS_NET_ERROR_ENOENT = 0x80410102;
constexpr int ORBIS_NET_ERROR_EINTR = 0x80410104;
constexpr int ORBIS_NET_ERROR_EBADF = 0x80410109;
constexpr int ORBIS_NET_ERROR_ENOMEM = 0x8041010c;
constexpr int ORBIS_NET_ERROR_EACCES = 0x8041010d;
constexpr int ORBIS_NET_ERROR_EFAULT = 0x8041010e;
constexpr int ORBIS_NET_ERROR_ENOTBLK = 0x8041010f;
constexpr int ORBIS_NET_ERROR_EEXIST = 0x80410111;
constexpr int ORBIS_NET_ERROR_ENODEV = 0x80410113;
constexpr int ORBIS_NET_ERROR_EINVAL = 0x80410116;
constexpr int ORBIS_NET_ERROR_ENFILE = 0x80410117;
constexpr int ORBIS_NET_ERROR_EMFILE = 0x80410118;
constexpr int ORBIS_NET_ERROR_ENOSPC = 0x8041011c;
constexpr int ORBIS_NET_ERROR_EPIPE = 0x80410120;
constexpr int ORBIS_NET_ERROR_EAGAIN = 0x80410123;
constexpr int ORBIS_NET_ERROR_EWOULDBLOCK = 0x80410123;
constexpr int ORBIS_NET_ERROR_EINPROGRESS = 0x80410124;
constexpr int ORBIS_NET_ERROR_EALREADY = 0x80410125;
constexpr int ORBIS_NET_ERROR_ENOTSOCK = 0x80410126;
constexpr int ORBIS_NET_ERROR_EDESTADDRREQ = 0x80410127;
constexpr int ORBIS_NET_ERROR_EMSGSIZE = 0x80410128;
constexpr int ORBIS_NET_ERROR_EPROTOTYPE = 0x80410129;
constexpr int ORBIS_NET_ERROR_ENOPROTOOPT = 0x8041012a;
constexpr int ORBIS_NET_ERROR_EPROTONOSUPPORT = 0x8041012b;
constexpr int ORBIS_NET_ERROR_EOPNOTSUPP = 0x8041012d;
constexpr int ORBIS_NET_ERROR_EPFNOSUPPORT = 0x8041012e;
constexpr int ORBIS_NET_ERROR_EAFNOSUPPORT = 0x8041012f;
constexpr int ORBIS_NET_ERROR_EADDRINUSE = 0x80410130;
constexpr int ORBIS_NET_ERROR_EADDRNOTAVAIL = 0x80410131;
constexpr int ORBIS_NET_ERROR_ENETDOWN = 0x80410132;
constexpr int ORBIS_NET_ERROR_ENETUNREACH = 0x80410133;
constexpr int ORBIS_NET_ERROR_ENETRESET = 0x80410134;
constexpr int ORBIS_NET_ERROR_ECONNABORTED = 0x80410135;
constexpr int ORBIS_NET_ERROR_ECONNRESET = 0x80410136;
constexpr int ORBIS_NET_ERROR_ENOBUFS = 0x80410137;
constexpr int ORBIS_NET_ERROR_EISCONN = 0x80410138;
constexpr int ORBIS_NET_ERROR_ENOTCONN = 0x80410139;
constexpr int ORBIS_NET_ERROR_ESHUTDOWN = 0x8041013a;
constexpr int ORBIS_NET_ERROR_ETOOMANYREFS = 0x8041013b;
constexpr int ORBIS_NET_ERROR_ETIMEDOUT = 0x8041013c;
constexpr int ORBIS_NET_ERROR_ECONNREFUSED = 0x8041013d;
constexpr int ORBIS_NET_ERROR_ELOOP = 0x8041013e;
constexpr int ORBIS_NET_ERROR_ENAMETOOLONG = 0x8041013f;
constexpr int ORBIS_NET_ERROR_EHOSTDOWN = 0x80410140;
constexpr int ORBIS_NET_ERROR_EHOSTUNREACH = 0x80410141;
constexpr int ORBIS_NET_ERROR_ENOTEMPTY = 0x80410142;
constexpr int ORBIS_NET_ERROR_EPROCUNAVAIL = 0x8041014C;
constexpr int ORBIS_NET_ERROR_ECANCELED = 0x80410157;
constexpr int ORBIS_NET_ERROR_EPROTO = 0x8041015C;
constexpr int ORBIS_NET_ERROR_EADHOC = 0x804101a0;
constexpr int ORBIS_NET_ERROR_ERESERVED161 = 0x804101a1;
constexpr int ORBIS_NET_ERROR_ERESERVED162 = 0x804101a2;
constexpr int ORBIS_NET_ERROR_EINACTIVEDISABLED = 0x804101a3;
constexpr int ORBIS_NET_ERROR_ENODATA = 0x804101a4;
constexpr int ORBIS_NET_ERROR_EDESC = 0x804101a5;
constexpr int ORBIS_NET_ERROR_EDESCTIMEDOUT = 0x804101a6;
constexpr int ORBIS_NET_ERROR_ENOTINIT = 0x804101c8;
constexpr int ORBIS_NET_ERROR_ENOLIBMEM = 0x804101c9;
constexpr int ORBIS_NET_ERROR_ECALLBACK = 0x804101cb;
constexpr int ORBIS_NET_ERROR_EINTERNAL = 0x804101cc;
constexpr int ORBIS_NET_ERROR_ERETURN = 0x804101cd;
constexpr int ORBIS_NET_ERROR_ENOALLOCMEM = 0x804101ce;
constexpr int ORBIS_NET_ERROR_RESOLVER_EINTERNAL = 0x804101dc;
constexpr int ORBIS_NET_ERROR_RESOLVER_EBUSY = 0x804101dd;
constexpr int ORBIS_NET_ERROR_RESOLVER_ENOSPACE = 0x804101de;
constexpr int ORBIS_NET_ERROR_RESOLVER_EPACKET = 0x804101df;
constexpr int ORBIS_NET_ERROR_RESOLVER_ENODNS = 0x804101e1;
constexpr int ORBIS_NET_ERROR_RESOLVER_ETIMEDOUT = 0x804101e2;
constexpr int ORBIS_NET_ERROR_RESOLVER_ENOSUPPORT = 0x804101e3;
constexpr int ORBIS_NET_ERROR_RESOLVER_EFORMAT = 0x804101e4;
constexpr int ORBIS_NET_ERROR_RESOLVER_ESERVERFAILURE = 0x804101e5;
constexpr int ORBIS_NET_ERROR_RESOLVER_ENOHOST = 0x804101e6;
constexpr int ORBIS_NET_ERROR_RESOLVER_ENOTIMPLEMENTED = 0x804101e7;
constexpr int ORBIS_NET_ERROR_RESOLVER_ESERVERREFUSED = 0x804101e8;
constexpr int ORBIS_NET_ERROR_RESOLVER_ENORECORD = 0x804101e9;
constexpr int ORBIS_NET_ERROR_RESOLVER_EALIGNMENT = 0x804101ea;

View File

@ -0,0 +1,110 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef _WIN32
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <Ws2tcpip.h>
#include <iphlpapi.h>
#include <winsock2.h>
typedef SOCKET net_socket;
typedef int socklen_t;
#else
#include <cerrno>
#include <arpa/inet.h>
#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
typedef int net_socket;
#endif
#if defined(__APPLE__)
#include <ifaddrs.h>
#include <net/if_dl.h>
#endif
#include <map>
#include <memory>
#include <mutex>
#include <vector>
#include <string.h>
#include "net_util.h"
namespace NetUtil {
const std::array<u8, 6>& NetUtilInternal::GetEthernetAddr() const {
return ether_address;
}
bool NetUtilInternal::RetrieveEthernetAddr() {
std::scoped_lock lock{m_mutex};
#ifdef _WIN32
std::vector<u8> adapter_infos(sizeof(IP_ADAPTER_INFO));
ULONG size_infos = sizeof(IP_ADAPTER_INFO);
if (GetAdaptersInfo(reinterpret_cast<PIP_ADAPTER_INFO>(adapter_infos.data()), &size_infos) ==
ERROR_BUFFER_OVERFLOW)
adapter_infos.resize(size_infos);
if (GetAdaptersInfo(reinterpret_cast<PIP_ADAPTER_INFO>(adapter_infos.data()), &size_infos) ==
NO_ERROR &&
size_infos) {
PIP_ADAPTER_INFO info = reinterpret_cast<PIP_ADAPTER_INFO>(adapter_infos.data());
memcpy(ether_address.data(), info[0].Address, 6);
return true;
}
#elif defined(__APPLE__)
ifaddrs* ifap;
if (getifaddrs(&ifap) == 0) {
ifaddrs* p;
for (p = ifap; p; p = p->ifa_next) {
if (p->ifa_addr->sa_family == AF_LINK) {
sockaddr_dl* sdp = reinterpret_cast<sockaddr_dl*>(p->ifa_addr);
memcpy(ether_address.data(), sdp->sdl_data + sdp->sdl_nlen, 6);
freeifaddrs(ifap);
return true;
}
}
freeifaddrs(ifap);
}
#else
ifreq ifr;
ifconf ifc;
char buf[1024];
int success = 0;
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sock == -1)
return false;
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = buf;
if (ioctl(sock, SIOCGIFCONF, &ifc) == -1)
return false;
ifreq* it = ifc.ifc_req;
const ifreq* const end = it + (ifc.ifc_len / sizeof(ifreq));
for (; it != end; ++it) {
strcpy(ifr.ifr_name, it->ifr_name);
if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) {
if (!(ifr.ifr_flags & IFF_LOOPBACK)) {
if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) {
success = 1;
break;
}
}
}
}
if (success) {
memcpy(ether_address.data(), ifr.ifr_hwaddr.sa_data, 6);
return true;
}
#endif
return false;
}
} // namespace NetUtil

View File

@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include "common/types.h"
namespace NetUtil {
class NetUtilInternal {
public:
explicit NetUtilInternal() = default;
~NetUtilInternal() = default;
private:
std::array<u8, 6> ether_address{};
std::mutex m_mutex;
public:
const std::array<u8, 6>& GetEthernetAddr() const;
bool RetrieveEthernetAddr();
};
} // namespace NetUtil

View File

@ -12,11 +12,13 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
#include <common/singleton.h>
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/libraries/error_codes.h" #include "core/libraries/error_codes.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
#include "core/libraries/network/net_ctl_codes.h" #include "core/libraries/network/net_ctl_codes.h"
#include "core/libraries/network/netctl.h" #include "core/libraries/network/netctl.h"
#include "net_util.h"
namespace Libraries::NetCtl { namespace Libraries::NetCtl {
@ -162,6 +164,14 @@ int PS4_SYSV_ABI sceNetCtlGetInfo(int code, OrbisNetCtlInfo* info) {
case ORBIS_NET_CTL_INFO_DEVICE: case ORBIS_NET_CTL_INFO_DEVICE:
info->device = ORBIS_NET_CTL_DEVICE_WIRED; info->device = ORBIS_NET_CTL_DEVICE_WIRED;
break; break;
case ORBIS_NET_CTL_INFO_ETHER_ADDR: {
auto* netinfo = Common::Singleton<NetUtil::NetUtilInternal>::Instance();
netinfo->RetrieveEthernetAddr();
memcpy(info->ether_addr.data, netinfo->GetEthernetAddr().data(), 6);
} break;
case ORBIS_NET_CTL_INFO_MTU:
info->mtu = 1500; // default value
break;
case ORBIS_NET_CTL_INFO_LINK: case ORBIS_NET_CTL_INFO_LINK:
info->link = ORBIS_NET_CTL_LINK_DISCONNECTED; info->link = ORBIS_NET_CTL_LINK_DISCONNECTED;
break; break;
@ -183,6 +193,7 @@ int PS4_SYSV_ABI sceNetCtlGetInfo(int code, OrbisNetCtlInfo* info) {
} }
break; break;
} }
default: default:
LOG_ERROR(Lib_NetCtl, "{} unsupported code", code); LOG_ERROR(Lib_NetCtl, "{} unsupported code", code);
} }

View File

@ -49,8 +49,26 @@ union OrbisNetCtlInfo {
// GetInfo codes // GetInfo codes
constexpr int ORBIS_NET_CTL_INFO_DEVICE = 1; constexpr int ORBIS_NET_CTL_INFO_DEVICE = 1;
constexpr int ORBIS_NET_CTL_INFO_ETHER_ADDR = 2;
constexpr int ORBIS_NET_CTL_INFO_MTU = 3;
constexpr int ORBIS_NET_CTL_INFO_LINK = 4; constexpr int ORBIS_NET_CTL_INFO_LINK = 4;
constexpr int ORBIS_NET_CTL_INFO_BSSID = 5;
constexpr int ORBIS_NET_CTL_INFO_SSID = 6;
constexpr int ORBIS_NET_CTL_INFO_WIFI_SECURITY = 7;
constexpr int ORBIS_NET_CTL_INFO_RSSI_DBM = 8;
constexpr int ORBIS_NET_CTL_INFO_RSSI_PERCENTAGE = 9;
constexpr int ORBIS_NET_CTL_INFO_CHANNEL = 10;
constexpr int ORBIS_NET_CTL_INFO_IP_CONFIG = 11;
constexpr int ORBIS_NET_CTL_INFO_DHCP_HOSTNAME = 12;
constexpr int ORBIS_NET_CTL_INFO_PPPOE_AUTH_NAME = 13;
constexpr int ORBIS_NET_CTL_INFO_IP_ADDRESS = 14; constexpr int ORBIS_NET_CTL_INFO_IP_ADDRESS = 14;
constexpr int ORBIS_NET_CTL_INFO_NETMASK = 15;
constexpr int ORBIS_NET_CTL_INFO_DEFAULT_ROUTE = 16;
constexpr int ORBIS_NET_CTL_INFO_PRIMARY_DNS = 17;
constexpr int ORBIS_NET_CTL_INFO_SECONDARY_DNS = 18;
constexpr int ORBIS_NET_CTL_INFO_HTTP_PROXY_CONFIG = 19;
constexpr int ORBIS_NET_CTL_INFO_HTTP_PROXY_SERVER = 20;
constexpr int ORBIS_NET_CTL_INFO_HTTP_PROXY_PORT = 21;
int PS4_SYSV_ABI sceNetBweCheckCallbackIpcInt(); int PS4_SYSV_ABI sceNetBweCheckCallbackIpcInt();
int PS4_SYSV_ABI sceNetBweClearEventIpcInt(); int PS4_SYSV_ABI sceNetBweClearEventIpcInt();

View File

@ -0,0 +1,60 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <common/assert.h>
#include "net.h"
#include "net_error.h"
#include "sockets.h"
namespace Libraries::Net {
int P2PSocket::Close() {
LOG_ERROR(Lib_Net, "(STUBBED) called");
return -1;
}
int P2PSocket::SetSocketOptions(int level, int optname, const void* optval, u32 optlen) {
LOG_ERROR(Lib_Net, "(STUBBED) called");
return -1;
}
int P2PSocket::GetSocketOptions(int level, int optname, void* optval, u32* optlen) {
LOG_ERROR(Lib_Net, "(STUBBED) called");
return -1;
}
int P2PSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) {
LOG_ERROR(Lib_Net, "(STUBBED) called");
return -1;
}
int P2PSocket::Listen(int backlog) {
LOG_ERROR(Lib_Net, "(STUBBED) called");
return -1;
}
int P2PSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to,
u32 tolen) {
LOG_ERROR(Lib_Net, "(STUBBED) called");
return -1;
}
int P2PSocket::ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, u32* fromlen) {
LOG_ERROR(Lib_Net, "(STUBBED) called");
return -1;
}
SocketPtr P2PSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) {
LOG_ERROR(Lib_Net, "(STUBBED) called");
return nullptr;
}
int P2PSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) {
LOG_ERROR(Lib_Net, "(STUBBED) called");
return -1;
}
int P2PSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) {
LOG_ERROR(Lib_Net, "(STUBBED) called");
return -1;
}
} // namespace Libraries::Net

View File

@ -0,0 +1,359 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <common/assert.h>
#include "net.h"
#include "net_error.h"
#include "sockets.h"
namespace Libraries::Net {
#ifdef _WIN32
#define ERROR_CASE(errname) \
case (WSA##errname): \
return ORBIS_NET_ERROR_##errname;
#else
#define ERROR_CASE(errname) \
case (errname): \
return ORBIS_NET_ERROR_##errname;
#endif
static int ConvertReturnErrorCode(int retval) {
if (retval < 0) {
#ifdef _WIN32
switch (WSAGetLastError()) {
#else
switch (errno) {
#endif
#ifndef _WIN32 // These errorcodes don't exist in WinSock
ERROR_CASE(EPERM)
ERROR_CASE(ENOENT)
// ERROR_CASE(ESRCH)
// ERROR_CASE(EIO)
// ERROR_CASE(ENXIO)
// ERROR_CASE(E2BIG)
// ERROR_CASE(ENOEXEC)
// ERROR_CASE(EDEADLK)
ERROR_CASE(ENOMEM)
// ERROR_CASE(ECHILD)
// ERROR_CASE(EBUSY)
ERROR_CASE(EEXIST)
// ERROR_CASE(EXDEV)
ERROR_CASE(ENODEV)
// ERROR_CASE(ENOTDIR)
// ERROR_CASE(EISDIR)
ERROR_CASE(ENFILE)
// ERROR_CASE(ENOTTY)
// ERROR_CASE(ETXTBSY)
// ERROR_CASE(EFBIG)
ERROR_CASE(ENOSPC)
// ERROR_CASE(ESPIPE)
// ERROR_CASE(EROFS)
// ERROR_CASE(EMLINK)
ERROR_CASE(EPIPE)
// ERROR_CASE(EDOM)
// ERROR_CASE(ERANGE)
// ERROR_CASE(ENOLCK)
// ERROR_CASE(ENOSYS)
// ERROR_CASE(EIDRM)
// ERROR_CASE(EOVERFLOW)
// ERROR_CASE(EILSEQ)
// ERROR_CASE(ENOTSUP)
ERROR_CASE(ECANCELED)
// ERROR_CASE(EBADMSG)
ERROR_CASE(ENODATA)
// ERROR_CASE(ENOSR)
// ERROR_CASE(ENOSTR)
// ERROR_CASE(ETIME)
#endif
ERROR_CASE(EINTR)
ERROR_CASE(EBADF)
ERROR_CASE(EACCES)
ERROR_CASE(EFAULT)
ERROR_CASE(EINVAL)
ERROR_CASE(EMFILE)
ERROR_CASE(EWOULDBLOCK)
ERROR_CASE(EINPROGRESS)
ERROR_CASE(EALREADY)
ERROR_CASE(ENOTSOCK)
ERROR_CASE(EDESTADDRREQ)
ERROR_CASE(EMSGSIZE)
ERROR_CASE(EPROTOTYPE)
ERROR_CASE(ENOPROTOOPT)
ERROR_CASE(EPROTONOSUPPORT)
#if defined(__APPLE__) || defined(_WIN32)
ERROR_CASE(EOPNOTSUPP)
#endif
ERROR_CASE(EAFNOSUPPORT)
ERROR_CASE(EADDRINUSE)
ERROR_CASE(EADDRNOTAVAIL)
ERROR_CASE(ENETDOWN)
ERROR_CASE(ENETUNREACH)
ERROR_CASE(ENETRESET)
ERROR_CASE(ECONNABORTED)
ERROR_CASE(ECONNRESET)
ERROR_CASE(ENOBUFS)
ERROR_CASE(EISCONN)
ERROR_CASE(ENOTCONN)
ERROR_CASE(ETIMEDOUT)
ERROR_CASE(ECONNREFUSED)
ERROR_CASE(ELOOP)
ERROR_CASE(ENAMETOOLONG)
ERROR_CASE(EHOSTUNREACH)
ERROR_CASE(ENOTEMPTY)
}
return ORBIS_NET_ERROR_EINTERNAL;
}
// if it is 0 or positive return it as it is
return retval;
}
static int ConvertLevels(int level) {
switch (level) {
case ORBIS_NET_SOL_SOCKET:
return SOL_SOCKET;
case ORBIS_NET_IPPROTO_IP:
return IPPROTO_IP;
case ORBIS_NET_IPPROTO_TCP:
return IPPROTO_TCP;
}
return -1;
}
static void convertOrbisNetSockaddrToPosix(const OrbisNetSockaddr* src, sockaddr* dst) {
if (src == nullptr || dst == nullptr)
return;
memset(dst, 0, sizeof(sockaddr));
const OrbisNetSockaddrIn* src_in = (const OrbisNetSockaddrIn*)src;
sockaddr_in* dst_in = (sockaddr_in*)dst;
dst_in->sin_family = src_in->sin_family;
dst_in->sin_port = src_in->sin_port;
memcpy(&dst_in->sin_addr, &src_in->sin_addr, 4);
}
static void convertPosixSockaddrToOrbis(sockaddr* src, OrbisNetSockaddr* dst) {
if (src == nullptr || dst == nullptr)
return;
memset(dst, 0, sizeof(OrbisNetSockaddr));
OrbisNetSockaddrIn* dst_in = (OrbisNetSockaddrIn*)dst;
sockaddr_in* src_in = (sockaddr_in*)src;
dst_in->sin_family = static_cast<unsigned char>(src_in->sin_family);
dst_in->sin_port = src_in->sin_port;
memcpy(&dst_in->sin_addr, &src_in->sin_addr, 4);
}
int PosixSocket::Close() {
#ifdef _WIN32
auto out = closesocket(sock);
#else
auto out = ::close(sock);
#endif
return ConvertReturnErrorCode(out);
}
int PosixSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) {
sockaddr addr2;
convertOrbisNetSockaddrToPosix(addr, &addr2);
return ConvertReturnErrorCode(::bind(sock, &addr2, sizeof(sockaddr_in)));
}
int PosixSocket::Listen(int backlog) {
return ConvertReturnErrorCode(::listen(sock, backlog));
}
int PosixSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to,
u32 tolen) {
if (to != nullptr) {
sockaddr addr;
convertOrbisNetSockaddrToPosix(to, &addr);
return ConvertReturnErrorCode(
sendto(sock, (const char*)msg, len, flags, &addr, sizeof(sockaddr_in)));
} else {
return ConvertReturnErrorCode(send(sock, (const char*)msg, len, flags));
}
}
int PosixSocket::ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from,
u32* fromlen) {
if (from != nullptr) {
sockaddr addr;
int res = recvfrom(sock, (char*)buf, len, flags, &addr, (socklen_t*)fromlen);
convertPosixSockaddrToOrbis(&addr, from);
*fromlen = sizeof(OrbisNetSockaddrIn);
return ConvertReturnErrorCode(res);
} else {
return ConvertReturnErrorCode(recv(sock, (char*)buf, len, flags));
}
}
SocketPtr PosixSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) {
sockaddr addr2;
net_socket new_socket = ::accept(sock, &addr2, (socklen_t*)addrlen);
#ifdef _WIN32
if (new_socket != INVALID_SOCKET) {
#else
if (new_socket >= 0) {
#endif
convertPosixSockaddrToOrbis(&addr2, addr);
*addrlen = sizeof(OrbisNetSockaddrIn);
return std::make_shared<PosixSocket>(new_socket);
}
return nullptr;
}
int PosixSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) {
sockaddr addr2;
convertOrbisNetSockaddrToPosix(addr, &addr2);
return ::connect(sock, &addr2, sizeof(sockaddr_in));
}
int PosixSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) {
sockaddr addr;
convertOrbisNetSockaddrToPosix(name, &addr);
if (name != nullptr) {
*namelen = sizeof(sockaddr_in);
}
int res = getsockname(sock, &addr, (socklen_t*)namelen);
if (res >= 0) {
convertPosixSockaddrToOrbis(&addr, name);
*namelen = sizeof(OrbisNetSockaddrIn);
}
return res;
}
#define CASE_SETSOCKOPT(opt) \
case ORBIS_NET_##opt: \
return ConvertReturnErrorCode(setsockopt(sock, level, opt, (const char*)optval, optlen))
#define CASE_SETSOCKOPT_VALUE(opt, value) \
case opt: \
if (optlen != sizeof(*value)) { \
return ORBIS_NET_ERROR_EFAULT; \
} \
memcpy(value, optval, optlen); \
return 0
int PosixSocket::SetSocketOptions(int level, int optname, const void* optval, u32 optlen) {
level = ConvertLevels(level);
if (level == SOL_SOCKET) {
switch (optname) {
CASE_SETSOCKOPT(SO_REUSEADDR);
CASE_SETSOCKOPT(SO_KEEPALIVE);
CASE_SETSOCKOPT(SO_BROADCAST);
CASE_SETSOCKOPT(SO_LINGER);
CASE_SETSOCKOPT(SO_SNDBUF);
CASE_SETSOCKOPT(SO_RCVBUF);
CASE_SETSOCKOPT(SO_SNDTIMEO);
CASE_SETSOCKOPT(SO_RCVTIMEO);
CASE_SETSOCKOPT(SO_ERROR);
CASE_SETSOCKOPT(SO_TYPE);
CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_REUSEPORT, &sockopt_so_reuseport);
CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_ONESBCAST, &sockopt_so_onesbcast);
CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_USECRYPTO, &sockopt_so_usecrypto);
CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_USESIGNATURE, &sockopt_so_usesignature);
case ORBIS_NET_SO_NAME:
return ORBIS_NET_ERROR_EINVAL; // don't support set for name
case ORBIS_NET_SO_NBIO: {
if (optlen != sizeof(sockopt_so_nbio)) {
return ORBIS_NET_ERROR_EFAULT;
}
memcpy(&sockopt_so_nbio, optval, optlen);
#ifdef _WIN32
static_assert(sizeof(u_long) == sizeof(sockopt_so_nbio),
"type used for ioctlsocket value does not have the expected size");
return ConvertReturnErrorCode(ioctlsocket(sock, FIONBIO, (u_long*)&sockopt_so_nbio));
#else
return ConvertReturnErrorCode(ioctl(sock, FIONBIO, &sockopt_so_nbio));
#endif
}
}
} else if (level == IPPROTO_IP) {
switch (optname) {
CASE_SETSOCKOPT(IP_HDRINCL);
CASE_SETSOCKOPT(IP_TOS);
CASE_SETSOCKOPT(IP_TTL);
CASE_SETSOCKOPT(IP_MULTICAST_IF);
CASE_SETSOCKOPT(IP_MULTICAST_TTL);
CASE_SETSOCKOPT(IP_MULTICAST_LOOP);
CASE_SETSOCKOPT(IP_ADD_MEMBERSHIP);
CASE_SETSOCKOPT(IP_DROP_MEMBERSHIP);
CASE_SETSOCKOPT_VALUE(ORBIS_NET_IP_TTLCHK, &sockopt_ip_ttlchk);
CASE_SETSOCKOPT_VALUE(ORBIS_NET_IP_MAXTTL, &sockopt_ip_maxttl);
}
} else if (level == IPPROTO_TCP) {
switch (optname) {
CASE_SETSOCKOPT(TCP_NODELAY);
CASE_SETSOCKOPT(TCP_MAXSEG);
CASE_SETSOCKOPT_VALUE(ORBIS_NET_TCP_MSS_TO_ADVERTISE, &sockopt_tcp_mss_to_advertise);
}
}
UNREACHABLE_MSG("Unknown level ={} optname ={}", level, optname);
return 0;
}
#define CASE_GETSOCKOPT(opt) \
case ORBIS_NET_##opt: { \
socklen_t optlen_temp = *optlen; \
auto retval = \
ConvertReturnErrorCode(getsockopt(sock, level, opt, (char*)optval, &optlen_temp)); \
*optlen = optlen_temp; \
return retval; \
}
#define CASE_GETSOCKOPT_VALUE(opt, value) \
case opt: \
if (*optlen < sizeof(value)) { \
*optlen = sizeof(value); \
return ORBIS_NET_ERROR_EFAULT; \
} \
*optlen = sizeof(value); \
*(decltype(value)*)optval = value; \
return 0;
int PosixSocket::GetSocketOptions(int level, int optname, void* optval, u32* optlen) {
level = ConvertLevels(level);
if (level == SOL_SOCKET) {
switch (optname) {
CASE_GETSOCKOPT(SO_REUSEADDR);
CASE_GETSOCKOPT(SO_KEEPALIVE);
CASE_GETSOCKOPT(SO_BROADCAST);
CASE_GETSOCKOPT(SO_LINGER);
CASE_GETSOCKOPT(SO_SNDBUF);
CASE_GETSOCKOPT(SO_RCVBUF);
CASE_GETSOCKOPT(SO_SNDTIMEO);
CASE_GETSOCKOPT(SO_RCVTIMEO);
CASE_GETSOCKOPT(SO_ERROR);
CASE_GETSOCKOPT(SO_TYPE);
CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_NBIO, sockopt_so_nbio);
CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_REUSEPORT, sockopt_so_reuseport);
CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_ONESBCAST, sockopt_so_onesbcast);
CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_USECRYPTO, sockopt_so_usecrypto);
CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_USESIGNATURE, sockopt_so_usesignature);
CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_NAME,
(char)0); // writes an empty string to the output buffer
}
} else if (level == IPPROTO_IP) {
switch (optname) {
CASE_GETSOCKOPT(IP_HDRINCL);
CASE_GETSOCKOPT(IP_TOS);
CASE_GETSOCKOPT(IP_TTL);
CASE_GETSOCKOPT(IP_MULTICAST_IF);
CASE_GETSOCKOPT(IP_MULTICAST_TTL);
CASE_GETSOCKOPT(IP_MULTICAST_LOOP);
CASE_GETSOCKOPT(IP_ADD_MEMBERSHIP);
CASE_GETSOCKOPT(IP_DROP_MEMBERSHIP);
CASE_GETSOCKOPT_VALUE(ORBIS_NET_IP_TTLCHK, sockopt_ip_ttlchk);
CASE_GETSOCKOPT_VALUE(ORBIS_NET_IP_MAXTTL, sockopt_ip_maxttl);
}
} else if (level == IPPROTO_TCP) {
switch (optname) {
CASE_GETSOCKOPT(TCP_NODELAY);
CASE_GETSOCKOPT(TCP_MAXSEG);
CASE_GETSOCKOPT_VALUE(ORBIS_NET_TCP_MSS_TO_ADVERTISE, sockopt_tcp_mss_to_advertise);
}
}
UNREACHABLE_MSG("Unknown level ={} optname ={}", level, optname);
return 0;
}
} // namespace Libraries::Net

View File

@ -0,0 +1,112 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#ifdef _WIN32
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <Ws2tcpip.h>
#include <iphlpapi.h>
#include <winsock2.h>
typedef SOCKET net_socket;
typedef int socklen_t;
#else
#include <cerrno>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
typedef int net_socket;
#endif
#include <map>
#include <memory>
#include <mutex>
#include "net.h"
namespace Libraries::Net {
struct Socket;
typedef std::shared_ptr<Socket> SocketPtr;
struct Socket {
explicit Socket(int domain, int type, int protocol) {}
virtual ~Socket() = default;
virtual int Close() = 0;
virtual int SetSocketOptions(int level, int optname, const void* optval, u32 optlen) = 0;
virtual int GetSocketOptions(int level, int optname, void* optval, u32* optlen) = 0;
virtual int Bind(const OrbisNetSockaddr* addr, u32 addrlen) = 0;
virtual int Listen(int backlog) = 0;
virtual int SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to,
u32 tolen) = 0;
virtual SocketPtr Accept(OrbisNetSockaddr* addr, u32* addrlen) = 0;
virtual int ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from,
u32* fromlen) = 0;
virtual int Connect(const OrbisNetSockaddr* addr, u32 namelen) = 0;
virtual int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) = 0;
};
struct PosixSocket : public Socket {
net_socket sock;
int sockopt_so_reuseport = 0;
int sockopt_so_onesbcast = 0;
int sockopt_so_usecrypto = 0;
int sockopt_so_usesignature = 0;
int sockopt_so_nbio = 0;
int sockopt_ip_ttlchk = 0;
int sockopt_ip_maxttl = 0;
int sockopt_tcp_mss_to_advertise = 0;
explicit PosixSocket(int domain, int type, int protocol)
: Socket(domain, type, protocol), sock(socket(domain, type, protocol)) {}
explicit PosixSocket(net_socket sock) : Socket(0, 0, 0), sock(sock) {}
int Close() override;
int SetSocketOptions(int level, int optname, const void* optval, u32 optlen) override;
int GetSocketOptions(int level, int optname, void* optval, u32* optlen) override;
int Bind(const OrbisNetSockaddr* addr, u32 addrlen) override;
int Listen(int backlog) override;
int SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to,
u32 tolen) override;
int ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, u32* fromlen) override;
SocketPtr Accept(OrbisNetSockaddr* addr, u32* addrlen) override;
int Connect(const OrbisNetSockaddr* addr, u32 namelen) override;
int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override;
};
struct P2PSocket : public Socket {
explicit P2PSocket(int domain, int type, int protocol) : Socket(domain, type, protocol) {}
int Close() override;
int SetSocketOptions(int level, int optname, const void* optval, u32 optlen) override;
int GetSocketOptions(int level, int optname, void* optval, u32* optlen) override;
int Bind(const OrbisNetSockaddr* addr, u32 addrlen) override;
int Listen(int backlog) override;
int SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to,
u32 tolen) override;
int ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, u32* fromlen) override;
SocketPtr Accept(OrbisNetSockaddr* addr, u32* addrlen) override;
int Connect(const OrbisNetSockaddr* addr, u32 namelen) override;
int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override;
};
class NetInternal {
public:
explicit NetInternal() = default;
~NetInternal() = default;
SocketPtr FindSocket(int sockid) {
std::scoped_lock lock{m_mutex};
const auto it = socks.find(sockid);
if (it != socks.end()) {
return it->second;
}
return 0;
}
public:
std::mutex m_mutex;
typedef std::map<int, SocketPtr> NetSockets;
NetSockets socks;
int next_sock_id = 0;
};
} // namespace Libraries::Net

View File

@ -0,0 +1,229 @@
#include "sys_net.h"
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <common/assert.h>
#include <common/logging/log.h>
#include <core/libraries/kernel/kernel.h>
#include "common/singleton.h"
#include "net_error.h"
#include "sockets.h"
#include "sys_net.h"
namespace Libraries::Net {
int PS4_SYSV_ABI sys_connect(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen) {
auto* netcall = Common::Singleton<NetInternal>::Instance();
auto sock = netcall->FindSocket(s);
if (!sock) {
*Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EBADF;
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
return -1;
}
int returncode = sock->Connect(addr, addrlen);
if (returncode >= 0) {
return returncode;
}
*Libraries::Kernel::__Error() = returncode;
LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode);
return -1;
}
int PS4_SYSV_ABI sys_bind(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen) {
auto* netcall = Common::Singleton<NetInternal>::Instance();
auto sock = netcall->FindSocket(s);
if (!sock) {
*Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EBADF;
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
return -1;
}
int returncode = sock->Bind(addr, addrlen);
if (returncode >= 0) {
return returncode;
}
*Libraries::Kernel::__Error() = returncode;
LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode);
return -1;
}
int PS4_SYSV_ABI sys_accept(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen) {
auto* netcall = Common::Singleton<NetInternal>::Instance();
auto sock = netcall->FindSocket(s);
if (!sock) {
*Libraries::Kernel::__Error() = ORBIS_NET_EBADF;
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
return -1;
}
auto new_sock = sock->Accept(addr, paddrlen);
if (!new_sock) {
*Libraries::Kernel::__Error() = ORBIS_NET_EBADF;
LOG_ERROR(Lib_Net, "error creating new socket for accepting");
return -1;
}
auto id = ++netcall->next_sock_id;
netcall->socks.emplace(id, new_sock);
return id;
}
int PS4_SYSV_ABI sys_getpeername(OrbisNetId s, const OrbisNetSockaddr* addr, u32* paddrlen) {
LOG_ERROR(Lib_Net, "(STUBBED) called");
return -1;
}
int PS4_SYSV_ABI sys_getsockname(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen) {
auto* netcall = Common::Singleton<NetInternal>::Instance();
auto sock = netcall->FindSocket(s);
if (!sock) {
*Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EBADF;
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
return -1;
}
int returncode = sock->GetSocketAddress(addr, paddrlen);
if (returncode >= 0) {
return returncode;
}
*Libraries::Kernel::__Error() = returncode;
LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode);
return -1;
}
int PS4_SYSV_ABI sys_getsockopt(OrbisNetId s, int level, int optname, void* optval, u32* optlen) {
auto* netcall = Common::Singleton<NetInternal>::Instance();
auto sock = netcall->FindSocket(s);
if (!sock) {
*Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EBADF;
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
return -1;
}
int returncode = sock->GetSocketOptions(level, optname, optval, optlen);
if (returncode >= 0) {
return returncode;
}
*Libraries::Kernel::__Error() = returncode;
LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode);
return -1;
}
int PS4_SYSV_ABI sys_listen(OrbisNetId s, int backlog) {
auto* netcall = Common::Singleton<NetInternal>::Instance();
auto sock = netcall->FindSocket(s);
if (!sock) {
*Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EBADF;
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
return -1;
}
int returncode = sock->Listen(backlog);
if (returncode >= 0) {
return returncode;
}
*Libraries::Kernel::__Error() = returncode;
LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode);
return -1;
}
int PS4_SYSV_ABI sys_setsockopt(OrbisNetId s, int level, int optname, const void* optval,
u32 optlen) {
auto* netcall = Common::Singleton<NetInternal>::Instance();
auto sock = netcall->FindSocket(s);
if (!sock) {
*Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EBADF;
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
return -1;
}
int returncode = sock->SetSocketOptions(level, optname, optval, optlen);
if (returncode >= 0) {
return returncode;
}
*Libraries::Kernel::__Error() = returncode;
LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode);
return -1;
}
int PS4_SYSV_ABI sys_shutdown(OrbisNetId s, int how) {
return -1;
}
int PS4_SYSV_ABI sys_socketex(const char* name, int family, int type, int protocol) {
if (name == nullptr) {
LOG_INFO(Lib_Net, "name = no-named family = {} type = {} protocol = {}", family, type,
protocol);
} else {
LOG_INFO(Lib_Net, "name = {} family = {} type = {} protocol = {}", std::string(name),
family, type, protocol);
}
SocketPtr sock;
switch (type) {
case ORBIS_NET_SOCK_STREAM:
case ORBIS_NET_SOCK_DGRAM:
case ORBIS_NET_SOCK_RAW:
sock = std::make_shared<PosixSocket>(family, type, protocol);
break;
case ORBIS_NET_SOCK_DGRAM_P2P:
case ORBIS_NET_SOCK_STREAM_P2P:
sock = std::make_shared<P2PSocket>(family, type, protocol);
break;
default:
UNREACHABLE_MSG("Unknown type {}", type);
}
auto* netcall = Common::Singleton<NetInternal>::Instance();
auto id = ++netcall->next_sock_id;
netcall->socks.emplace(id, sock);
return id;
}
int PS4_SYSV_ABI sys_socket(int family, int type, int protocol) {
return sys_socketex(nullptr, family, type, protocol);
}
int PS4_SYSV_ABI sys_netabort(OrbisNetId s, int flags) {
LOG_ERROR(Lib_Net, "(STUBBED) called");
return -1;
}
int PS4_SYSV_ABI sys_socketclose(OrbisNetId s) {
auto* netcall = Common::Singleton<NetInternal>::Instance();
auto sock = netcall->FindSocket(s);
if (!sock) {
*Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EBADF;
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
return -1;
}
int returncode = sock->Close();
if (returncode >= 0) {
return returncode;
}
*Libraries::Kernel::__Error() = returncode;
LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode);
return -1;
}
int PS4_SYSV_ABI sys_sendto(OrbisNetId s, const void* buf, u64 len, int flags,
const OrbisNetSockaddr* addr, u32 addrlen) {
auto* netcall = Common::Singleton<NetInternal>::Instance();
auto sock = netcall->FindSocket(s);
if (!sock) {
*Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EBADF;
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
return -1;
}
int returncode = sock->SendPacket(buf, len, flags, addr, addrlen);
if (returncode >= 0) {
return returncode;
}
*Libraries::Kernel::__Error() = returncode;
LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode);
return -1;
}
int PS4_SYSV_ABI sys_sendmsg(OrbisNetId s, const OrbisNetMsghdr* msg, int flags) {
LOG_ERROR(Lib_Net, "(STUBBED) called");
return -1;
}
int PS4_SYSV_ABI sys_recvfrom(OrbisNetId s, void* buf, u64 len, int flags, OrbisNetSockaddr* addr,
u32* paddrlen) {
auto* netcall = Common::Singleton<NetInternal>::Instance();
auto sock = netcall->FindSocket(s);
if (!sock) {
*Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EBADF;
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
return -1;
}
int returncode = sock->ReceivePacket(buf, len, flags, addr, paddrlen);
if (returncode >= 0) {
return returncode;
}
*Libraries::Kernel::__Error() = returncode;
LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode);
return -1;
}
int PS4_SYSV_ABI sys_recvmsg(OrbisNetId s, OrbisNetMsghdr* msg, int flags) {
LOG_ERROR(Lib_Net, "(STUBBED) called");
return -1;
}
} // namespace Libraries::Net

View File

@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
#include "net.h"
namespace Libraries::Net {
int PS4_SYSV_ABI sys_connect(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen);
int PS4_SYSV_ABI sys_bind(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen);
int PS4_SYSV_ABI sys_accept(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen);
int PS4_SYSV_ABI sys_getpeername(OrbisNetId s, const OrbisNetSockaddr* addr, u32* paddrlen);
int PS4_SYSV_ABI sys_getsockname(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen);
int PS4_SYSV_ABI sys_getsockopt(OrbisNetId s, int level, int optname, void* optval, u32* optlen);
int PS4_SYSV_ABI sys_listen(OrbisNetId s, int backlog);
int PS4_SYSV_ABI sys_setsockopt(OrbisNetId s, int level, int optname, const void* optval,
u32 optlen);
int PS4_SYSV_ABI sys_shutdown(OrbisNetId s, int how);
int PS4_SYSV_ABI sys_socketex(const char* name, int family, int type, int protocol);
int PS4_SYSV_ABI sys_socket(int family, int type, int protocol);
int PS4_SYSV_ABI sys_netabort(OrbisNetId s, int flags);
int PS4_SYSV_ABI sys_socketclose(OrbisNetId s);
int PS4_SYSV_ABI sys_sendto(OrbisNetId s, const void* buf, u64 len, int flags,
const OrbisNetSockaddr* addr, u32 addrlen);
int PS4_SYSV_ABI sys_sendmsg(OrbisNetId s, const OrbisNetMsghdr* msg, int flags);
int PS4_SYSV_ABI sys_recvfrom(OrbisNetId s, void* buf, u64 len, int flags, OrbisNetSockaddr* addr,
u32* paddrlen);
int PS4_SYSV_ABI sys_recvmsg(OrbisNetId s, OrbisNetMsghdr* msg, int flags);
} // namespace Libraries::Net

View File

@ -49,13 +49,11 @@ void SaveDialogResult::CopyTo(OrbisSaveDataDialogResult& result) const {
result.mode = this->mode; result.mode = this->mode;
result.result = this->result; result.result = this->result;
result.buttonId = this->button_id; result.buttonId = this->button_id;
if (mode == SaveDataDialogMode::LIST || ElfInfo::Instance().FirmwareVer() >= ElfInfo::FW_45) { if (result.dirName != nullptr) {
if (result.dirName != nullptr) { result.dirName->data.FromString(this->dir_name);
result.dirName->data.FromString(this->dir_name); }
} if (result.param != nullptr && this->param.GetString(SaveParams::MAINTITLE).has_value()) {
if (result.param != nullptr && this->param.GetString(SaveParams::MAINTITLE).has_value()) { result.param->FromSFO(this->param);
result.param->FromSFO(this->param);
}
} }
result.userData = this->user_data; result.userData = this->user_data;
} }
@ -345,12 +343,15 @@ SaveDialogUi::SaveDialogUi(SaveDialogUi&& other) noexcept
} }
} }
SaveDialogUi& SaveDialogUi::operator=(SaveDialogUi other) { SaveDialogUi& SaveDialogUi::operator=(SaveDialogUi&& other) noexcept {
std::scoped_lock lock(draw_mutex, other.draw_mutex); std::scoped_lock lock(draw_mutex, other.draw_mutex);
using std::swap; using std::swap;
swap(state, other.state); state = other.state;
swap(status, other.status); other.state = nullptr;
swap(result, other.result); status = other.status;
other.status = nullptr;
result = other.result;
other.result = nullptr;
if (status && *status == Status::RUNNING) { if (status && *status == Status::RUNNING) {
first_render = true; first_render = true;
AddLayer(this); AddLayer(this);

View File

@ -300,7 +300,8 @@ public:
~SaveDialogUi() override; ~SaveDialogUi() override;
SaveDialogUi(const SaveDialogUi& other) = delete; SaveDialogUi(const SaveDialogUi& other) = delete;
SaveDialogUi(SaveDialogUi&& other) noexcept; SaveDialogUi(SaveDialogUi&& other) noexcept;
SaveDialogUi& operator=(SaveDialogUi other); SaveDialogUi& operator=(SaveDialogUi& other) = delete;
SaveDialogUi& operator=(SaveDialogUi&& other) noexcept;
void Finish(ButtonId buttonId, CommonDialog::Result r = CommonDialog::Result::OK); void Finish(ButtonId buttonId, CommonDialog::Result r = CommonDialog::Result::OK);

View File

@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Generated By moduleGenerator
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "signindialog.h"
namespace Libraries::SigninDialog {
s32 PS4_SYSV_ABI sceSigninDialogInitialize() {
LOG_ERROR(Lib_SigninDialog, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceSigninDialogOpen() {
LOG_ERROR(Lib_SigninDialog, "(STUBBED) called");
return ORBIS_OK;
}
Status PS4_SYSV_ABI sceSigninDialogGetStatus() {
LOG_ERROR(Lib_SigninDialog, "(STUBBED) called, return 'finished' status");
return Status::FINISHED;
}
Status PS4_SYSV_ABI sceSigninDialogUpdateStatus() {
LOG_ERROR(Lib_SigninDialog, "(STUBBED) called, return 'finished' status");
return Status::FINISHED;
}
s32 PS4_SYSV_ABI sceSigninDialogGetResult() {
LOG_ERROR(Lib_SigninDialog, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceSigninDialogClose() {
LOG_ERROR(Lib_SigninDialog, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceSigninDialogTerminate() {
LOG_ERROR(Lib_SigninDialog, "(STUBBED) called");
return ORBIS_OK;
}
void RegisterlibSceSigninDialog(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("mlYGfmqE3fQ", "libSceSigninDialog", 1, "libSceSigninDialog", 1, 1,
sceSigninDialogInitialize);
LIB_FUNCTION("JlpJVoRWv7U", "libSceSigninDialog", 1, "libSceSigninDialog", 1, 1,
sceSigninDialogOpen);
LIB_FUNCTION("2m077aeC+PA", "libSceSigninDialog", 1, "libSceSigninDialog", 1, 1,
sceSigninDialogGetStatus);
LIB_FUNCTION("Bw31liTFT3A", "libSceSigninDialog", 1, "libSceSigninDialog", 1, 1,
sceSigninDialogUpdateStatus);
LIB_FUNCTION("nqG7rqnYw1U", "libSceSigninDialog", 1, "libSceSigninDialog", 1, 1,
sceSigninDialogGetResult);
LIB_FUNCTION("M3OkENHcyiU", "libSceSigninDialog", 1, "libSceSigninDialog", 1, 1,
sceSigninDialogClose);
LIB_FUNCTION("LXlmS6PvJdU", "libSceSigninDialog", 1, "libSceSigninDialog", 1, 1,
sceSigninDialogTerminate);
};
} // namespace Libraries::SigninDialog

View File

@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
namespace Core::Loader {
class SymbolsResolver;
}
enum class Status : u32 {
NONE = 0,
INITIALIZED = 1,
RUNNING = 2,
FINISHED = 3,
};
namespace Libraries::SigninDialog {
s32 PS4_SYSV_ABI sceSigninDialogInitialize();
s32 PS4_SYSV_ABI sceSigninDialogOpen();
Status PS4_SYSV_ABI sceSigninDialogGetStatus();
Status PS4_SYSV_ABI sceSigninDialogUpdateStatus();
s32 PS4_SYSV_ABI sceSigninDialogGetResult();
s32 PS4_SYSV_ABI sceSigninDialogClose();
s32 PS4_SYSV_ABI sceSigninDialogTerminate();
void RegisterlibSceSigninDialog(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::SigninDialog

View File

@ -19,11 +19,40 @@ int PS4_SYSV_ABI sceSysmoduleGetModuleHandleInternal() {
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceSysmoduleGetModuleInfoForUnwind(VAddr addr, s32 flags, void* info) { s32 PS4_SYSV_ABI sceSysmoduleGetModuleInfoForUnwind(VAddr addr, s32 flags,
LOG_ERROR(Lib_SysModule, "(STUBBED) called"); Kernel::OrbisModuleInfoForUnwind* info) {
Kernel::OrbisModuleInfoForUnwind module_info; LOG_TRACE(Lib_SysModule, "sceSysmoduleGetModuleInfoForUnwind(addr=0x{:X}, flags=0x{:X})", addr,
module_info.st_size = 0x130; flags);
s32 res = Kernel::sceKernelGetModuleInfoForUnwind(addr, flags, &module_info);
s32 res = Kernel::sceKernelGetModuleInfoForUnwind(addr, flags, info);
if (res != 0) {
return res;
}
static constexpr std::array<std::string_view, 17> modules_to_hide = {
"libc.prx",
"libc.sprx",
"libSceAudioLatencyEstimation.prx",
"libSceFace.prx",
"libSceFaceTracker.prx",
"libSceFios2.prx",
"libSceFios2.sprx",
"libSceFontGsm.prx",
"libSceHand.prx",
"libSceHandTracker.prx",
"libSceHeadTracker.prx",
"libSceJobManager.prx",
"libSceNpCppWebApi.prx",
"libSceNpToolkit.prx",
"libSceNpToolkit2.prx",
"libSceS3DConversion.prx",
"libSceSmart.prx",
};
const std::string_view module_name = info->name.data();
if (std::ranges::find(modules_to_hide, module_name) != modules_to_hide.end()) {
std::ranges::fill(info->name, '\0');
}
return res; return res;
} }
@ -56,7 +85,6 @@ int PS4_SYSV_ABI sceSysmoduleIsLoadedInternal(OrbisSysModuleInternal id) {
} }
int PS4_SYSV_ABI sceSysmoduleLoadModule(OrbisSysModule id) { int PS4_SYSV_ABI sceSysmoduleLoadModule(OrbisSysModule id) {
auto color_name = magic_enum::enum_name(id);
LOG_ERROR(Lib_SysModule, "(DUMMY) called module = {}", magic_enum::enum_name(id)); LOG_ERROR(Lib_SysModule, "(DUMMY) called module = {}", magic_enum::enum_name(id));
return ORBIS_OK; return ORBIS_OK;
} }

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include "common/types.h" #include "common/types.h"
#include "core/libraries/kernel/process.h"
namespace Core::Loader { namespace Core::Loader {
class SymbolsResolver; class SymbolsResolver;
@ -152,7 +153,8 @@ enum class OrbisSysModuleInternal : u32 {
}; };
int PS4_SYSV_ABI sceSysmoduleGetModuleHandleInternal(); int PS4_SYSV_ABI sceSysmoduleGetModuleHandleInternal();
s32 PS4_SYSV_ABI sceSysmoduleGetModuleInfoForUnwind(VAddr addr, s32 flags, void* info); s32 PS4_SYSV_ABI sceSysmoduleGetModuleInfoForUnwind(VAddr addr, s32 flags,
Kernel::OrbisModuleInfoForUnwind* info);
int PS4_SYSV_ABI sceSysmoduleIsCalledFromSysModule(); int PS4_SYSV_ABI sceSysmoduleIsCalledFromSysModule();
int PS4_SYSV_ABI sceSysmoduleIsCameraPreloaded(); int PS4_SYSV_ABI sceSysmoduleIsCameraPreloaded();
int PS4_SYSV_ABI sceSysmoduleIsLoaded(OrbisSysModule id); int PS4_SYSV_ABI sceSysmoduleIsLoaded(OrbisSysModule id);

View File

@ -220,7 +220,7 @@ s32 PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, s64*
if (ev->ident != static_cast<s32>(OrbisVideoOutInternalEventId::Flip) || ev->data == 0) { if (ev->ident != static_cast<s32>(OrbisVideoOutInternalEventId::Flip) || ev->data == 0) {
*data = event_data; *data = event_data;
} else { } else {
*data = event_data | 0xFFFF000000000000; *data = event_data | 0xffff000000000000;
} }
return ORBIS_OK; return ORBIS_OK;
} }
@ -233,7 +233,8 @@ s32 PS4_SYSV_ABI sceVideoOutGetEventCount(const Kernel::SceKernelEvent* ev) {
return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT; return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT;
} }
return (ev->data >> 0xc) & 0xf; auto event_data = static_cast<OrbisVideoOutEventData>(ev->data);
return event_data.count;
} }
s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, FlipStatus* status) { s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, FlipStatus* status) {

View File

@ -111,6 +111,12 @@ struct SceVideoOutColorSettings {
u32 reserved[3]; u32 reserved[3];
}; };
struct OrbisVideoOutEventData {
u64 time : 12;
u64 count : 4;
u64 flip_arg : 48;
};
void PS4_SYSV_ABI sceVideoOutSetBufferAttribute(BufferAttribute* attribute, PixelFormat pixelFormat, void PS4_SYSV_ABI sceVideoOutSetBufferAttribute(BufferAttribute* attribute, PixelFormat pixelFormat,
u32 tilingMode, u32 aspectRatio, u32 width, u32 tilingMode, u32 aspectRatio, u32 width,
u32 height, u32 pitchInPixel); u32 height, u32 pitchInPixel);
@ -128,8 +134,8 @@ s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutio
s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 index, s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 index,
const void* param); const void* param);
s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle); s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle);
int PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev); s32 PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev);
int PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, int64_t* data); s32 PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, s64* data);
s32 PS4_SYSV_ABI sceVideoOutColorSettingsSetGamma(SceVideoOutColorSettings* settings, float gamma); s32 PS4_SYSV_ABI sceVideoOutColorSettingsSetGamma(SceVideoOutColorSettings* settings, float gamma);
s32 PS4_SYSV_ABI sceVideoOutAdjustColor(s32 handle, const SceVideoOutColorSettings* settings); s32 PS4_SYSV_ABI sceVideoOutAdjustColor(s32 handle, const SceVideoOutColorSettings* settings);

View File

@ -112,18 +112,6 @@ void Linker::Execute(const std::vector<std::string> args) {
0, "SceKernelInternalMemory"); 0, "SceKernelInternalMemory");
ASSERT_MSG(ret == 0, "Unable to perform sceKernelInternalMemory mapping"); ASSERT_MSG(ret == 0, "Unable to perform sceKernelInternalMemory mapping");
// Simulate libSceGnmDriver initialization, which maps a chunk of direct memory.
// Some games fail without accurately emulating this behavior.
s64 phys_addr{};
ret = Libraries::Kernel::sceKernelAllocateDirectMemory(
0, Libraries::Kernel::sceKernelGetDirectMemorySize(), 0x10000, 0x10000, 3, &phys_addr);
if (ret == 0) {
void* addr{reinterpret_cast<void*>(0xfe0000000)};
ret = Libraries::Kernel::sceKernelMapNamedDirectMemory(&addr, 0x10000, 0x13, 0, phys_addr,
0x10000, "SceGnmDriver");
}
ASSERT_MSG(ret == 0, "Unable to emulate libSceGnmDriver initialization");
main_thread.Run([this, module, args](std::stop_token) { main_thread.Run([this, module, args](std::stop_token) {
Common::SetCurrentThreadName("GAME_MainThread"); Common::SetCurrentThreadName("GAME_MainThread");
LoadSharedLibraries(); LoadSharedLibraries();

View File

@ -83,7 +83,7 @@ public:
} }
Module* GetModule(s32 index) const { Module* GetModule(s32 index) const {
if (index >= 0 || index < m_modules.size()) { if (index >= 0 && index < m_modules.size()) {
return m_modules.at(index).get(); return m_modules.at(index).get();
} }
return nullptr; return nullptr;

View File

@ -75,7 +75,8 @@ u64 MemoryManager::ClampRangeSize(VAddr virtual_addr, u64 size) {
// Clamp size to the remaining size of the current VMA. // Clamp size to the remaining size of the current VMA.
auto vma = FindVMA(virtual_addr); auto vma = FindVMA(virtual_addr);
ASSERT_MSG(vma != vma_map.end(), "Attempted to access invalid GPU address {:#x}", virtual_addr); ASSERT_MSG(vma->second.Contains(virtual_addr, 0),
"Attempted to access invalid GPU address {:#x}", virtual_addr);
u64 clamped_size = vma->second.base + vma->second.size - virtual_addr; u64 clamped_size = vma->second.base + vma->second.size - virtual_addr;
++vma; ++vma;
@ -96,6 +97,8 @@ u64 MemoryManager::ClampRangeSize(VAddr virtual_addr, u64 size) {
bool MemoryManager::TryWriteBacking(void* address, const void* data, u32 num_bytes) { bool MemoryManager::TryWriteBacking(void* address, const void* data, u32 num_bytes) {
const VAddr virtual_addr = std::bit_cast<VAddr>(address); const VAddr virtual_addr = std::bit_cast<VAddr>(address);
const auto& vma = FindVMA(virtual_addr)->second; const auto& vma = FindVMA(virtual_addr)->second;
ASSERT_MSG(vma.Contains(virtual_addr, 0),
"Attempting to access out of bounds memory at address {:#x}", virtual_addr);
if (vma.type != VMAType::Direct) { if (vma.type != VMAType::Direct) {
return false; return false;
} }
@ -142,19 +145,19 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size,
auto mapping_start = search_start > dmem_area->second.base auto mapping_start = search_start > dmem_area->second.base
? Common::AlignUp(search_start, alignment) ? Common::AlignUp(search_start, alignment)
: Common::AlignUp(dmem_area->second.base, alignment); : Common::AlignUp(dmem_area->second.base, alignment);
auto mapping_end = Common::AlignUp(mapping_start + size, alignment); auto mapping_end = mapping_start + size;
// Find the first free, large enough dmem area in the range. // Find the first free, large enough dmem area in the range.
while ((!dmem_area->second.is_free || dmem_area->second.GetEnd() < mapping_end) && while (!dmem_area->second.is_free || dmem_area->second.GetEnd() < mapping_end) {
dmem_area != dmem_map.end()) {
// The current dmem_area isn't suitable, move to the next one. // The current dmem_area isn't suitable, move to the next one.
dmem_area++; dmem_area++;
if (dmem_area == dmem_map.end()) {
break;
}
// Update local variables based on the new dmem_area // Update local variables based on the new dmem_area
mapping_start = search_start > dmem_area->second.base mapping_start = Common::AlignUp(dmem_area->second.base, alignment);
? Common::AlignUp(search_start, alignment) mapping_end = mapping_start + size;
: Common::AlignUp(dmem_area->second.base, alignment);
mapping_end = Common::AlignUp(mapping_start + size, alignment);
} }
if (dmem_area == dmem_map.end()) { if (dmem_area == dmem_map.end()) {
@ -174,7 +177,6 @@ void MemoryManager::Free(PAddr phys_addr, size_t size) {
std::scoped_lock lk{mutex}; std::scoped_lock lk{mutex};
auto dmem_area = CarveDmemArea(phys_addr, size); auto dmem_area = CarveDmemArea(phys_addr, size);
ASSERT(dmem_area != dmem_map.end() && dmem_area->second.size >= size);
// Release any dmem mappings that reference this physical block. // Release any dmem mappings that reference this physical block.
std::vector<std::pair<VAddr, u64>> remove_list; std::vector<std::pair<VAddr, u64>> remove_list;
@ -218,12 +220,18 @@ int MemoryManager::PoolReserve(void** out_addr, VAddr virtual_addr, size_t size,
vma = FindVMA(mapped_addr)->second; vma = FindVMA(mapped_addr)->second;
} }
const size_t remaining_size = vma.base + vma.size - mapped_addr; const size_t remaining_size = vma.base + vma.size - mapped_addr;
ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size); ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size,
"Memory region {:#x} to {:#x} is not large enough to reserve {:#x} to {:#x}",
vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size);
} }
// Find the first free area starting with provided virtual address. // Find the first free area starting with provided virtual address.
if (False(flags & MemoryMapFlags::Fixed)) { if (False(flags & MemoryMapFlags::Fixed)) {
mapped_addr = SearchFree(mapped_addr, size, alignment); mapped_addr = SearchFree(mapped_addr, size, alignment);
if (mapped_addr == -1) {
// No suitable memory areas to map to
return ORBIS_KERNEL_ERROR_ENOMEM;
}
} }
// Add virtual memory area // Add virtual memory area
@ -231,7 +239,7 @@ int MemoryManager::PoolReserve(void** out_addr, VAddr virtual_addr, size_t size,
auto& new_vma = new_vma_handle->second; auto& new_vma = new_vma_handle->second;
new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce); new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
new_vma.prot = MemoryProt::NoAccess; new_vma.prot = MemoryProt::NoAccess;
new_vma.name = ""; new_vma.name = "anon";
new_vma.type = VMAType::PoolReserved; new_vma.type = VMAType::PoolReserved;
MergeAdjacent(vma_map, new_vma_handle); MergeAdjacent(vma_map, new_vma_handle);
@ -249,19 +257,25 @@ int MemoryManager::Reserve(void** out_addr, VAddr virtual_addr, size_t size, Mem
// Fixed mapping means the virtual address must exactly match the provided one. // Fixed mapping means the virtual address must exactly match the provided one.
if (True(flags & MemoryMapFlags::Fixed)) { if (True(flags & MemoryMapFlags::Fixed)) {
auto& vma = FindVMA(mapped_addr)->second; auto vma = FindVMA(mapped_addr)->second;
// If the VMA is mapped, unmap the region first. // If the VMA is mapped, unmap the region first.
if (vma.IsMapped()) { if (vma.IsMapped()) {
UnmapMemoryImpl(mapped_addr, size); UnmapMemoryImpl(mapped_addr, size);
vma = FindVMA(mapped_addr)->second; vma = FindVMA(mapped_addr)->second;
} }
const size_t remaining_size = vma.base + vma.size - mapped_addr; const size_t remaining_size = vma.base + vma.size - mapped_addr;
ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size); ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size,
"Memory region {:#x} to {:#x} is not large enough to reserve {:#x} to {:#x}",
vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size);
} }
// Find the first free area starting with provided virtual address. // Find the first free area starting with provided virtual address.
if (False(flags & MemoryMapFlags::Fixed)) { if (False(flags & MemoryMapFlags::Fixed)) {
mapped_addr = SearchFree(mapped_addr, size, alignment); mapped_addr = SearchFree(mapped_addr, size, alignment);
if (mapped_addr == -1) {
// No suitable memory areas to map to
return ORBIS_KERNEL_ERROR_ENOMEM;
}
} }
// Add virtual memory area // Add virtual memory area
@ -269,7 +283,7 @@ int MemoryManager::Reserve(void** out_addr, VAddr virtual_addr, size_t size, Mem
auto& new_vma = new_vma_handle->second; auto& new_vma = new_vma_handle->second;
new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce); new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
new_vma.prot = MemoryProt::NoAccess; new_vma.prot = MemoryProt::NoAccess;
new_vma.name = ""; new_vma.name = "anon";
new_vma.type = VMAType::Reserved; new_vma.type = VMAType::Reserved;
MergeAdjacent(vma_map, new_vma_handle); MergeAdjacent(vma_map, new_vma_handle);
@ -290,7 +304,9 @@ int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot)
// This should return SCE_KERNEL_ERROR_ENOMEM but shouldn't normally happen. // This should return SCE_KERNEL_ERROR_ENOMEM but shouldn't normally happen.
const auto& vma = FindVMA(mapped_addr)->second; const auto& vma = FindVMA(mapped_addr)->second;
const size_t remaining_size = vma.base + vma.size - mapped_addr; const size_t remaining_size = vma.base + vma.size - mapped_addr;
ASSERT_MSG(!vma.IsMapped() && remaining_size >= size); ASSERT_MSG(!vma.IsMapped() && remaining_size >= size,
"Memory region {:#x} to {:#x} isn't free enough to map region {:#x} to {:#x}",
vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size);
// Perform the mapping. // Perform the mapping.
void* out_addr = impl.Map(mapped_addr, size, alignment, -1, false); void* out_addr = impl.Map(mapped_addr, size, alignment, -1, false);
@ -304,7 +320,10 @@ int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot)
new_vma.is_exec = false; new_vma.is_exec = false;
new_vma.phys_base = 0; new_vma.phys_base = 0;
rasterizer->MapMemory(mapped_addr, size); if (IsValidGpuMapping(mapped_addr, size)) {
rasterizer->MapMemory(mapped_addr, size);
}
return ORBIS_OK; return ORBIS_OK;
} }
@ -327,15 +346,34 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M
// Fixed mapping means the virtual address must exactly match the provided one. // Fixed mapping means the virtual address must exactly match the provided one.
if (True(flags & MemoryMapFlags::Fixed)) { if (True(flags & MemoryMapFlags::Fixed)) {
// This should return SCE_KERNEL_ERROR_ENOMEM but shouldn't normally happen. auto vma = FindVMA(mapped_addr)->second;
const auto& vma = FindVMA(mapped_addr)->second; size_t remaining_size = vma.base + vma.size - mapped_addr;
const size_t remaining_size = vma.base + vma.size - mapped_addr; // There's a possible edge case where we're mapping to a partially reserved range.
ASSERT_MSG(!vma.IsMapped() && remaining_size >= size); // To account for this, unmap any reserved areas within this mapping range first.
auto unmap_addr = mapped_addr;
auto unmap_size = size;
while (!vma.IsMapped() && unmap_addr < mapped_addr + size && remaining_size < size) {
auto unmapped = UnmapBytesFromEntry(unmap_addr, vma, unmap_size);
unmap_addr += unmapped;
unmap_size -= unmapped;
vma = FindVMA(unmap_addr)->second;
}
// This should return SCE_KERNEL_ERROR_ENOMEM but rarely happens.
vma = FindVMA(mapped_addr)->second;
remaining_size = vma.base + vma.size - mapped_addr;
ASSERT_MSG(!vma.IsMapped() && remaining_size >= size,
"Memory region {:#x} to {:#x} isn't free enough to map region {:#x} to {:#x}",
vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size);
} }
// Find the first free area starting with provided virtual address. // Find the first free area starting with provided virtual address.
if (False(flags & MemoryMapFlags::Fixed)) { if (False(flags & MemoryMapFlags::Fixed)) {
mapped_addr = SearchFree(mapped_addr, size, alignment); mapped_addr = SearchFree(mapped_addr, size, alignment);
if (mapped_addr == -1) {
// No suitable memory areas to map to
return ORBIS_KERNEL_ERROR_ENOMEM;
}
} }
// Perform the mapping. // Perform the mapping.
@ -355,7 +393,10 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M
if (type == VMAType::Flexible) { if (type == VMAType::Flexible) {
flexible_usage += size; flexible_usage += size;
} }
rasterizer->MapMemory(mapped_addr, size);
if (IsValidGpuMapping(mapped_addr, size)) {
rasterizer->MapMemory(mapped_addr, size);
}
return ORBIS_OK; return ORBIS_OK;
} }
@ -368,12 +409,18 @@ int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, Mem
// Find first free area to map the file. // Find first free area to map the file.
if (False(flags & MemoryMapFlags::Fixed)) { if (False(flags & MemoryMapFlags::Fixed)) {
mapped_addr = SearchFree(mapped_addr, size_aligned, 1); mapped_addr = SearchFree(mapped_addr, size_aligned, 1);
if (mapped_addr == -1) {
// No suitable memory areas to map to
return ORBIS_KERNEL_ERROR_ENOMEM;
}
} }
if (True(flags & MemoryMapFlags::Fixed)) { if (True(flags & MemoryMapFlags::Fixed)) {
const auto& vma = FindVMA(virtual_addr)->second; const auto& vma = FindVMA(virtual_addr)->second;
const size_t remaining_size = vma.base + vma.size - virtual_addr; const size_t remaining_size = vma.base + vma.size - virtual_addr;
ASSERT_MSG(!vma.IsMapped() && remaining_size >= size); ASSERT_MSG(!vma.IsMapped() && remaining_size >= size,
"Memory region {:#x} to {:#x} isn't free enough to map region {:#x} to {:#x}",
vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size);
} }
// Map the file. // Map the file.
@ -406,7 +453,9 @@ void MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) {
const auto start_in_vma = virtual_addr - vma_base_addr; const auto start_in_vma = virtual_addr - vma_base_addr;
const auto type = vma_base.type; const auto type = vma_base.type;
rasterizer->UnmapMemory(virtual_addr, size); if (IsValidGpuMapping(virtual_addr, size)) {
rasterizer->UnmapMemory(virtual_addr, size);
}
// Mark region as free and attempt to coalesce it with neighbours. // Mark region as free and attempt to coalesce it with neighbours.
const auto new_it = CarveVMA(virtual_addr, size); const auto new_it = CarveVMA(virtual_addr, size);
@ -446,7 +495,10 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma
if (type == VMAType::Flexible) { if (type == VMAType::Flexible) {
flexible_usage -= adjusted_size; flexible_usage -= adjusted_size;
} }
rasterizer->UnmapMemory(virtual_addr, adjusted_size);
if (IsValidGpuMapping(virtual_addr, adjusted_size)) {
rasterizer->UnmapMemory(virtual_addr, adjusted_size);
}
// Mark region as free and attempt to coalesce it with neighbours. // Mark region as free and attempt to coalesce it with neighbours.
const auto new_it = CarveVMA(virtual_addr, adjusted_size); const auto new_it = CarveVMA(virtual_addr, adjusted_size);
@ -473,6 +525,8 @@ s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, u64 size) {
do { do {
auto it = FindVMA(virtual_addr + unmapped_bytes); auto it = FindVMA(virtual_addr + unmapped_bytes);
auto& vma_base = it->second; auto& vma_base = it->second;
ASSERT_MSG(vma_base.Contains(virtual_addr + unmapped_bytes, 0),
"Address {:#x} is out of bounds", virtual_addr + unmapped_bytes);
auto unmapped = auto unmapped =
UnmapBytesFromEntry(virtual_addr + unmapped_bytes, vma_base, size - unmapped_bytes); UnmapBytesFromEntry(virtual_addr + unmapped_bytes, vma_base, size - unmapped_bytes);
ASSERT_MSG(unmapped > 0, "Failed to unmap memory, progress is impossible"); ASSERT_MSG(unmapped > 0, "Failed to unmap memory, progress is impossible");
@ -487,7 +541,10 @@ int MemoryManager::QueryProtection(VAddr addr, void** start, void** end, u32* pr
const auto it = FindVMA(addr); const auto it = FindVMA(addr);
const auto& vma = it->second; const auto& vma = it->second;
ASSERT_MSG(vma.type != VMAType::Free, "Provided address is not mapped"); if (!vma.Contains(addr, 0) || vma.IsFree()) {
LOG_ERROR(Kernel_Vmm, "Address {:#x} is not mapped", addr);
return ORBIS_KERNEL_ERROR_EACCES;
}
if (start != nullptr) { if (start != nullptr) {
*start = reinterpret_cast<void*>(vma.base); *start = reinterpret_cast<void*>(vma.base);
@ -557,6 +614,8 @@ s32 MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) {
do { do {
auto it = FindVMA(addr + protected_bytes); auto it = FindVMA(addr + protected_bytes);
auto& vma_base = it->second; auto& vma_base = it->second;
ASSERT_MSG(vma_base.Contains(addr + protected_bytes, 0), "Address {:#x} is out of bounds",
addr + protected_bytes);
auto result = 0; auto result = 0;
result = ProtectBytes(addr + protected_bytes, vma_base, size - protected_bytes, prot); result = ProtectBytes(addr + protected_bytes, vma_base, size - protected_bytes, prot);
if (result < 0) { if (result < 0) {
@ -573,8 +632,16 @@ int MemoryManager::VirtualQuery(VAddr addr, int flags,
::Libraries::Kernel::OrbisVirtualQueryInfo* info) { ::Libraries::Kernel::OrbisVirtualQueryInfo* info) {
std::scoped_lock lk{mutex}; std::scoped_lock lk{mutex};
auto it = FindVMA(addr); // FindVMA on addresses before the vma_map return garbage data.
if (it->second.type == VMAType::Free && flags == 1) { auto query_addr =
addr < impl.SystemManagedVirtualBase() ? impl.SystemManagedVirtualBase() : addr;
if (addr < query_addr && flags == 0) {
LOG_WARNING(Kernel_Vmm, "VirtualQuery on free memory region");
return ORBIS_KERNEL_ERROR_EACCES;
}
auto it = FindVMA(query_addr);
while (it->second.type == VMAType::Free && flags == 1 && it != --vma_map.end()) {
++it; ++it;
} }
if (it->second.type == VMAType::Free) { if (it->second.type == VMAType::Free) {
@ -587,15 +654,17 @@ int MemoryManager::VirtualQuery(VAddr addr, int flags,
info->end = vma.base + vma.size; info->end = vma.base + vma.size;
info->offset = vma.phys_base; info->offset = vma.phys_base;
info->protection = static_cast<s32>(vma.prot); info->protection = static_cast<s32>(vma.prot);
info->is_flexible.Assign(vma.type == VMAType::Flexible); info->is_flexible = vma.type == VMAType::Flexible ? 1 : 0;
info->is_direct.Assign(vma.type == VMAType::Direct); info->is_direct = vma.type == VMAType::Direct ? 1 : 0;
info->is_stack.Assign(vma.type == VMAType::Stack); info->is_stack = vma.type == VMAType::Stack ? 1 : 0;
info->is_pooled.Assign(vma.type == VMAType::PoolReserved || vma.type == VMAType::Pooled); info->is_pooled = vma.type == VMAType::PoolReserved || vma.type == VMAType::Pooled ? 1 : 0;
info->is_committed.Assign(vma.IsMapped()); info->is_committed = vma.IsMapped() ? 1 : 0;
vma.name.copy(info->name.data(), std::min(info->name.size(), vma.name.size()));
strncpy(info->name, vma.name.data(), ::Libraries::Kernel::ORBIS_KERNEL_MAXIMUM_NAME_LENGTH);
if (vma.type == VMAType::Direct) { if (vma.type == VMAType::Direct) {
const auto dmem_it = FindDmemArea(vma.phys_base); const auto dmem_it = FindDmemArea(vma.phys_base);
ASSERT(dmem_it != dmem_map.end()); ASSERT_MSG(vma.phys_base <= dmem_it->second.GetEnd(), "vma.phys_base is not in dmem_map!");
info->memory_type = dmem_it->second.memory_type; info->memory_type = dmem_it->second.memory_type;
} else { } else {
info->memory_type = ::Libraries::Kernel::SCE_KERNEL_WB_ONION; info->memory_type = ::Libraries::Kernel::SCE_KERNEL_WB_ONION;
@ -609,11 +678,11 @@ int MemoryManager::DirectMemoryQuery(PAddr addr, bool find_next,
std::scoped_lock lk{mutex}; std::scoped_lock lk{mutex};
auto dmem_area = FindDmemArea(addr); auto dmem_area = FindDmemArea(addr);
while (dmem_area != dmem_map.end() && dmem_area->second.is_free && find_next) { while (dmem_area != --dmem_map.end() && dmem_area->second.is_free && find_next) {
dmem_area++; dmem_area++;
} }
if (dmem_area == dmem_map.end() || dmem_area->second.is_free) { if (dmem_area->second.is_free) {
LOG_ERROR(Core, "Unable to find allocated direct memory region to query!"); LOG_ERROR(Core, "Unable to find allocated direct memory region to query!");
return ORBIS_KERNEL_ERROR_EACCES; return ORBIS_KERNEL_ERROR_EACCES;
} }
@ -693,36 +762,56 @@ VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment)
virtual_addr = min_search_address; virtual_addr = min_search_address;
} }
// If the requested address is beyond the maximum our code can handle, throw an assert
auto max_search_address = impl.UserVirtualBase() + impl.UserVirtualSize();
ASSERT_MSG(virtual_addr <= max_search_address, "Input address {:#x} is out of bounds",
virtual_addr);
auto it = FindVMA(virtual_addr); auto it = FindVMA(virtual_addr);
ASSERT_MSG(it != vma_map.end(), "Specified mapping address was not found!");
// If the VMA is free and contains the requested mapping we are done. // If the VMA is free and contains the requested mapping we are done.
if (it->second.IsFree() && it->second.Contains(virtual_addr, size)) { if (it->second.IsFree() && it->second.Contains(virtual_addr, size)) {
return virtual_addr; return virtual_addr;
} }
// Search for the first free VMA that fits our mapping. // Search for the first free VMA that fits our mapping.
const auto is_suitable = [&] { while (it != vma_map.end()) {
if (!it->second.IsFree()) { if (!it->second.IsFree()) {
return false; it++;
continue;
} }
const auto& vma = it->second; const auto& vma = it->second;
virtual_addr = Common::AlignUp(vma.base, alignment); virtual_addr = Common::AlignUp(vma.base, alignment);
// Sometimes the alignment itself might be larger than the VMA. // Sometimes the alignment itself might be larger than the VMA.
if (virtual_addr > vma.base + vma.size) { if (virtual_addr > vma.base + vma.size) {
return false; it++;
continue;
} }
// Make sure the address is within our defined bounds
if (virtual_addr >= max_search_address) {
// There are no free mappings within our safely usable address space.
break;
}
// If there's enough space in the VMA, return the address.
const size_t remaining_size = vma.base + vma.size - virtual_addr; const size_t remaining_size = vma.base + vma.size - virtual_addr;
return remaining_size >= size; if (remaining_size >= size) {
}; return virtual_addr;
while (!is_suitable()) { }
++it; it++;
} }
return virtual_addr;
// Couldn't find a suitable VMA, return an error.
LOG_ERROR(Kernel_Vmm, "Couldn't find a free mapping for address {:#x}, size {:#x}",
virtual_addr, size);
return -1;
} }
MemoryManager::VMAHandle MemoryManager::CarveVMA(VAddr virtual_addr, size_t size) { MemoryManager::VMAHandle MemoryManager::CarveVMA(VAddr virtual_addr, size_t size) {
auto vma_handle = FindVMA(virtual_addr); auto vma_handle = FindVMA(virtual_addr);
ASSERT_MSG(vma_handle != vma_map.end(), "Virtual address not in vm_map"); ASSERT_MSG(vma_handle->second.Contains(virtual_addr, 0), "Virtual address not in vm_map");
const VirtualMemoryArea& vma = vma_handle->second; const VirtualMemoryArea& vma = vma_handle->second;
ASSERT_MSG(vma.base <= virtual_addr, "Adding a mapping to already mapped region"); ASSERT_MSG(vma.base <= virtual_addr, "Adding a mapping to already mapped region");
@ -751,7 +840,7 @@ MemoryManager::VMAHandle MemoryManager::CarveVMA(VAddr virtual_addr, size_t size
MemoryManager::DMemHandle MemoryManager::CarveDmemArea(PAddr addr, size_t size) { MemoryManager::DMemHandle MemoryManager::CarveDmemArea(PAddr addr, size_t size) {
auto dmem_handle = FindDmemArea(addr); auto dmem_handle = FindDmemArea(addr);
ASSERT_MSG(dmem_handle != dmem_map.end(), "Physical address not in dmem_map"); ASSERT_MSG(addr <= dmem_handle->second.GetEnd(), "Physical address not in dmem_map");
const DirectMemoryArea& area = dmem_handle->second; const DirectMemoryArea& area = dmem_handle->second;
ASSERT_MSG(area.base <= addr, "Adding an allocation to already allocated region"); ASSERT_MSG(area.base <= addr, "Adding an allocation to already allocated region");
@ -806,7 +895,7 @@ int MemoryManager::GetDirectMemoryType(PAddr addr, int* directMemoryTypeOut,
auto dmem_area = FindDmemArea(addr); auto dmem_area = FindDmemArea(addr);
if (dmem_area == dmem_map.end() || dmem_area->second.is_free) { if (addr > dmem_area->second.GetEnd() || dmem_area->second.is_free) {
LOG_ERROR(Core, "Unable to find allocated direct memory region to check type!"); LOG_ERROR(Core, "Unable to find allocated direct memory region to check type!");
return ORBIS_KERNEL_ERROR_ENOENT; return ORBIS_KERNEL_ERROR_ENOENT;
} }

View File

@ -157,6 +157,12 @@ public:
return impl.SystemReservedVirtualBase(); 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 { bool IsValidAddress(const void* addr) const noexcept {
const VAddr virtual_addr = reinterpret_cast<VAddr>(addr); const VAddr virtual_addr = reinterpret_cast<VAddr>(addr);
const auto end_it = std::prev(vma_map.end()); const auto end_it = std::prev(vma_map.end());
@ -186,7 +192,7 @@ public:
int PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot); int PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot);
int MapMemory(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, int MapMemory(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot,
MemoryMapFlags flags, VMAType type, std::string_view name = "", MemoryMapFlags flags, VMAType type, std::string_view name = "anon",
bool is_exec = false, PAddr phys_addr = -1, u64 alignment = 0); bool is_exec = false, PAddr phys_addr = -1, u64 alignment = 0);
int MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, int MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot,

View File

@ -19,8 +19,7 @@ namespace Core {
using EntryFunc = PS4_SYSV_ABI int (*)(size_t args, const void* argp, void* param); using EntryFunc = PS4_SYSV_ABI int (*)(size_t args, const void* argp, void* param);
static u64 LoadOffset = CODE_BASE_OFFSET; static constexpr u64 ModuleLoadBase = 0x800000000;
static constexpr u64 CODE_BASE_INCR = 0x010000000u;
static u64 GetAlignedSize(const elf_program_header& phdr) { static u64 GetAlignedSize(const elf_program_header& phdr) {
return (phdr.p_align != 0 ? (phdr.p_memsz + (phdr.p_align - 1)) & ~(phdr.p_align - 1) return (phdr.p_align != 0 ? (phdr.p_memsz + (phdr.p_align - 1)) & ~(phdr.p_align - 1)
@ -84,7 +83,7 @@ static std::string StringToNid(std::string_view symbol) {
} }
Module::Module(Core::MemoryManager* memory_, const std::filesystem::path& file_, u32& max_tls_index) Module::Module(Core::MemoryManager* memory_, const std::filesystem::path& file_, u32& max_tls_index)
: memory{memory_}, file{file_}, name{file.stem().string()} { : memory{memory_}, file{file_}, name{file.filename().string()} {
elf.Open(file); elf.Open(file);
if (elf.IsElfFile()) { if (elf.IsElfFile()) {
LoadModuleToMemory(max_tls_index); LoadModuleToMemory(max_tls_index);
@ -113,10 +112,8 @@ void Module::LoadModuleToMemory(u32& max_tls_index) {
// Map module segments (and possible TLS trampolines) // Map module segments (and possible TLS trampolines)
void** out_addr = reinterpret_cast<void**>(&base_virtual_addr); void** out_addr = reinterpret_cast<void**>(&base_virtual_addr);
memory->MapMemory(out_addr, memory->SystemReservedVirtualBase() + LoadOffset, memory->MapMemory(out_addr, ModuleLoadBase, aligned_base_size + TrampolineSize,
aligned_base_size + TrampolineSize, MemoryProt::CpuReadWrite, MemoryProt::CpuReadWrite, MemoryMapFlags::NoFlags, VMAType::Code, name, true);
MemoryMapFlags::Fixed, VMAType::Code, name, true);
LoadOffset += CODE_BASE_INCR * (1 + aligned_base_size / CODE_BASE_INCR);
LOG_INFO(Core_Linker, "Loading module {} to {}", name, fmt::ptr(*out_addr)); LOG_INFO(Core_Linker, "Loading module {} to {}", name, fmt::ptr(*out_addr));
#ifdef ARCH_X86_64 #ifdef ARCH_X86_64
@ -135,10 +132,14 @@ void Module::LoadModuleToMemory(u32& max_tls_index) {
if (do_map) { if (do_map) {
elf.LoadSegment(segment_addr, phdr.p_offset, phdr.p_filesz); elf.LoadSegment(segment_addr, phdr.p_offset, phdr.p_filesz);
} }
auto& segment = info.segments[info.num_segments++]; if (info.num_segments < 4) {
segment.address = segment_addr; auto& segment = info.segments[info.num_segments++];
segment.prot = phdr.p_flags; segment.address = segment_addr;
segment.size = GetAlignedSize(phdr); segment.prot = phdr.p_flags;
segment.size = GetAlignedSize(phdr);
} else {
LOG_ERROR(Core_Linker, "Attempting to add too many segments!");
}
}; };
for (u16 i = 0; i < elf_header.e_phnum; i++) { for (u16 i = 0; i < elf_header.e_phnum; i++) {
@ -225,7 +226,7 @@ void Module::LoadModuleToMemory(u32& max_tls_index) {
LOG_INFO(Core_Linker, "program entry addr ..........: {:#018x}", entry_addr); LOG_INFO(Core_Linker, "program entry addr ..........: {:#018x}", entry_addr);
if (MemoryPatcher::g_eboot_address == 0) { if (MemoryPatcher::g_eboot_address == 0) {
if (name == "eboot") { if (name == "eboot.bin") {
MemoryPatcher::g_eboot_address = base_virtual_addr; MemoryPatcher::g_eboot_address = base_virtual_addr;
MemoryPatcher::g_eboot_image_size = base_size; MemoryPatcher::g_eboot_image_size = base_size;
MemoryPatcher::OnGameLoaded(); MemoryPatcher::OnGameLoaded();

View File

@ -5,6 +5,8 @@
#include "common/types.h" #include "common/types.h"
void* memset(void* ptr, int value, size_t num);
namespace Xbyak { namespace Xbyak {
class CodeGenerator; class CodeGenerator;
} }
@ -41,9 +43,18 @@ Tcb* GetTcbBase();
/// Makes sure TLS is initialized for the thread before entering guest. /// Makes sure TLS is initialized for the thread before entering guest.
void EnsureThreadInitialized(); void EnsureThreadInitialized();
template <size_t size>
__attribute__((optnone)) void ClearStack() {
volatile void* buf = alloca(size);
memset(const_cast<void*>(buf), 0, size);
buf = nullptr;
}
template <class ReturnType, class... FuncArgs, class... CallArgs> template <class ReturnType, class... FuncArgs, class... CallArgs>
ReturnType ExecuteGuest(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), CallArgs&&... args) { ReturnType ExecuteGuest(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), CallArgs&&... args) {
EnsureThreadInitialized(); EnsureThreadInitialized();
// clear stack to avoid trash from EnsureThreadInitialized
ClearStack<13_KB>();
return func(std::forward<CallArgs>(args)...); return func(std::forward<CallArgs>(args)...);
} }

View File

@ -10,13 +10,16 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#ifdef ENABLE_QT_GUI #ifdef ENABLE_QT_GUI
#include <QtCore> #include <QtCore>
#include "common/memory_patcher.h"
#endif #endif
#include "common/assert.h" #include "common/assert.h"
#ifdef ENABLE_DISCORD_RPC #ifdef ENABLE_DISCORD_RPC
#include "common/discord_rpc_handler.h" #include "common/discord_rpc_handler.h"
#endif #endif
#ifdef _WIN32
#include <WinSock2.h>
#endif
#include "common/elf_info.h" #include "common/elf_info.h"
#include "common/memory_patcher.h"
#include "common/ntapi.h" #include "common/ntapi.h"
#include "common/path_util.h" #include "common/path_util.h"
#include "common/polyfill_thread.h" #include "common/polyfill_thread.h"
@ -46,27 +49,10 @@ Emulator::Emulator() {
#ifdef _WIN32 #ifdef _WIN32
Common::NtApi::Initialize(); Common::NtApi::Initialize();
SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS); SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
#endif // need to init this in order for winsock2 to work
WORD versionWanted = MAKEWORD(2, 2);
// Create stdin/stdout/stderr WSADATA wsaData;
Common::Singleton<FileSys::HandleTable>::Instance()->CreateStdHandles(); WSAStartup(versionWanted, &wsaData);
// Defer until after logging is initialized.
memory = Core::Memory::Instance();
controller = Common::Singleton<Input::GameController>::Instance();
linker = Common::Singleton<Core::Linker>::Instance();
// Load renderdoc module.
VideoCore::LoadRenderDoc();
// Start the timer (Play Time)
#ifdef ENABLE_QT_GUI
start_time = std::chrono::steady_clock::now();
const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
QString filePath = QString::fromStdString((user_dir / "play_time.txt").string());
QFile file(filePath);
ASSERT_MSG(file.open(QIODevice::ReadWrite | QIODevice::Text),
"Error opening or creating play_time.txt");
#endif #endif
} }
@ -91,58 +77,93 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector<std::str
// Applications expect to be run from /app0 so mount the file's parent path as app0. // Applications expect to be run from /app0 so mount the file's parent path as app0.
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance(); auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
mnt->Mount(game_folder, "/app0"); mnt->Mount(game_folder, "/app0", true);
// Certain games may use /hostapp as well such as CUSA001100 // Certain games may use /hostapp as well such as CUSA001100
mnt->Mount(game_folder, "/hostapp"); mnt->Mount(game_folder, "/hostapp", true);
auto& game_info = Common::ElfInfo::Instance(); const auto param_sfo_path = mnt->GetHostPath("/app0/sce_sys/param.sfo");
const auto param_sfo_exists = std::filesystem::exists(param_sfo_path);
// Loading param.sfo file if exists // Load param.sfo details if it exists
std::string id; std::string id;
std::string title; std::string title;
std::string app_version; std::string app_version;
u32 fw_version; u32 fw_version;
Common::PSFAttributes psf_attributes{}; Common::PSFAttributes psf_attributes{};
if (param_sfo_exists) {
const auto param_sfo_path = mnt->GetHostPath("/app0/sce_sys/param.sfo");
if (!std::filesystem::exists(param_sfo_path) || !Config::getSeparateLogFilesEnabled()) {
Common::Log::Initialize();
Common::Log::Start();
}
if (std::filesystem::exists(param_sfo_path)) {
auto* param_sfo = Common::Singleton<PSF>::Instance(); auto* param_sfo = Common::Singleton<PSF>::Instance();
const bool success = param_sfo->Open(param_sfo_path); ASSERT_MSG(param_sfo->Open(param_sfo_path), "Failed to open param.sfo");
ASSERT_MSG(success, "Failed to open param.sfo");
const auto content_id = param_sfo->GetString("CONTENT_ID"); const auto content_id = param_sfo->GetString("CONTENT_ID");
ASSERT_MSG(content_id.has_value(), "Failed to get CONTENT_ID"); ASSERT_MSG(content_id.has_value(), "Failed to get CONTENT_ID");
id = std::string(*content_id, 7, 9); id = std::string(*content_id, 7, 9);
title = param_sfo->GetString("TITLE").value_or("Unknown title");
if (Config::getSeparateLogFilesEnabled()) { fw_version = param_sfo->GetInteger("SYSTEM_VER").value_or(0x4700000);
Common::Log::Initialize(id + ".log"); app_version = param_sfo->GetString("APP_VER").value_or("Unknown version");
Common::Log::Start(); if (const auto raw_attributes = param_sfo->GetInteger("ATTRIBUTE")) {
psf_attributes.raw = *raw_attributes;
} }
LOG_INFO(Loader, "Starting shadps4 emulator v{} ", Common::g_version); }
LOG_INFO(Loader, "Revision {}", Common::g_scm_rev);
LOG_INFO(Loader, "Branch {}", Common::g_scm_branch);
LOG_INFO(Loader, "Description {}", Common::g_scm_desc);
LOG_INFO(Loader, "Remote {}", Common::g_scm_remote_url);
LOG_INFO(Config, "General LogType: {}", Config::getLogType()); // Initialize logging as soon as possible
LOG_INFO(Config, "General isNeo: {}", Config::isNeoModeConsole()); if (!id.empty() && Config::getSeparateLogFilesEnabled()) {
LOG_INFO(Config, "GPU isNullGpu: {}", Config::nullGpu()); Common::Log::Initialize(id + ".log");
LOG_INFO(Config, "GPU shouldDumpShaders: {}", Config::dumpShaders()); } else {
LOG_INFO(Config, "GPU vblankDivider: {}", Config::vblankDiv()); Common::Log::Initialize();
LOG_INFO(Config, "Vulkan gpuId: {}", Config::getGpuId()); }
LOG_INFO(Config, "Vulkan vkValidation: {}", Config::vkValidationEnabled()); Common::Log::Start();
LOG_INFO(Config, "Vulkan vkValidationSync: {}", Config::vkValidationSyncEnabled());
LOG_INFO(Config, "Vulkan vkValidationGpu: {}", Config::vkValidationGpuEnabled());
LOG_INFO(Config, "Vulkan crashDiagnostics: {}", Config::getVkCrashDiagnosticEnabled());
LOG_INFO(Config, "Vulkan hostMarkers: {}", Config::getVkHostMarkersEnabled());
LOG_INFO(Config, "Vulkan guestMarkers: {}", Config::getVkGuestMarkersEnabled());
LOG_INFO(Config, "Vulkan rdocEnable: {}", Config::isRdocEnabled());
LOG_INFO(Loader, "Starting shadps4 emulator v{} ", Common::g_version);
LOG_INFO(Loader, "Revision {}", Common::g_scm_rev);
LOG_INFO(Loader, "Branch {}", Common::g_scm_branch);
LOG_INFO(Loader, "Description {}", Common::g_scm_desc);
LOG_INFO(Loader, "Remote {}", Common::g_scm_remote_url);
LOG_INFO(Config, "General LogType: {}", Config::getLogType());
LOG_INFO(Config, "General isNeo: {}", Config::isNeoModeConsole());
LOG_INFO(Config, "GPU isNullGpu: {}", Config::nullGpu());
LOG_INFO(Config, "GPU shouldDumpShaders: {}", Config::dumpShaders());
LOG_INFO(Config, "GPU vblankDivider: {}", Config::vblankDiv());
LOG_INFO(Config, "Vulkan gpuId: {}", Config::getGpuId());
LOG_INFO(Config, "Vulkan vkValidation: {}", Config::vkValidationEnabled());
LOG_INFO(Config, "Vulkan vkValidationSync: {}", Config::vkValidationSyncEnabled());
LOG_INFO(Config, "Vulkan vkValidationGpu: {}", Config::vkValidationGpuEnabled());
LOG_INFO(Config, "Vulkan crashDiagnostics: {}", Config::getVkCrashDiagnosticEnabled());
LOG_INFO(Config, "Vulkan hostMarkers: {}", Config::getVkHostMarkersEnabled());
LOG_INFO(Config, "Vulkan guestMarkers: {}", Config::getVkGuestMarkersEnabled());
LOG_INFO(Config, "Vulkan rdocEnable: {}", Config::isRdocEnabled());
if (param_sfo_exists) {
LOG_INFO(Loader, "Game id: {} Title: {}", id, title);
LOG_INFO(Loader, "Fw: {:#x} App Version: {}", fw_version, app_version);
}
if (!args.empty()) {
const auto argc = std::min<size_t>(args.size(), 32);
for (auto i = 0; i < argc; i++) {
LOG_INFO(Loader, "Game argument {}: {}", i, args[i]);
}
if (args.size() > 32) {
LOG_ERROR(Loader, "Too many game arguments, only passing the first 32");
}
}
// Create stdin/stdout/stderr
Common::Singleton<FileSys::HandleTable>::Instance()->CreateStdHandles();
// Initialize components
memory = Core::Memory::Instance();
controller = Common::Singleton<Input::GameController>::Instance();
linker = Common::Singleton<Core::Linker>::Instance();
// Load renderdoc module
VideoCore::LoadRenderDoc();
// Initialize patcher and trophies
if (!id.empty()) {
MemoryPatcher::g_game_serial = id;
Libraries::NpTrophy::game_serial = id; Libraries::NpTrophy::game_serial = id;
const auto trophyDir = const auto trophyDir =
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / id / "TrophyFiles"; Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / id / "TrophyFiles";
if (!std::filesystem::exists(trophyDir)) { if (!std::filesystem::exists(trophyDir)) {
@ -151,41 +172,9 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector<std::str
LOG_ERROR(Loader, "Couldn't extract trophies"); LOG_ERROR(Loader, "Couldn't extract trophies");
} }
} }
#ifdef ENABLE_QT_GUI
MemoryPatcher::g_game_serial = id;
// Timer for 'Play Time'
QTimer* timer = new QTimer();
QObject::connect(timer, &QTimer::timeout, [this, id]() {
UpdatePlayTime(id);
start_time = std::chrono::steady_clock::now();
});
timer->start(60000); // 60000 ms = 1 minute
#endif
title = param_sfo->GetString("TITLE").value_or("Unknown title");
LOG_INFO(Loader, "Game id: {} Title: {}", id, title);
fw_version = param_sfo->GetInteger("SYSTEM_VER").value_or(0x4700000);
app_version = param_sfo->GetString("APP_VER").value_or("Unknown version");
LOG_INFO(Loader, "Fw: {:#x} App Version: {}", fw_version, app_version);
if (const auto raw_attributes = param_sfo->GetInteger("ATTRIBUTE")) {
psf_attributes.raw = *raw_attributes;
}
if (!args.empty()) {
int argc = std::min<int>(args.size(), 32);
for (int i = 0; i < argc; i++) {
LOG_INFO(Loader, "Game argument {}: {}", i, args[i]);
}
if (args.size() > 32) {
LOG_ERROR(Loader, "Too many game arguments, only passing the first 32");
}
}
}
const auto pic1_path = mnt->GetHostPath("/app0/sce_sys/pic1.png");
if (std::filesystem::exists(pic1_path)) {
game_info.splash_path = pic1_path;
} }
auto& game_info = Common::ElfInfo::Instance();
game_info.initialized = true; game_info.initialized = true;
game_info.game_serial = id; game_info.game_serial = id;
game_info.title = title; game_info.title = title;
@ -194,6 +183,11 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector<std::str
game_info.raw_firmware_ver = fw_version; game_info.raw_firmware_ver = fw_version;
game_info.psf_attributes = psf_attributes; game_info.psf_attributes = psf_attributes;
const auto pic1_path = mnt->GetHostPath("/app0/sce_sys/pic1.png");
if (std::filesystem::exists(pic1_path)) {
game_info.splash_path = pic1_path;
}
std::string game_title = fmt::format("{} - {} <{}>", id, title, app_version); std::string game_title = fmt::format("{} - {} <{}>", id, title, app_version);
std::string window_title = ""; std::string window_title = "";
if (Common::g_is_release) { if (Common::g_is_release) {
@ -224,11 +218,15 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector<std::str
std::filesystem::create_directory(mount_data_dir); std::filesystem::create_directory(mount_data_dir);
} }
mnt->Mount(mount_data_dir, "/data"); // should just exist, manually create with game serial mnt->Mount(mount_data_dir, "/data"); // should just exist, manually create with game serial
// Mounting temp folders
const auto& mount_temp_dir = Common::FS::GetUserPath(Common::FS::PathType::TempDataDir) / id; const auto& mount_temp_dir = Common::FS::GetUserPath(Common::FS::PathType::TempDataDir) / id;
if (!std::filesystem::exists(mount_temp_dir)) { if (std::filesystem::exists(mount_temp_dir)) {
std::filesystem::create_directory(mount_temp_dir); // Temp folder should be cleared on each boot.
std::filesystem::remove_all(mount_temp_dir);
} }
mnt->Mount(mount_temp_dir, "/temp0"); // called in app_content ==> stat/mkdir std::filesystem::create_directory(mount_temp_dir);
mnt->Mount(mount_temp_dir, "/temp0");
mnt->Mount(mount_temp_dir, "/temp"); mnt->Mount(mount_temp_dir, "/temp");
const auto& mount_download_dir = const auto& mount_download_dir =
@ -273,6 +271,25 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector<std::str
} }
#endif #endif
// Start the timer (Play Time)
#ifdef ENABLE_QT_GUI
if (!id.empty()) {
auto* timer = new QTimer();
QObject::connect(timer, &QTimer::timeout, [this, id]() {
UpdatePlayTime(id);
start_time = std::chrono::steady_clock::now();
});
timer->start(60000); // 60000 ms = 1 minute
start_time = std::chrono::steady_clock::now();
const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
QString filePath = QString::fromStdString((user_dir / "play_time.txt").string());
QFile file(filePath);
ASSERT_MSG(file.open(QIODevice::ReadWrite | QIODevice::Text),
"Error opening or creating play_time.txt");
}
#endif
linker->Execute(args); linker->Execute(args);
window->InitTimers(); window->InitTimers();

View File

@ -4,8 +4,11 @@
#pragma once #pragma once
#include <filesystem> #include <filesystem>
#include <vector>
#include <imgui.h> #include <imgui.h>
#include "common/types.h"
namespace ImGui { namespace ImGui {
namespace Core::TextureManager { namespace Core::TextureManager {

View File

@ -112,6 +112,8 @@ void Initialize(const ::Vulkan::Instance& instance, const Frontend::WindowSDL& w
if (const auto dpi = SDL_GetWindowDisplayScale(window.GetSDLWindow()); dpi > 0.0f) { if (const auto dpi = SDL_GetWindowDisplayScale(window.GetSDLWindow()); dpi > 0.0f) {
GetIO().FontGlobalScale = dpi; GetIO().FontGlobalScale = dpi;
} }
std::at_quick_exit([] { SaveIniSettingsToDisk(GetIO().IniFilename); });
} }
void OnResize() { void OnResize() {

View File

@ -154,7 +154,7 @@ int main(int argc, char* argv[]) {
// If no game directory is set and no command line argument, prompt for it // If no game directory is set and no command line argument, prompt for it
if (Config::getGameInstallDirs().empty()) { if (Config::getGameInstallDirs().empty()) {
std::cout << "Warning: No game folder set, please set it by calling shadps4" std::cout << "Warning: No game folder set, please set it by calling shadps4"
" with the --add-game-folder <folder_name> argument"; " with the --add-game-folder <folder_name> argument\n";
} }
if (!has_game_argument) { if (!has_game_argument) {

View File

@ -1387,7 +1387,7 @@ void CheatsPatches::applyPatch(const QString& patchName, bool enabled) {
if (type == "mask_jump32") if (type == "mask_jump32")
patchMask = MemoryPatcher::PatchMask::Mask_Jump32; patchMask = MemoryPatcher::PatchMask::Mask_Jump32;
if (type == "mask" || type == "mask_jump32" && !maskOffsetStr.toStdString().empty()) { if ((type == "mask" || type == "mask_jump32") && !maskOffsetStr.toStdString().empty()) {
maskOffsetValue = std::stoi(maskOffsetStr.toStdString(), 0, 10); maskOffsetValue = std::stoi(maskOffsetStr.toStdString(), 0, 10);
} }

View File

@ -106,8 +106,6 @@ public:
toggleLabelsAct = new QAction(MainWindow); toggleLabelsAct = new QAction(MainWindow);
toggleLabelsAct->setObjectName("toggleLabelsAct"); toggleLabelsAct->setObjectName("toggleLabelsAct");
toggleLabelsAct->setText(
QCoreApplication::translate("MainWindow", "Show Labels Under Icons"));
toggleLabelsAct->setCheckable(true); toggleLabelsAct->setCheckable(true);
toggleLabelsAct->setChecked(Config::getShowLabelsUnderIcons()); toggleLabelsAct->setChecked(Config::getShowLabelsUnderIcons());
@ -413,6 +411,8 @@ public:
setThemeTokyoNight->setText("Tokyo Night"); setThemeTokyoNight->setText("Tokyo Night");
setThemeOled->setText("OLED"); setThemeOled->setText("OLED");
toolBar->setWindowTitle(QCoreApplication::translate("MainWindow", "toolBar", nullptr)); toolBar->setWindowTitle(QCoreApplication::translate("MainWindow", "toolBar", nullptr));
toggleLabelsAct->setText(
QCoreApplication::translate("MainWindow", "Show Labels Under Icons"));
} // retranslateUi } // retranslateUi
}; };

View File

@ -66,7 +66,7 @@
</message> </message>
<message> <message>
<source>You can delete the cheats you don&apos;t want after downloading them.</source> <source>You can delete the cheats you don&apos;t want after downloading them.</source>
<translation>يمكنك حذف الشفرات التي لا تريدها بعد تنزيلها.</translation> <translation>يمكنك حذف الشفرات التي لا &apos;تريدها بعد تنزيلها.</translation>
</message> </message>
<message> <message>
<source>Do you want to delete the selected file?\n%1</source> <source>Do you want to delete the selected file?\n%1</source>
@ -74,11 +74,11 @@
</message> </message>
<message> <message>
<source>Select Patch File:</source> <source>Select Patch File:</source>
<translation>إختر ملف الباتش:</translation> <translation>اختر مِلَف التصحيح:</translation>
</message> </message>
<message> <message>
<source>Download Patches</source> <source>Download Patches</source>
<translation>تحميل الباتشات</translation> <translation>تحميل ملفات التصحيح</translation>
</message> </message>
<message> <message>
<source>Save</source> <source>Save</source>
@ -98,15 +98,15 @@
</message> </message>
<message> <message>
<source>No patch selected.</source> <source>No patch selected.</source>
<translation>لم يتم اختيار أي تصحيح.</translation> <translation>لم يتم تحديد أي مِلَف تصحيح.</translation>
</message> </message>
<message> <message>
<source>Unable to open files.json for reading.</source> <source>Unable to open files.json for reading.</source>
<translation>تعذر فتح files.json للقراءة.</translation> <translation>تعذّر فتح مِلَف files.json للقراءة.</translation>
</message> </message>
<message> <message>
<source>No patch file found for the current serial.</source> <source>No patch file found for the current serial.</source>
<translation>لم يتم العثور على مِلَفّ باتش للسيريال الحالي.</translation> <translation>لم يتم العثور على مِلَف تصحيح للسيريال الحالي.</translation>
</message> </message>
<message> <message>
<source>Unable to open the file for reading.</source> <source>Unable to open the file for reading.</source>
@ -126,11 +126,11 @@
</message> </message>
<message> <message>
<source>Options saved successfully.</source> <source>Options saved successfully.</source>
<translation>تم حفظ الخيارات بنجاح.</translation> <translation>تم حفظ الإعدادات.</translation>
</message> </message>
<message> <message>
<source>Invalid Source</source> <source>Invalid Source</source>
<translation>مصدر غير صالح</translation> <translation>المصدر غير صالح</translation>
</message> </message>
<message> <message>
<source>The selected source is invalid.</source> <source>The selected source is invalid.</source>
@ -138,11 +138,11 @@
</message> </message>
<message> <message>
<source>File Exists</source> <source>File Exists</source>
<translation>الملف موجود</translation> <translation>المِلَف موجود مسبقًا</translation>
</message> </message>
<message> <message>
<source>File already exists. Do you want to replace it?</source> <source>File already exists. Do you want to replace it?</source>
<translation>يوجد ملف بنفس الاسم. هل ترغب في استبداله؟</translation> <translation>المِلَف موجود مسبقًا. هل ترغب في استبداله؟</translation>
</message> </message>
<message> <message>
<source>Failed to save file:</source> <source>Failed to save file:</source>
@ -158,7 +158,7 @@
</message> </message>
<message> <message>
<source>No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game.</source> <source>No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game.</source>
<translation>لم يتم العثور على شفرات لهذه اللعبة في هذه النسخة من المستودع المحدد. حاول استخدام مستودع آخر أو نسخة مختلفة من اللعبة.</translation> <translation>لم يتم العثور على شفرات لهذه اللعبة في هذا الإصدار من المستودع المحدد. جرّب مستودعًا آخر أو إصدارًا مختلفًا من اللعبة.</translation>
</message> </message>
<message> <message>
<source>Cheats Downloaded Successfully</source> <source>Cheats Downloaded Successfully</source>
@ -182,7 +182,7 @@
</message> </message>
<message> <message>
<source>Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. If the patch does not appear, it may be that it does not exist for the specific serial and version of the game.</source> <source>Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. If the patch does not appear, it may be that it does not exist for the specific serial and version of the game.</source>
<translation>تم تنزيل التصحيحات بنجاح! تم تنزيل جميع التصحيحات لجميع الألعاب، ولا داعي لتنزيلها بشكل فردي لكل لعبة كما هو الحال مع الغش. إذا لم يظهر التحديث، قد يكون السبب أنه غير متوفر للإصدار وسيريال اللعبة المحدد.</translation> <translation>تم تنزيل التصحيحات بنجاح! تم تنزيل جميع التصحيحات المتوفرة لجميع الألعاب، ولا حاجة إلى تنزيلها بشكل فردي لكل لعبة كما هو الحال مع الشفرات. إذا لم يظهر التصحيح، فقد لا يكون متوفرًا للسيريال أو الإصدار المحدد من اللعبة.</translation>
</message> </message>
<message> <message>
<source>Failed to parse JSON data from HTML.</source> <source>Failed to parse JSON data from HTML.</source>
@ -190,15 +190,15 @@
</message> </message>
<message> <message>
<source>Failed to retrieve HTML page.</source> <source>Failed to retrieve HTML page.</source>
<translation>.HTML فشل في استرجاع صفحة</translation> <translation>فشل في جلب صفحة HTML.</translation>
</message> </message>
<message> <message>
<source>The game is in version: %1</source> <source>The game is in version: %1</source>
<translation>النسخة الحالية للعبة هي: %1</translation> <translation>إصدار اللعبة الحالي: %1</translation>
</message> </message>
<message> <message>
<source>The downloaded patch only works on version: %1</source> <source>The downloaded patch only works on version: %1</source>
<translation>الباتش الذي تم تنزيله يعمل فقط على الإصدار: %1</translation> <translation>التصحيح الذي تم تنزيله يعمل فقط مع الإصدار:%1</translation>
</message> </message>
<message> <message>
<source>You may need to update your game.</source> <source>You may need to update your game.</source>
@ -206,7 +206,7 @@
</message> </message>
<message> <message>
<source>Incompatibility Notice</source> <source>Incompatibility Notice</source>
<translation>إشعار عدم التوافق</translation> <translation>إشعار بعدم التوافق</translation>
</message> </message>
<message> <message>
<source>Failed to open file:</source> <source>Failed to open file:</source>
@ -238,7 +238,7 @@
</message> </message>
<message> <message>
<source>Can&apos;t apply cheats before the game is started</source> <source>Can&apos;t apply cheats before the game is started</source>
<translation>لا يمكن تطبيق الغش قبل بدء اللعبة.</translation> <translation>لا &apos;يمكن تطبيق الشفرات قبل بَدْء اللعبة</translation>
</message> </message>
<message> <message>
<source>Close</source> <source>Close</source>
@ -249,7 +249,7 @@
<name>CheckUpdate</name> <name>CheckUpdate</name>
<message> <message>
<source>Auto Updater</source> <source>Auto Updater</source>
<translation>محدث تلقائي</translation> <translation>التحديثات التلقائية</translation>
</message> </message>
<message> <message>
<source>Error</source> <source>Error</source>
@ -261,7 +261,7 @@
</message> </message>
<message> <message>
<source>The Auto Updater allows up to 60 update checks per hour.\nYou have reached this limit. Please try again later.</source> <source>The Auto Updater allows up to 60 update checks per hour.\nYou have reached this limit. Please try again later.</source>
<translation>يتيح التحديث التلقائي ما يصل إلى 60 عملية تحقق من التحديث في الساعة.\nلقد وصلت إلى هذا الحد. الرجاء المحاولة مرة أخرى لاحقًا.</translation> <translation>تسمح التحديثات التلقائية بـ 60 عملية تحقق من التحديث في الساعة.\nلقد وصلت إلى الحد المسموح به. الرجاء المحاولة لاحقًا.</translation>
</message> </message>
<message> <message>
<source>Failed to parse update information.</source> <source>Failed to parse update information.</source>

View File

@ -384,23 +384,23 @@
</message> </message>
<message> <message>
<source>Nothing</source> <source>Nothing</source>
<translation>Không có </translation> <translation>Không chạy đưc</translation>
</message> </message>
<message> <message>
<source>Boots</source> <source>Boots</source>
<translation>Giày ng</translation> <translation>Chạy đưc</translation>
</message> </message>
<message> <message>
<source>Menus</source> <source>Menus</source>
<translation>Menu</translation> <translation>Vào đưc menu</translation>
</message> </message>
<message> <message>
<source>Ingame</source> <source>Ingame</source>
<translation>Trong game</translation> <translation>Vào đưc trò chơi</translation>
</message> </message>
<message> <message>
<source>Playable</source> <source>Playable</source>
<translation>Có thể chơi</translation> <translation>Chơi đưc</translation>
</message> </message>
</context> </context>
<context> <context>
@ -411,7 +411,7 @@
</message> </message>
<message> <message>
<source>D-Pad</source> <source>D-Pad</source>
<translation type="unfinished">D-Pad</translation> <translation>D-Pad</translation>
</message> </message>
<message> <message>
<source>Up</source> <source>Up</source>
@ -447,7 +447,7 @@
</message> </message>
<message> <message>
<source>Common Config</source> <source>Common Config</source>
<translation type="unfinished">Common Config</translation> <translation>Cài Đt Chung</translation>
</message> </message>
<message> <message>
<source>Use per-game configs</source> <source>Use per-game configs</source>
@ -551,26 +551,26 @@
</message> </message>
<message> <message>
<source>Save</source> <source>Save</source>
<translation type="unfinished">Save</translation> <translation>Lưu</translation>
</message> </message>
<message> <message>
<source>Apply</source> <source>Apply</source>
<translation type="unfinished">Apply</translation> <translation>Áp dụng</translation>
</message> </message>
<message> <message>
<source>Restore Defaults</source> <source>Restore Defaults</source>
<translation type="unfinished">Restore Defaults</translation> <translation>Khôi Phục Mặc Đnh</translation>
</message> </message>
<message> <message>
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished">Cancel</translation> <translation>Hủy</translation>
</message> </message>
</context> </context>
<context> <context>
<name>EditorDialog</name> <name>EditorDialog</name>
<message> <message>
<source>Edit Keyboard + Mouse and Controller input bindings</source> <source>Edit Keyboard + Mouse and Controller input bindings</source>
<translation type="unfinished">Edit Keyboard + Mouse and Controller input bindings</translation> <translation>Tùy chỉnh phím đưc gán cho Bàn phím + Chuột Tay cầm</translation>
</message> </message>
<message> <message>
<source>Use Per-Game configs</source> <source>Use Per-Game configs</source>
@ -578,7 +578,7 @@
</message> </message>
<message> <message>
<source>Error</source> <source>Error</source>
<translation type="unfinished">Error</translation> <translation>Lỗi</translation>
</message> </message>
<message> <message>
<source>Could not open the file for reading</source> <source>Could not open the file for reading</source>
@ -590,15 +590,15 @@
</message> </message>
<message> <message>
<source>Save Changes</source> <source>Save Changes</source>
<translation type="unfinished">Save Changes</translation> <translation>Lưu Thay Đi</translation>
</message> </message>
<message> <message>
<source>Do you want to save changes?</source> <source>Do you want to save changes?</source>
<translation type="unfinished">Do you want to save changes?</translation> <translation>Bạn muốn lưu thay đi?</translation>
</message> </message>
<message> <message>
<source>Help</source> <source>Help</source>
<translation type="unfinished">Help</translation> <translation>Trợ giúp</translation>
</message> </message>
<message> <message>
<source>Do you want to reset your custom default config to the original default config?</source> <source>Do you want to reset your custom default config to the original default config?</source>
@ -706,15 +706,15 @@
</message> </message>
<message> <message>
<source>h</source> <source>h</source>
<translation type="unfinished">h</translation> <translation>giờ</translation>
</message> </message>
<message> <message>
<source>m</source> <source>m</source>
<translation type="unfinished">m</translation> <translation>phút</translation>
</message> </message>
<message> <message>
<source>s</source> <source>s</source>
<translation type="unfinished">s</translation> <translation>giây</translation>
</message> </message>
<message> <message>
<source>Compatibility is untested</source> <source>Compatibility is untested</source>
@ -722,23 +722,23 @@
</message> </message>
<message> <message>
<source>Game does not initialize properly / crashes the emulator</source> <source>Game does not initialize properly / crashes the emulator</source>
<translation type="unfinished">Game does not initialize properly / crashes the emulator</translation> <translation>Trò chơi không đưc khởi chạy đúng cách / khiến giả lập bị văng</translation>
</message> </message>
<message> <message>
<source>Game boots, but only displays a blank screen</source> <source>Game boots, but only displays a blank screen</source>
<translation type="unfinished">Game boots, but only displays a blank screen</translation> <translation>Trò chơi thể khởi chạy, nhưng chẳng hiện cả</translation>
</message> </message>
<message> <message>
<source>Game displays an image but does not go past the menu</source> <source>Game displays an image but does not go past the menu</source>
<translation type="unfinished">Game displays an image but does not go past the menu</translation> <translation>Trò chơi hiển thị đưc hình nh, nhưng không thể tiếp tục từ menu</translation>
</message> </message>
<message> <message>
<source>Game has game-breaking glitches or unplayable performance</source> <source>Game has game-breaking glitches or unplayable performance</source>
<translation type="unfinished">Game has game-breaking glitches or unplayable performance</translation> <translation>Trò chơi lỗi nh hưởng đến trải nghiệm, hoặc hiệu năng khi chơi không n đnh</translation>
</message> </message>
<message> <message>
<source>Game can be completed with playable performance and no major glitches</source> <source>Game can be completed with playable performance and no major glitches</source>
<translation type="unfinished">Game can be completed with playable performance and no major glitches</translation> <translation>Trò chơi thể đưc hoàn thành từ đu đến cuối, hiệu năng n đnh không lỗi nh hưởng đến trải nghiệm</translation>
</message> </message>
<message> <message>
<source>Click to see details on github</source> <source>Click to see details on github</source>
@ -1170,19 +1170,19 @@
</message> </message>
<message> <message>
<source>Save</source> <source>Save</source>
<translation type="unfinished">Save</translation> <translation>Lưu</translation>
</message> </message>
<message> <message>
<source>Apply</source> <source>Apply</source>
<translation type="unfinished">Apply</translation> <translation>Áp dụng</translation>
</message> </message>
<message> <message>
<source>Restore Defaults</source> <source>Restore Defaults</source>
<translation type="unfinished">Restore Defaults</translation> <translation>Khôi Phục Mặc Đnh</translation>
</message> </message>
<message> <message>
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished">Cancel</translation> <translation>Hủy</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1193,7 +1193,7 @@
</message> </message>
<message> <message>
<source>Boot Game</source> <source>Boot Game</source>
<translation type="unfinished">Boot Game</translation> <translation>Khởi Chạy Trò Chơi</translation>
</message> </message>
<message> <message>
<source>Check for Updates</source> <source>Check for Updates</source>
@ -1201,7 +1201,7 @@
</message> </message>
<message> <message>
<source>About shadPS4</source> <source>About shadPS4</source>
<translation type="unfinished">About shadPS4</translation> <translation>Thông Tin Về shadPS4</translation>
</message> </message>
<message> <message>
<source>Configure...</source> <source>Configure...</source>
@ -1213,23 +1213,23 @@
</message> </message>
<message> <message>
<source>Open shadPS4 Folder</source> <source>Open shadPS4 Folder</source>
<translation type="unfinished">Open shadPS4 Folder</translation> <translation>Mở Thư Mục Của shadPS4</translation>
</message> </message>
<message> <message>
<source>Exit</source> <source>Exit</source>
<translation type="unfinished">Exit</translation> <translation>Thoát</translation>
</message> </message>
<message> <message>
<source>Exit shadPS4</source> <source>Exit shadPS4</source>
<translation type="unfinished">Exit shadPS4</translation> <translation>Thoát shadPS4</translation>
</message> </message>
<message> <message>
<source>Exit the application.</source> <source>Exit the application.</source>
<translation type="unfinished">Exit the application.</translation> <translation>Thoát ng dụng.</translation>
</message> </message>
<message> <message>
<source>Show Game List</source> <source>Show Game List</source>
<translation type="unfinished">Show Game List</translation> <translation>Hiển Thị Danh Sách Trò Chơi</translation>
</message> </message>
<message> <message>
<source>Game List Refresh</source> <source>Game List Refresh</source>

View File

@ -270,6 +270,10 @@ void SetupCapabilities(const Info& info, const Profile& profile, EmitContext& ct
if (info.has_image_query) { if (info.has_image_query) {
ctx.AddCapability(spv::Capability::ImageQuery); ctx.AddCapability(spv::Capability::ImageQuery);
} }
if (info.uses_atomic_float_min_max && profile.supports_image_fp32_atomic_min_max) {
ctx.AddExtension("SPV_EXT_shader_atomic_float_min_max");
ctx.AddCapability(spv::Capability::AtomicFloat32MinMaxEXT);
}
if (info.uses_lane_id) { if (info.uses_lane_id) {
ctx.AddCapability(spv::Capability::GroupNonUniform); ctx.AddCapability(spv::Capability::GroupNonUniform);
} }
@ -335,8 +339,7 @@ void DefineEntryPoint(const Info& info, EmitContext& ctx, Id main) {
ctx.AddExecutionMode(main, spv::ExecutionMode::OriginUpperLeft); ctx.AddExecutionMode(main, spv::ExecutionMode::OriginUpperLeft);
} }
if (info.has_discard) { if (info.has_discard) {
ctx.AddExtension("SPV_EXT_demote_to_helper_invocation"); ctx.AddCapability(spv::Capability::DemoteToHelperInvocation);
ctx.AddCapability(spv::Capability::DemoteToHelperInvocationEXT);
} }
if (info.stores.GetAny(IR::Attribute::Depth)) { if (info.stores.GetAny(IR::Attribute::Depth)) {
ctx.AddExecutionMode(main, spv::ExecutionMode::DepthReplacing); ctx.AddExecutionMode(main, spv::ExecutionMode::DepthReplacing);

View File

@ -38,6 +38,7 @@ Id BufferAtomicU32BoundsCheck(EmitContext& ctx, Id index, Id buffer_size, auto e
const Id ib_label = ctx.OpLabel(); const Id ib_label = ctx.OpLabel();
const Id oob_label = ctx.OpLabel(); const Id oob_label = ctx.OpLabel();
const Id end_label = ctx.OpLabel(); const Id end_label = ctx.OpLabel();
ctx.OpSelectionMerge(end_label, spv::SelectionControlMask::MaskNone);
ctx.OpBranchConditional(in_bounds, ib_label, oob_label); ctx.OpBranchConditional(in_bounds, ib_label, oob_label);
ctx.AddLabel(ib_label); ctx.AddLabel(ib_label);
const Id ib_result = emit_func(); const Id ib_result = emit_func();
@ -74,6 +75,14 @@ Id ImageAtomicU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id va
const auto [scope, semantics]{AtomicArgs(ctx)}; const auto [scope, semantics]{AtomicArgs(ctx)};
return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value); return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value);
} }
Id ImageAtomicF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value,
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
const auto& texture = ctx.images[handle & 0xFFFF];
const Id pointer{ctx.OpImageTexelPointer(ctx.image_f32, texture.id, coords, ctx.ConstU32(0U))};
const auto [scope, semantics]{AtomicArgs(ctx)};
return (ctx.*atomic_func)(ctx.F32[1], pointer, scope, semantics, value);
}
} // Anonymous namespace } // Anonymous namespace
Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id offset, Id value) { Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id offset, Id value) {
@ -186,6 +195,40 @@ Id EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords
return ImageAtomicU32(ctx, inst, handle, coords, value, &Sirit::Module::OpAtomicUMax); return ImageAtomicU32(ctx, inst, handle, coords, value, &Sirit::Module::OpAtomicUMax);
} }
Id EmitImageAtomicFMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value) {
if (ctx.profile.supports_image_fp32_atomic_min_max) {
return ImageAtomicF32(ctx, inst, handle, coords, value, &Sirit::Module::OpAtomicFMax);
}
const auto u32_value = ctx.OpBitcast(ctx.U32[1], value);
const auto sign_bit_set =
ctx.OpBitFieldUExtract(ctx.U32[1], u32_value, ctx.ConstU32(31u), ctx.ConstU32(1u));
const auto result = ctx.OpSelect(
ctx.F32[1], sign_bit_set,
EmitBitCastF32U32(ctx, EmitImageAtomicUMin32(ctx, inst, handle, coords, u32_value)),
EmitBitCastF32U32(ctx, EmitImageAtomicSMax32(ctx, inst, handle, coords, u32_value)));
return result;
}
Id EmitImageAtomicFMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value) {
if (ctx.profile.supports_image_fp32_atomic_min_max) {
return ImageAtomicF32(ctx, inst, handle, coords, value, &Sirit::Module::OpAtomicFMin);
}
const auto u32_value = ctx.OpBitcast(ctx.U32[1], value);
const auto sign_bit_set =
ctx.OpBitFieldUExtract(ctx.U32[1], u32_value, ctx.ConstU32(31u), ctx.ConstU32(1u));
const auto result = ctx.OpSelect(
ctx.F32[1], sign_bit_set,
EmitBitCastF32U32(ctx, EmitImageAtomicUMax32(ctx, inst, handle, coords, u32_value)),
EmitBitCastF32U32(ctx, EmitImageAtomicSMin32(ctx, inst, handle, coords, u32_value)));
return result;
}
Id EmitImageAtomicInc32(EmitContext&, IR::Inst*, u32, Id, Id) { Id EmitImageAtomicInc32(EmitContext&, IR::Inst*, u32, Id, Id) {
// TODO: This is not yet implemented // TODO: This is not yet implemented
throw NotImplementedException("SPIR-V Instruction"); throw NotImplementedException("SPIR-V Instruction");

View File

@ -64,10 +64,6 @@ Id EmitBitCastU32F32(EmitContext& ctx, Id value) {
return ctx.OpBitcast(ctx.U32[1], value); return ctx.OpBitcast(ctx.U32[1], value);
} }
Id EmitBitCastU64F64(EmitContext& ctx, Id value) {
return ctx.OpBitcast(ctx.U64, value);
}
Id EmitBitCastF16U16(EmitContext& ctx, Id value) { Id EmitBitCastF16U16(EmitContext& ctx, Id value) {
return ctx.OpBitcast(ctx.F16[1], value); return ctx.OpBitcast(ctx.F16[1], value);
} }
@ -76,10 +72,6 @@ Id EmitBitCastF32U32(EmitContext& ctx, Id value) {
return ctx.OpBitcast(ctx.F32[1], value); return ctx.OpBitcast(ctx.F32[1], value);
} }
void EmitBitCastF64U64(EmitContext&) {
UNREACHABLE_MSG("SPIR-V Instruction");
}
Id EmitPackUint2x32(EmitContext& ctx, Id value) { Id EmitPackUint2x32(EmitContext& ctx, Id value) {
return ctx.OpBitcast(ctx.U64, value); return ctx.OpBitcast(ctx.U64, value);
} }
@ -88,10 +80,14 @@ Id EmitUnpackUint2x32(EmitContext& ctx, Id value) {
return ctx.OpBitcast(ctx.U32[2], value); return ctx.OpBitcast(ctx.U32[2], value);
} }
Id EmitPackFloat2x32(EmitContext& ctx, Id value) { Id EmitPackDouble2x32(EmitContext& ctx, Id value) {
return ctx.OpBitcast(ctx.F64[1], value); return ctx.OpBitcast(ctx.F64[1], value);
} }
Id EmitUnpackDouble2x32(EmitContext& ctx, Id value) {
return ctx.OpBitcast(ctx.U32[2], value);
}
Id EmitPackUnorm2x16(EmitContext& ctx, Id value) { Id EmitPackUnorm2x16(EmitContext& ctx, Id value) {
return ctx.OpPackUnorm2x16(ctx.U32[1], value); return ctx.OpPackUnorm2x16(ctx.U32[1], value);
} }

View File

@ -529,7 +529,7 @@ void EmitStoreBufferBoundsCheck(EmitContext& ctx, Id index, Id buffer_size, auto
// Bounds checking enabled, wrap in a conditional branch. // Bounds checking enabled, wrap in a conditional branch.
auto compare_index = index; auto compare_index = index;
if (N > 1) { if (N > 1) {
index = ctx.OpIAdd(ctx.U32[1], index, ctx.ConstU32(N - 1)); compare_index = ctx.OpIAdd(ctx.U32[1], index, ctx.ConstU32(N - 1));
} }
const Id in_bounds = ctx.OpULessThan(ctx.U1[1], compare_index, buffer_size); const Id in_bounds = ctx.OpULessThan(ctx.U1[1], compare_index, buffer_size);
const Id in_bounds_label = ctx.OpLabel(); const Id in_bounds_label = ctx.OpLabel();

View File

@ -202,13 +202,12 @@ Id EmitSelectF32(EmitContext& ctx, Id cond, Id true_value, Id false_value);
Id EmitSelectF64(EmitContext& ctx, Id cond, Id true_value, Id false_value); Id EmitSelectF64(EmitContext& ctx, Id cond, Id true_value, Id false_value);
Id EmitBitCastU16F16(EmitContext& ctx, Id value); Id EmitBitCastU16F16(EmitContext& ctx, Id value);
Id EmitBitCastU32F32(EmitContext& ctx, Id value); Id EmitBitCastU32F32(EmitContext& ctx, Id value);
Id EmitBitCastU64F64(EmitContext& ctx, Id value);
Id EmitBitCastF16U16(EmitContext& ctx, Id value); Id EmitBitCastF16U16(EmitContext& ctx, Id value);
Id EmitBitCastF32U32(EmitContext& ctx, Id value); Id EmitBitCastF32U32(EmitContext& ctx, Id value);
void EmitBitCastF64U64(EmitContext& ctx);
Id EmitPackUint2x32(EmitContext& ctx, Id value); Id EmitPackUint2x32(EmitContext& ctx, Id value);
Id EmitUnpackUint2x32(EmitContext& ctx, Id value); Id EmitUnpackUint2x32(EmitContext& ctx, Id value);
Id EmitPackFloat2x32(EmitContext& ctx, Id value); Id EmitPackDouble2x32(EmitContext& ctx, Id value);
Id EmitUnpackDouble2x32(EmitContext& ctx, Id value);
Id EmitPackUnorm2x16(EmitContext& ctx, Id value); Id EmitPackUnorm2x16(EmitContext& ctx, Id value);
Id EmitUnpackUnorm2x16(EmitContext& ctx, Id value); Id EmitUnpackUnorm2x16(EmitContext& ctx, Id value);
Id EmitPackSnorm2x16(EmitContext& ctx, Id value); Id EmitPackSnorm2x16(EmitContext& ctx, Id value);
@ -483,6 +482,8 @@ Id EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords
Id EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); Id EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); Id EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); Id EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicFMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicFMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicInc32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); Id EmitImageAtomicInc32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicDec32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); Id EmitImageAtomicDec32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); Id EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);

View File

@ -869,6 +869,7 @@ void EmitContext::DefineImagesAndSamplers() {
} }
if (std::ranges::any_of(info.images, &ImageResource::is_atomic)) { if (std::ranges::any_of(info.images, &ImageResource::is_atomic)) {
image_u32 = TypePointer(spv::StorageClass::Image, U32[1]); image_u32 = TypePointer(spv::StorageClass::Image, U32[1]);
image_f32 = TypePointer(spv::StorageClass::Image, F32[1]);
} }
if (info.samplers.empty()) { if (info.samplers.empty()) {
return; return;

View File

@ -207,6 +207,7 @@ public:
Id invocation_id{}; Id invocation_id{};
Id subgroup_local_invocation_id{}; Id subgroup_local_invocation_id{};
Id image_u32{}; Id image_u32{};
Id image_f32{};
Id shared_memory_u8{}; Id shared_memory_u8{};
Id shared_memory_u16{}; Id shared_memory_u16{};

View File

@ -4,6 +4,7 @@
#include <algorithm> #include <algorithm>
#include <unordered_map> #include <unordered_map>
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h"
#include "shader_recompiler/frontend/control_flow_graph.h" #include "shader_recompiler/frontend/control_flow_graph.h"
namespace Shader::Gcn { namespace Shader::Gcn {
@ -67,6 +68,39 @@ static bool IgnoresExecMask(const GcnInst& inst) {
return false; return false;
} }
static std::optional<u32> ResolveSetPcTarget(std::span<const GcnInst> list, u32 setpc_index,
std::span<const u32> pc_map) {
if (setpc_index < 3) {
return std::nullopt;
}
const auto& getpc = list[setpc_index - 3];
const auto& arith = list[setpc_index - 2];
const auto& setpc = list[setpc_index];
if (getpc.opcode != Opcode::S_GETPC_B64 ||
!(arith.opcode == Opcode::S_ADD_U32 || arith.opcode == Opcode::S_SUB_U32) ||
setpc.opcode != Opcode::S_SETPC_B64)
return std::nullopt;
if (getpc.dst[0].code != setpc.src[0].code || arith.dst[0].code != setpc.src[0].code)
return std::nullopt;
if (arith.src_count < 2 || arith.src[1].field != OperandField::LiteralConst)
return std::nullopt;
const u32 imm = arith.src[1].code;
const s32 signed_offset =
(arith.opcode == Opcode::S_ADD_U32) ? static_cast<s32>(imm) : -static_cast<s32>(imm);
const u32 base_pc = pc_map[setpc_index - 3] + getpc.length;
const u32 result_pc = static_cast<u32>(static_cast<s32>(base_pc) + signed_offset);
LOG_DEBUG(Render_Recompiler, "SetPC target: {} + {} = {}", base_pc, signed_offset, result_pc);
return result_pc & ~0x3u;
}
static constexpr size_t LabelReserveSize = 32; static constexpr size_t LabelReserveSize = 32;
CFG::CFG(Common::ObjectPool<Block>& block_pool_, std::span<const GcnInst> inst_list_) CFG::CFG(Common::ObjectPool<Block>& block_pool_, std::span<const GcnInst> inst_list_)
@ -89,9 +123,20 @@ void CFG::EmitLabels() {
index_to_pc[i] = pc; index_to_pc[i] = pc;
const GcnInst inst = inst_list[i]; const GcnInst inst = inst_list[i];
if (inst.IsUnconditionalBranch()) { if (inst.IsUnconditionalBranch()) {
const u32 target = inst.BranchTarget(pc); u32 target = inst.BranchTarget(pc);
if (inst.opcode == Opcode::S_SETPC_B64) {
if (auto t = ResolveSetPcTarget(inst_list, i, index_to_pc)) {
target = *t;
} else {
ASSERT_MSG(
false,
"S_SETPC_B64 without a resolvable offset at PC {:#x} (Index {}): Involved "
"instructions not recognized or invalid pattern",
pc, i);
}
}
AddLabel(target); AddLabel(target);
// Emit this label so that the block ends with s_branch instruction // Emit this label so that the block ends with the branching instruction
AddLabel(pc + inst.length); AddLabel(pc + inst.length);
} else if (inst.IsConditionalBranch()) { } else if (inst.IsConditionalBranch()) {
const u32 true_label = inst.BranchTarget(pc); const u32 true_label = inst.BranchTarget(pc);
@ -102,6 +147,7 @@ void CFG::EmitLabels() {
const u32 next_label = pc + inst.length; const u32 next_label = pc + inst.length;
AddLabel(next_label); AddLabel(next_label);
} }
pc += inst.length; pc += inst.length;
} }
index_to_pc[inst_list.size()] = pc; index_to_pc[inst_list.size()] = pc;
@ -280,7 +326,18 @@ void CFG::LinkBlocks() {
// Find the branch targets from the instruction and link the blocks. // Find the branch targets from the instruction and link the blocks.
// Note: Block end address is one instruction after end_inst. // Note: Block end address is one instruction after end_inst.
const u32 branch_pc = block.end - end_inst.length; const u32 branch_pc = block.end - end_inst.length;
const u32 target_pc = end_inst.BranchTarget(branch_pc); u32 target_pc = 0;
if (end_inst.opcode == Opcode::S_SETPC_B64) {
auto tgt = ResolveSetPcTarget(inst_list, block.end_index, index_to_pc);
ASSERT_MSG(tgt,
"S_SETPC_B64 without a resolvable offset at PC {:#x} (Index {}): Involved "
"instructions not recognized or invalid pattern",
branch_pc, block.end_index);
target_pc = *tgt;
} else {
target_pc = end_inst.BranchTarget(branch_pc);
}
if (end_inst.IsUnconditionalBranch()) { if (end_inst.IsUnconditionalBranch()) {
auto* target_block = get_block(target_pc); auto* target_block = get_block(target_pc);
++target_block->num_predecessors; ++target_block->num_predecessors;

View File

@ -18,7 +18,7 @@ bool GcnInst::IsTerminateInstruction() const {
} }
bool GcnInst::IsUnconditionalBranch() const { bool GcnInst::IsUnconditionalBranch() const {
return opcode == Opcode::S_BRANCH; return opcode == Opcode::S_BRANCH || opcode == Opcode::S_SETPC_B64;
} }
bool GcnInst::IsFork() const { bool GcnInst::IsFork() const {

View File

@ -18,6 +18,7 @@ void Translator::EmitFlowControl(u32 pc, const GcnInst& inst) {
return; return;
case Opcode::S_GETPC_B64: case Opcode::S_GETPC_B64:
return S_GETPC_B64(pc, inst); return S_GETPC_B64(pc, inst);
case Opcode::S_SETPC_B64:
case Opcode::S_WAITCNT: case Opcode::S_WAITCNT:
case Opcode::S_NOP: case Opcode::S_NOP:
case Opcode::S_ENDPGM: case Opcode::S_ENDPGM:

View File

@ -336,7 +336,7 @@ T Translator::GetSrc64(const InstOperand& operand) {
const auto value_lo = ir.GetVectorReg(IR::VectorReg(operand.code)); const auto value_lo = ir.GetVectorReg(IR::VectorReg(operand.code));
const auto value_hi = ir.GetVectorReg(IR::VectorReg(operand.code + 1)); const auto value_hi = ir.GetVectorReg(IR::VectorReg(operand.code + 1));
if constexpr (is_float) { if constexpr (is_float) {
value = ir.PackFloat2x32(ir.CompositeConstruct(value_lo, value_hi)); value = ir.PackDouble2x32(ir.CompositeConstruct(value_lo, value_hi));
} else { } else {
value = ir.PackUint2x32(ir.CompositeConstruct(value_lo, value_hi)); value = ir.PackUint2x32(ir.CompositeConstruct(value_lo, value_hi));
} }
@ -444,10 +444,9 @@ void Translator::SetDst64(const InstOperand& operand, const IR::U64F64& value_ra
value_untyped = ir.FPSaturate(value_raw); value_untyped = ir.FPSaturate(value_raw);
} }
} }
const IR::U64 value =
is_float ? ir.BitCast<IR::U64>(IR::F64{value_untyped}) : IR::U64{value_untyped};
const IR::Value unpacked{ir.UnpackUint2x32(value)}; const IR::Value unpacked{is_float ? ir.UnpackDouble2x32(IR::F64{value_untyped})
: ir.UnpackUint2x32(IR::U64{value_untyped})};
const IR::U32 lo{ir.CompositeExtract(unpacked, 0U)}; const IR::U32 lo{ir.CompositeExtract(unpacked, 0U)};
const IR::U32 hi{ir.CompositeExtract(unpacked, 1U)}; const IR::U32 hi{ir.CompositeExtract(unpacked, 1U)};
switch (operand.field) { switch (operand.field) {
@ -518,7 +517,9 @@ void Translator::EmitFetch(const GcnInst& inst) {
const auto values = const auto values =
ir.CompositeConstruct(ir.GetAttribute(attr, 0), ir.GetAttribute(attr, 1), ir.CompositeConstruct(ir.GetAttribute(attr, 0), ir.GetAttribute(attr, 1),
ir.GetAttribute(attr, 2), ir.GetAttribute(attr, 3)); ir.GetAttribute(attr, 2), ir.GetAttribute(attr, 3));
const auto swizzled = ApplySwizzle(ir, values, buffer.DstSelect()); const auto converted =
IR::ApplyReadNumberConversionVec4(ir, values, buffer.GetNumberConversion());
const auto swizzled = ApplySwizzle(ir, converted, buffer.DstSelect());
for (u32 i = 0; i < 4; i++) { for (u32 i = 0; i < 4; i++) {
ir.SetVectorReg(dst_reg++, IR::F32{ir.CompositeExtract(swizzled, i)}); ir.SetVectorReg(dst_reg++, IR::F32{ir.CompositeExtract(swizzled, i)});
} }

View File

@ -513,13 +513,13 @@ void Translator::V_LSHLREV_B32(const GcnInst& inst) {
void Translator::V_AND_B32(const GcnInst& inst) { void Translator::V_AND_B32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 src1{ir.GetVectorReg(IR::VectorReg(inst.src[1].code))}; const IR::U32 src1{GetSrc(inst.src[1])};
SetDst(inst.dst[0], ir.BitwiseAnd(src0, src1)); SetDst(inst.dst[0], ir.BitwiseAnd(src0, src1));
} }
void Translator::V_OR_B32(bool is_xor, const GcnInst& inst) { void Translator::V_OR_B32(bool is_xor, const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 src1{ir.GetVectorReg(IR::VectorReg(inst.src[1].code))}; const IR::U32 src1{GetSrc(inst.src[1])};
SetDst(inst.dst[0], is_xor ? ir.BitwiseXor(src0, src1) : IR::U32(ir.BitwiseOr(src0, src1))); SetDst(inst.dst[0], is_xor ? ir.BitwiseXor(src0, src1) : IR::U32(ir.BitwiseOr(src0, src1)));
} }
@ -579,7 +579,7 @@ void Translator::V_MBCNT_U32_B32(bool is_low, const GcnInst& inst) {
void Translator::V_ADD_I32(const GcnInst& inst) { void Translator::V_ADD_I32(const GcnInst& inst) {
// Signed or unsigned components // Signed or unsigned components
const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 src1{ir.GetVectorReg(IR::VectorReg(inst.src[1].code))}; const IR::U32 src1{GetSrc(inst.src[1])};
const IR::U32 result{ir.IAdd(src0, src1)}; const IR::U32 result{ir.IAdd(src0, src1)};
SetDst(inst.dst[0], result); SetDst(inst.dst[0], result);

View File

@ -115,8 +115,12 @@ void Translator::EmitVectorMemory(const GcnInst& inst) {
return IMAGE_ATOMIC(AtomicOp::Smin, inst); return IMAGE_ATOMIC(AtomicOp::Smin, inst);
case Opcode::IMAGE_ATOMIC_UMIN: case Opcode::IMAGE_ATOMIC_UMIN:
return IMAGE_ATOMIC(AtomicOp::Umin, inst); return IMAGE_ATOMIC(AtomicOp::Umin, inst);
case Opcode::IMAGE_ATOMIC_FMIN:
return IMAGE_ATOMIC(AtomicOp::Fmin, inst);
case Opcode::IMAGE_ATOMIC_SMAX: case Opcode::IMAGE_ATOMIC_SMAX:
return IMAGE_ATOMIC(AtomicOp::Smax, inst); return IMAGE_ATOMIC(AtomicOp::Smax, inst);
case Opcode::IMAGE_ATOMIC_FMAX:
return IMAGE_ATOMIC(AtomicOp::Fmax, inst);
case Opcode::IMAGE_ATOMIC_UMAX: case Opcode::IMAGE_ATOMIC_UMAX:
return IMAGE_ATOMIC(AtomicOp::Umax, inst); return IMAGE_ATOMIC(AtomicOp::Umax, inst);
case Opcode::IMAGE_ATOMIC_AND: case Opcode::IMAGE_ATOMIC_AND:
@ -139,6 +143,7 @@ void Translator::EmitVectorMemory(const GcnInst& inst) {
case Opcode::IMAGE_SAMPLE_C_LZ: case Opcode::IMAGE_SAMPLE_C_LZ:
case Opcode::IMAGE_SAMPLE_O: case Opcode::IMAGE_SAMPLE_O:
case Opcode::IMAGE_SAMPLE_L_O: case Opcode::IMAGE_SAMPLE_L_O:
case Opcode::IMAGE_SAMPLE_B_O:
case Opcode::IMAGE_SAMPLE_LZ_O: case Opcode::IMAGE_SAMPLE_LZ_O:
case Opcode::IMAGE_SAMPLE_C_O: case Opcode::IMAGE_SAMPLE_C_O:
case Opcode::IMAGE_SAMPLE_C_LZ_O: case Opcode::IMAGE_SAMPLE_C_LZ_O:
@ -466,6 +471,10 @@ void Translator::IMAGE_ATOMIC(AtomicOp op, const GcnInst& inst) {
return ir.ImageAtomicIMax(handle, body, value, true, info); return ir.ImageAtomicIMax(handle, body, value, true, info);
case AtomicOp::Umax: case AtomicOp::Umax:
return ir.ImageAtomicUMax(handle, body, value, info); return ir.ImageAtomicUMax(handle, body, value, info);
case AtomicOp::Fmax:
return ir.ImageAtomicFMax(handle, body, value, info);
case AtomicOp::Fmin:
return ir.ImageAtomicFMin(handle, body, value, info);
case AtomicOp::And: case AtomicOp::And:
return ir.ImageAtomicAnd(handle, body, value, info); return ir.ImageAtomicAnd(handle, body, value, info);
case AtomicOp::Or: case AtomicOp::Or:

View File

@ -196,6 +196,7 @@ struct Info {
bool has_discard{}; bool has_discard{};
bool has_image_gather{}; bool has_image_gather{};
bool has_image_query{}; bool has_image_query{};
bool uses_atomic_float_min_max{};
bool uses_lane_id{}; bool uses_lane_id{};
bool uses_group_quad{}; bool uses_group_quad{};
bool uses_group_ballot{}; bool uses_group_ballot{};
@ -280,6 +281,11 @@ constexpr AmdGpu::Image ImageResource::GetSharp(const Info& info) const noexcept
// Fall back to null image if unbound. // Fall back to null image if unbound.
return AmdGpu::Image::Null(); return AmdGpu::Image::Null();
} }
const auto data_fmt = image.GetDataFmt();
if (is_depth && data_fmt != AmdGpu::DataFormat::Format16 &&
data_fmt != AmdGpu::DataFormat::Format32) {
return AmdGpu::Image::NullDepth();
}
return image; return image;
} }

View File

@ -84,16 +84,6 @@ IR::F16 IREmitter::BitCast<IR::F16, IR::U16>(const IR::U16& value) {
return Inst<IR::F16>(Opcode::BitCastF16U16, value); return Inst<IR::F16>(Opcode::BitCastF16U16, value);
} }
template <>
IR::U64 IREmitter::BitCast<IR::U64, IR::F64>(const IR::F64& value) {
return Inst<IR::U64>(Opcode::BitCastU64F64, value);
}
template <>
IR::F64 IREmitter::BitCast<IR::F64, IR::U64>(const IR::U64& value) {
return Inst<IR::F64>(Opcode::BitCastF64U64, value);
}
U1 IREmitter::ConditionRef(const U1& value) { U1 IREmitter::ConditionRef(const U1& value) {
return Inst<U1>(Opcode::ConditionRef, value); return Inst<U1>(Opcode::ConditionRef, value);
} }
@ -841,8 +831,12 @@ Value IREmitter::UnpackUint2x32(const U64& value) {
return Inst<Value>(Opcode::UnpackUint2x32, value); return Inst<Value>(Opcode::UnpackUint2x32, value);
} }
F64 IREmitter::PackFloat2x32(const Value& vector) { F64 IREmitter::PackDouble2x32(const Value& vector) {
return Inst<F64>(Opcode::PackFloat2x32, vector); return Inst<F64>(Opcode::PackDouble2x32, vector);
}
Value IREmitter::UnpackDouble2x32(const F64& value) {
return Inst<Value>(Opcode::UnpackDouble2x32, value);
} }
U32 IREmitter::Pack2x16(const AmdGpu::NumberFormat number_format, const Value& vector) { U32 IREmitter::Pack2x16(const AmdGpu::NumberFormat number_format, const Value& vector) {
@ -1876,6 +1870,16 @@ Value IREmitter::ImageAtomicUMax(const Value& handle, const Value& coords, const
return Inst(Opcode::ImageAtomicUMax32, Flags{info}, handle, coords, value); return Inst(Opcode::ImageAtomicUMax32, Flags{info}, handle, coords, value);
} }
Value IREmitter::ImageAtomicFMax(const Value& handle, const Value& coords, const Value& value,
TextureInstInfo info) {
return Inst(Opcode::ImageAtomicFMax32, Flags{info}, handle, coords, value);
}
Value IREmitter::ImageAtomicFMin(const Value& handle, const Value& coords, const Value& value,
TextureInstInfo info) {
return Inst(Opcode::ImageAtomicFMin32, Flags{info}, handle, coords, value);
}
Value IREmitter::ImageAtomicIMax(const Value& handle, const Value& coords, const Value& value, Value IREmitter::ImageAtomicIMax(const Value& handle, const Value& coords, const Value& value,
bool is_signed, TextureInstInfo info) { bool is_signed, TextureInstInfo info) {
return is_signed ? ImageAtomicSMax(handle, coords, value, info) return is_signed ? ImageAtomicSMax(handle, coords, value, info)

View File

@ -181,7 +181,8 @@ public:
[[nodiscard]] U64 PackUint2x32(const Value& vector); [[nodiscard]] U64 PackUint2x32(const Value& vector);
[[nodiscard]] Value UnpackUint2x32(const U64& value); [[nodiscard]] Value UnpackUint2x32(const U64& value);
[[nodiscard]] F64 PackFloat2x32(const Value& vector); [[nodiscard]] F64 PackDouble2x32(const Value& vector);
[[nodiscard]] Value UnpackDouble2x32(const F64& value);
[[nodiscard]] U32 Pack2x16(AmdGpu::NumberFormat number_format, const Value& vector); [[nodiscard]] U32 Pack2x16(AmdGpu::NumberFormat number_format, const Value& vector);
[[nodiscard]] Value Unpack2x16(AmdGpu::NumberFormat number_format, const U32& value); [[nodiscard]] Value Unpack2x16(AmdGpu::NumberFormat number_format, const U32& value);
@ -320,6 +321,10 @@ public:
const Value& value, TextureInstInfo info); const Value& value, TextureInstInfo info);
[[nodiscard]] Value ImageAtomicUMax(const Value& handle, const Value& coords, [[nodiscard]] Value ImageAtomicUMax(const Value& handle, const Value& coords,
const Value& value, TextureInstInfo info); const Value& value, TextureInstInfo info);
[[nodiscard]] Value ImageAtomicFMax(const Value& handle, const Value& coords,
const Value& value, TextureInstInfo info);
[[nodiscard]] Value ImageAtomicFMin(const Value& handle, const Value& coords,
const Value& value, TextureInstInfo info);
[[nodiscard]] Value ImageAtomicIMax(const Value& handle, const Value& coords, [[nodiscard]] Value ImageAtomicIMax(const Value& handle, const Value& coords,
const Value& value, bool is_signed, TextureInstInfo info); const Value& value, bool is_signed, TextureInstInfo info);
[[nodiscard]] Value ImageAtomicInc(const Value& handle, const Value& coords, const Value& value, [[nodiscard]] Value ImageAtomicInc(const Value& handle, const Value& coords, const Value& value,

View File

@ -94,6 +94,8 @@ bool Inst::MayHaveSideEffects() const noexcept {
case Opcode::ImageAtomicUMin32: case Opcode::ImageAtomicUMin32:
case Opcode::ImageAtomicSMax32: case Opcode::ImageAtomicSMax32:
case Opcode::ImageAtomicUMax32: case Opcode::ImageAtomicUMax32:
case Opcode::ImageAtomicFMax32:
case Opcode::ImageAtomicFMin32:
case Opcode::ImageAtomicInc32: case Opcode::ImageAtomicInc32:
case Opcode::ImageAtomicDec32: case Opcode::ImageAtomicDec32:
case Opcode::ImageAtomicAnd32: case Opcode::ImageAtomicAnd32:

View File

@ -191,14 +191,13 @@ OPCODE(SelectF64, F64, U1,
// Bitwise conversions // Bitwise conversions
OPCODE(BitCastU16F16, U16, F16, ) OPCODE(BitCastU16F16, U16, F16, )
OPCODE(BitCastU32F32, U32, F32, ) OPCODE(BitCastU32F32, U32, F32, )
OPCODE(BitCastU64F64, U64, F64, )
OPCODE(BitCastF16U16, F16, U16, ) OPCODE(BitCastF16U16, F16, U16, )
OPCODE(BitCastF32U32, F32, U32, ) OPCODE(BitCastF32U32, F32, U32, )
OPCODE(BitCastF64U64, F64, U64, )
OPCODE(PackUint2x32, U64, U32x2, ) OPCODE(PackUint2x32, U64, U32x2, )
OPCODE(UnpackUint2x32, U32x2, U64, ) OPCODE(UnpackUint2x32, U32x2, U64, )
OPCODE(PackFloat2x32, F64, F32x2, ) OPCODE(PackDouble2x32, F64, U32x2, )
OPCODE(UnpackDouble2x32, U32x2, F64, )
OPCODE(PackUnorm2x16, U32, F32x2, ) OPCODE(PackUnorm2x16, U32, F32x2, )
OPCODE(UnpackUnorm2x16, F32x2, U32, ) OPCODE(UnpackUnorm2x16, F32x2, U32, )
@ -421,6 +420,8 @@ OPCODE(ImageAtomicSMin32, U32, Opaq
OPCODE(ImageAtomicUMin32, U32, Opaque, Opaque, U32, ) OPCODE(ImageAtomicUMin32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicSMax32, U32, Opaque, Opaque, U32, ) OPCODE(ImageAtomicSMax32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicUMax32, U32, Opaque, Opaque, U32, ) OPCODE(ImageAtomicUMax32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicFMax32, F32, Opaque, Opaque, F32, )
OPCODE(ImageAtomicFMin32, F32, Opaque, Opaque, F32, )
OPCODE(ImageAtomicInc32, U32, Opaque, Opaque, U32, ) OPCODE(ImageAtomicInc32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicDec32, U32, Opaque, Opaque, U32, ) OPCODE(ImageAtomicDec32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicAnd32, U32, Opaque, Opaque, U32, ) OPCODE(ImageAtomicAnd32, U32, Opaque, Opaque, U32, )

View File

@ -21,6 +21,7 @@ void ReadLaneEliminationPass(IR::Program& program);
void ResourceTrackingPass(IR::Program& program); void ResourceTrackingPass(IR::Program& program);
void CollectShaderInfoPass(IR::Program& program); void CollectShaderInfoPass(IR::Program& program);
void LowerBufferFormatToRaw(IR::Program& program); void LowerBufferFormatToRaw(IR::Program& program);
void LowerFp64ToFp32(IR::Program& program);
void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtime_info); void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtime_info);
void TessellationPreprocess(IR::Program& program, RuntimeInfo& runtime_info); void TessellationPreprocess(IR::Program& program, RuntimeInfo& runtime_info);
void HullShaderTransform(IR::Program& program, RuntimeInfo& runtime_info); void HullShaderTransform(IR::Program& program, RuntimeInfo& runtime_info);

View File

@ -196,13 +196,19 @@ static void LowerBufferFormatInst(IR::Block& block, IR::Inst& inst, Info& info)
const auto buffer{desc.GetSharp(info)}; const auto buffer{desc.GetSharp(info)};
const auto is_inst_typed = flags.inst_data_fmt != AmdGpu::DataFormat::FormatInvalid; const auto is_inst_typed = flags.inst_data_fmt != AmdGpu::DataFormat::FormatInvalid;
const auto data_format = is_inst_typed ? flags.inst_data_fmt.Value() : buffer.GetDataFmt(); const auto data_format =
const auto num_format = is_inst_typed ? flags.inst_num_fmt.Value() : buffer.GetNumberFmt(); is_inst_typed ? AmdGpu::RemapDataFormat(flags.inst_data_fmt.Value()) : buffer.GetDataFmt();
const auto format_info = FormatInfo{ const auto format_info = FormatInfo{
.data_format = data_format, .data_format = data_format,
.num_format = num_format, .num_format = is_inst_typed
.swizzle = is_inst_typed ? AmdGpu::IdentityMapping : buffer.DstSelect(), ? AmdGpu::RemapNumberFormat(flags.inst_num_fmt.Value(), data_format)
.num_conversion = AmdGpu::MapNumberConversion(num_format), : buffer.GetNumberFmt(),
.swizzle = is_inst_typed
? AmdGpu::RemapSwizzle(flags.inst_data_fmt.Value(), AmdGpu::IdentityMapping)
: buffer.DstSelect(),
.num_conversion = is_inst_typed ? AmdGpu::MapNumberConversion(flags.inst_num_fmt.Value(),
flags.inst_data_fmt.Value())
: buffer.GetNumberConversion(),
.num_components = AmdGpu::NumComponents(data_format), .num_components = AmdGpu::NumComponents(data_format),
}; };

View File

@ -0,0 +1,186 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "shader_recompiler/info.h"
#include "shader_recompiler/ir/basic_block.h"
#include "shader_recompiler/ir/ir_emitter.h"
#include "shader_recompiler/ir/program.h"
namespace Shader::Optimization {
constexpr s32 F64ToF32Exp = +1023 - 127;
constexpr s32 F32ToF64Exp = +127 - 1023;
static IR::F32 PackedF64ToF32(IR::IREmitter& ir, const IR::Value& packed) {
const IR::U32 lo{ir.CompositeExtract(packed, 0)};
const IR::U32 hi{ir.CompositeExtract(packed, 1)};
const IR::U32 sign{ir.BitFieldExtract(hi, ir.Imm32(31), ir.Imm32(1))};
const IR::U32 exp{ir.BitFieldExtract(hi, ir.Imm32(20), ir.Imm32(11))};
const IR::U32 mantissa_hi{ir.BitFieldExtract(hi, ir.Imm32(0), ir.Imm32(20))};
const IR::U32 mantissa_lo{ir.BitFieldExtract(lo, ir.Imm32(29), ir.Imm32(3))};
const IR::U32 mantissa{
ir.BitwiseOr(ir.ShiftLeftLogical(mantissa_hi, ir.Imm32(3)), mantissa_lo)};
const IR::U32 exp_if_subnorm{
ir.Select(ir.IEqual(exp, ir.Imm32(0)), ir.Imm32(0), ir.IAdd(exp, ir.Imm32(F64ToF32Exp)))};
const IR::U32 exp_if_infnan{
ir.Select(ir.IEqual(exp, ir.Imm32(0x7ff)), ir.Imm32(0xff), exp_if_subnorm)};
const IR::U32 result{
ir.BitwiseOr(ir.ShiftLeftLogical(sign, ir.Imm32(31)),
ir.BitwiseOr(ir.ShiftLeftLogical(exp_if_infnan, ir.Imm32(23)), mantissa))};
return ir.BitCast<IR::F32>(result);
}
IR::Value F32ToPackedF64(IR::IREmitter& ir, const IR::Value& raw) {
const IR::U32 value{ir.BitCast<IR::U32>(IR::F32(raw))};
const IR::U32 sign{ir.BitFieldExtract(value, ir.Imm32(31), ir.Imm32(1))};
const IR::U32 exp{ir.BitFieldExtract(value, ir.Imm32(23), ir.Imm32(8))};
const IR::U32 mantissa{ir.BitFieldExtract(value, ir.Imm32(0), ir.Imm32(23))};
const IR::U32 mantissa_hi{ir.BitFieldExtract(mantissa, ir.Imm32(3), ir.Imm32(20))};
const IR::U32 mantissa_lo{ir.BitFieldExtract(mantissa, ir.Imm32(0), ir.Imm32(3))};
const IR::U32 exp_if_subnorm{
ir.Select(ir.IEqual(exp, ir.Imm32(0)), ir.Imm32(0), ir.IAdd(exp, ir.Imm32(F32ToF64Exp)))};
const IR::U32 exp_if_infnan{
ir.Select(ir.IEqual(exp, ir.Imm32(0xff)), ir.Imm32(0x7ff), exp_if_subnorm)};
const IR::U32 lo{ir.ShiftLeftLogical(mantissa_lo, ir.Imm32(29))};
const IR::U32 hi{
ir.BitwiseOr(ir.ShiftLeftLogical(sign, ir.Imm32(31)),
ir.BitwiseOr(ir.ShiftLeftLogical(exp_if_infnan, ir.Imm32(20)), mantissa_hi))};
return ir.CompositeConstruct(lo, hi);
}
static IR::Opcode Replace(IR::Opcode op) {
switch (op) {
case IR::Opcode::CompositeConstructF64x2:
return IR::Opcode::CompositeConstructF32x2;
case IR::Opcode::CompositeConstructF64x3:
return IR::Opcode::CompositeConstructF32x3;
case IR::Opcode::CompositeConstructF64x4:
return IR::Opcode::CompositeConstructF32x4;
case IR::Opcode::CompositeExtractF64x2:
return IR::Opcode::CompositeExtractF32x2;
case IR::Opcode::CompositeExtractF64x3:
return IR::Opcode::CompositeExtractF32x3;
case IR::Opcode::CompositeExtractF64x4:
return IR::Opcode::CompositeExtractF32x4;
case IR::Opcode::CompositeInsertF64x2:
return IR::Opcode::CompositeInsertF32x2;
case IR::Opcode::CompositeInsertF64x3:
return IR::Opcode::CompositeInsertF32x3;
case IR::Opcode::CompositeInsertF64x4:
return IR::Opcode::CompositeInsertF32x4;
case IR::Opcode::CompositeShuffleF64x2:
return IR::Opcode::CompositeShuffleF32x2;
case IR::Opcode::CompositeShuffleF64x3:
return IR::Opcode::CompositeShuffleF32x3;
case IR::Opcode::CompositeShuffleF64x4:
return IR::Opcode::CompositeShuffleF32x4;
case IR::Opcode::SelectF64:
return IR::Opcode::SelectF64;
case IR::Opcode::FPAbs64:
return IR::Opcode::FPAbs32;
case IR::Opcode::FPAdd64:
return IR::Opcode::FPAdd32;
case IR::Opcode::FPFma64:
return IR::Opcode::FPFma32;
case IR::Opcode::FPMax64:
return IR::Opcode::FPMax32;
case IR::Opcode::FPMin64:
return IR::Opcode::FPMin32;
case IR::Opcode::FPMul64:
return IR::Opcode::FPMul32;
case IR::Opcode::FPDiv64:
return IR::Opcode::FPDiv32;
case IR::Opcode::FPNeg64:
return IR::Opcode::FPNeg32;
case IR::Opcode::FPRecip64:
return IR::Opcode::FPRecip32;
case IR::Opcode::FPRecipSqrt64:
return IR::Opcode::FPRecipSqrt32;
case IR::Opcode::FPSaturate64:
return IR::Opcode::FPSaturate32;
case IR::Opcode::FPClamp64:
return IR::Opcode::FPClamp32;
case IR::Opcode::FPRoundEven64:
return IR::Opcode::FPRoundEven32;
case IR::Opcode::FPFloor64:
return IR::Opcode::FPFloor32;
case IR::Opcode::FPCeil64:
return IR::Opcode::FPCeil32;
case IR::Opcode::FPTrunc64:
return IR::Opcode::FPTrunc32;
case IR::Opcode::FPFract64:
return IR::Opcode::FPFract32;
case IR::Opcode::FPFrexpSig64:
return IR::Opcode::FPFrexpSig32;
case IR::Opcode::FPFrexpExp64:
return IR::Opcode::FPFrexpExp32;
case IR::Opcode::FPOrdEqual64:
return IR::Opcode::FPOrdEqual32;
case IR::Opcode::FPUnordEqual64:
return IR::Opcode::FPUnordEqual32;
case IR::Opcode::FPOrdNotEqual64:
return IR::Opcode::FPOrdNotEqual32;
case IR::Opcode::FPUnordNotEqual64:
return IR::Opcode::FPUnordNotEqual32;
case IR::Opcode::FPOrdLessThan64:
return IR::Opcode::FPOrdLessThan32;
case IR::Opcode::FPUnordLessThan64:
return IR::Opcode::FPUnordLessThan32;
case IR::Opcode::FPOrdGreaterThan64:
return IR::Opcode::FPOrdGreaterThan32;
case IR::Opcode::FPUnordGreaterThan64:
return IR::Opcode::FPUnordGreaterThan32;
case IR::Opcode::FPOrdLessThanEqual64:
return IR::Opcode::FPOrdLessThanEqual32;
case IR::Opcode::FPUnordLessThanEqual64:
return IR::Opcode::FPUnordLessThanEqual32;
case IR::Opcode::FPOrdGreaterThanEqual64:
return IR::Opcode::FPOrdGreaterThanEqual32;
case IR::Opcode::FPUnordGreaterThanEqual64:
return IR::Opcode::FPUnordGreaterThanEqual32;
case IR::Opcode::FPIsNan64:
return IR::Opcode::FPIsNan32;
case IR::Opcode::FPIsInf64:
return IR::Opcode::FPIsInf32;
case IR::Opcode::ConvertS32F64:
return IR::Opcode::ConvertS32F32;
case IR::Opcode::ConvertF32F64:
return IR::Opcode::Identity;
case IR::Opcode::ConvertF64F32:
return IR::Opcode::Identity;
case IR::Opcode::ConvertF64S32:
return IR::Opcode::ConvertF32S32;
case IR::Opcode::ConvertF64U32:
return IR::Opcode::ConvertF32U32;
default:
return op;
}
}
static void Lower(IR::Block& block, IR::Inst& inst) {
switch (inst.GetOpcode()) {
case IR::Opcode::PackDouble2x32: {
IR::IREmitter ir(block, IR::Block::InstructionList::s_iterator_to(inst));
inst.ReplaceUsesWith(PackedF64ToF32(ir, inst.Arg(0)));
break;
}
case IR::Opcode::UnpackDouble2x32: {
IR::IREmitter ir(block, IR::Block::InstructionList::s_iterator_to(inst));
inst.ReplaceUsesWith(F32ToPackedF64(ir, inst.Arg(0)));
break;
}
default:
inst.ReplaceOpcode(Replace(inst.GetOpcode()));
break;
}
}
void LowerFp64ToFp32(IR::Program& program) {
for (IR::Block* const block : program.blocks) {
for (IR::Inst& inst : block->Instructions()) {
Lower(*block, inst);
}
}
}
} // namespace Shader::Optimization

View File

@ -101,6 +101,8 @@ bool IsImageAtomicInstruction(const IR::Inst& inst) {
case IR::Opcode::ImageAtomicUMin32: case IR::Opcode::ImageAtomicUMin32:
case IR::Opcode::ImageAtomicSMax32: case IR::Opcode::ImageAtomicSMax32:
case IR::Opcode::ImageAtomicUMax32: case IR::Opcode::ImageAtomicUMax32:
case IR::Opcode::ImageAtomicFMax32:
case IR::Opcode::ImageAtomicFMin32:
case IR::Opcode::ImageAtomicInc32: case IR::Opcode::ImageAtomicInc32:
case IR::Opcode::ImageAtomicDec32: case IR::Opcode::ImageAtomicDec32:
case IR::Opcode::ImageAtomicAnd32: case IR::Opcode::ImageAtomicAnd32:
@ -361,6 +363,12 @@ void PatchImageSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors&
LOG_ERROR(Render_Vulkan, "Shader compiled with unbound image!"); LOG_ERROR(Render_Vulkan, "Shader compiled with unbound image!");
image = AmdGpu::Image::Null(); image = AmdGpu::Image::Null();
} }
const auto data_fmt = image.GetDataFmt();
if (inst_info.is_depth && data_fmt != AmdGpu::DataFormat::Format16 &&
data_fmt != AmdGpu::DataFormat::Format32) {
LOG_ERROR(Render_Vulkan, "Shader compiled using non-depth image with depth instruction!");
image = AmdGpu::Image::NullDepth();
}
ASSERT(image.GetType() != AmdGpu::ImageType::Invalid); ASSERT(image.GetType() != AmdGpu::ImageType::Invalid);
const bool is_written = inst.GetOpcode() == IR::Opcode::ImageWrite; const bool is_written = inst.GetOpcode() == IR::Opcode::ImageWrite;

View File

@ -44,7 +44,8 @@ void Visit(Info& info, const IR::Inst& inst) {
case IR::Opcode::BitCastF16U16: case IR::Opcode::BitCastF16U16:
info.uses_fp16 = true; info.uses_fp16 = true;
break; break;
case IR::Opcode::BitCastU64F64: case IR::Opcode::PackDouble2x32:
case IR::Opcode::UnpackDouble2x32:
info.uses_fp64 = true; info.uses_fp64 = true;
break; break;
case IR::Opcode::ImageWrite: case IR::Opcode::ImageWrite:
@ -70,6 +71,10 @@ void Visit(Info& info, const IR::Inst& inst) {
case IR::Opcode::ImageQueryLod: case IR::Opcode::ImageQueryLod:
info.has_image_query = true; info.has_image_query = true;
break; break;
case IR::Opcode::ImageAtomicFMax32:
case IR::Opcode::ImageAtomicFMin32:
info.uses_atomic_float_min_max = true;
break;
case IR::Opcode::LaneId: case IR::Opcode::LaneId:
info.uses_lane_id = true; info.uses_lane_id = true;
break; break;

View File

@ -34,6 +34,18 @@ inline F32 ApplyReadNumberConversion(IREmitter& ir, const F32& value,
case AmdGpu::NumberConversion::UnormToUbnorm: case AmdGpu::NumberConversion::UnormToUbnorm:
// Convert 0...1 to -1...1 // Convert 0...1 to -1...1
return ir.FPSub(ir.FPMul(value, ir.Imm32(2.f)), ir.Imm32(1.f)); return ir.FPSub(ir.FPMul(value, ir.Imm32(2.f)), ir.Imm32(1.f));
case AmdGpu::NumberConversion::Sint8ToSnormNz: {
const IR::U32 additon = ir.IAdd(ir.IMul(ir.BitCast<U32>(value), ir.Imm32(2)), ir.Imm32(1));
const IR::F32 left = ir.ConvertSToF(32, 32, additon);
const IR::F32 max = ir.Imm32(float(std::numeric_limits<u8>::max()));
return ir.FPDiv(left, max);
}
case AmdGpu::NumberConversion::Sint16ToSnormNz: {
const IR::U32 additon = ir.IAdd(ir.IMul(ir.BitCast<U32>(value), ir.Imm32(2)), ir.Imm32(1));
const IR::F32 left = ir.ConvertSToF(32, 32, additon);
const IR::F32 max = ir.Imm32(float(std::numeric_limits<u16>::max()));
return ir.FPDiv(left, max);
}
default: default:
UNREACHABLE(); UNREACHABLE();
} }
@ -66,6 +78,20 @@ inline F32 ApplyWriteNumberConversion(IREmitter& ir, const F32& value,
case AmdGpu::NumberConversion::UnormToUbnorm: case AmdGpu::NumberConversion::UnormToUbnorm:
// Convert -1...1 to 0...1 // Convert -1...1 to 0...1
return ir.FPDiv(ir.FPAdd(value, ir.Imm32(1.f)), ir.Imm32(2.f)); return ir.FPDiv(ir.FPAdd(value, ir.Imm32(1.f)), ir.Imm32(2.f));
case AmdGpu::NumberConversion::Sint8ToSnormNz: {
const IR::F32 max = ir.Imm32(float(std::numeric_limits<u8>::max()));
const IR::F32 mul = ir.FPMul(ir.FPClamp(value, ir.Imm32(-1.f), ir.Imm32(1.f)), max);
const IR::F32 left = ir.FPSub(mul, ir.Imm32(1.f));
const IR::U32 raw = ir.ConvertFToS(32, ir.FPDiv(left, ir.Imm32(2.f)));
return ir.BitCast<F32>(raw);
}
case AmdGpu::NumberConversion::Sint16ToSnormNz: {
const IR::F32 max = ir.Imm32(float(std::numeric_limits<u16>::max()));
const IR::F32 mul = ir.FPMul(ir.FPClamp(value, ir.Imm32(-1.f), ir.Imm32(1.f)), max);
const IR::F32 left = ir.FPSub(mul, ir.Imm32(1.f));
const IR::U32 raw = ir.ConvertFToS(32, ir.FPDiv(left, ir.Imm32(2.f)));
return ir.BitCast<F32>(raw);
}
default: default:
UNREACHABLE(); UNREACHABLE();
} }

View File

@ -15,6 +15,7 @@ struct Profile {
bool support_int8{}; bool support_int8{};
bool support_int16{}; bool support_int16{};
bool support_int64{}; bool support_int64{};
bool support_float64{};
bool support_vertex_instance_id{}; bool support_vertex_instance_id{};
bool support_float_controls{}; bool support_float_controls{};
bool support_separate_denorm_behavior{}; bool support_separate_denorm_behavior{};
@ -28,6 +29,7 @@ struct Profile {
bool supports_native_cube_calc{}; bool supports_native_cube_calc{};
bool supports_trinary_minmax{}; bool supports_trinary_minmax{};
bool supports_robust_buffer_access{}; bool supports_robust_buffer_access{};
bool supports_image_fp32_atomic_min_max{};
bool has_broken_spirv_clamp{}; bool has_broken_spirv_clamp{};
bool lower_left_origin_mode{}; bool lower_left_origin_mode{};
bool needs_manual_interpolation{}; bool needs_manual_interpolation{};

View File

@ -60,6 +60,9 @@ IR::Program TranslateProgram(std::span<const u32> code, Pools& pools, Info& info
program.post_order_blocks = Shader::IR::PostOrder(program.syntax_list.front()); program.post_order_blocks = Shader::IR::PostOrder(program.syntax_list.front());
// Run optimization passes // Run optimization passes
if (!profile.support_float64) {
Shader::Optimization::LowerFp64ToFp32(program);
}
Shader::Optimization::SsaRewritePass(program.post_order_blocks); Shader::Optimization::SsaRewritePass(program.post_order_blocks);
Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); Shader::Optimization::ConstantPropagationPass(program.post_order_blocks);
Shader::Optimization::IdentityRemovalPass(program.blocks); Shader::Optimization::IdentityRemovalPass(program.blocks);

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