diff --git a/CMakeLists.txt b/CMakeLists.txt old mode 100755 new mode 100644 index 97a223d06..3b67bbe74 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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.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/ssl.cpp src/core/libraries/network/ssl.h src/core/libraries/network/ssl2.cpp 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 @@ -593,6 +601,13 @@ set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp src/core/libraries/move/move.h src/core/libraries/ulobjmgr/ulobjmgr.cpp src/core/libraries/ulobjmgr/ulobjmgr.h + src/core/libraries/signin_dialog/signindialog.cpp + src/core/libraries/signin_dialog/signindialog.h +) + +set(CAMERA_LIBS src/core/libraries/camera/camera.cpp + src/core/libraries/camera/camera.h + src/core/libraries/camera/camera_error.h ) set(DEV_TOOLS src/core/devtools/layer.cpp @@ -758,6 +773,7 @@ set(CORE src/core/aerolib/stubs.cpp ${FIBER_LIB} ${VDEC_LIB} ${VR_LIBS} + ${CAMERA_LIBS} ${DEV_TOOLS} src/core/debug_state.cpp src/core/debug_state.h @@ -1082,9 +1098,13 @@ if (ENABLE_DISCORD_RPC) target_compile_definitions(shadps4 PRIVATE ENABLE_DISCORD_RPC) endif() -# Optional due to https://github.com/shadps4-emu/shadPS4/issues/1704 -if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND ENABLE_USERFAULTFD) - target_compile_definitions(shadps4 PRIVATE ENABLE_USERFAULTFD) +if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + # Optional due to https://github.com/shadps4-emu/shadPS4/issues/1704 + if (ENABLE_USERFAULTFD) + target_compile_definitions(shadps4 PRIVATE ENABLE_USERFAULTFD) + endif() + + target_link_libraries(shadps4 PRIVATE uuid) endif() if (APPLE) diff --git a/externals/sdl3 b/externals/sdl3 index 4093e4a19..86b206dad 160000 --- a/externals/sdl3 +++ b/externals/sdl3 @@ -1 +1 @@ -Subproject commit 4093e4a193971ef1d4928158e0a1832be42e4599 +Subproject commit 86b206dadf8ad40e6657fa37db371a0aeff74e9c diff --git a/externals/sirit b/externals/sirit index 427a42c9e..09a1416ab 160000 --- a/externals/sirit +++ b/externals/sirit @@ -1 +1 @@ -Subproject commit 427a42c9ed99b38204d9107bc3dc14e92458acf1 +Subproject commit 09a1416ab1b59ddfebd2618412f118f2004f3b2c diff --git a/src/common/io_file.cpp b/src/common/io_file.cpp index 3efadc6ea..6fa9062a7 100644 --- a/src/common/io_file.cpp +++ b/src/common/io_file.cpp @@ -131,9 +131,7 @@ namespace { case SeekOrigin::End: return SEEK_END; default: - LOG_ERROR(Common_Filesystem, "Unsupported origin {}, defaulting to SEEK_SET", - static_cast(origin)); - return SEEK_SET; + UNREACHABLE_MSG("Impossible SeekOrigin {}", static_cast(origin)); } } diff --git a/src/common/io_file.h b/src/common/io_file.h index fb20a2bc5..45787a092 100644 --- a/src/common/io_file.h +++ b/src/common/io_file.h @@ -61,8 +61,6 @@ enum class SeekOrigin : u32 { SetOrigin, // Seeks from the start of the file. CurrentPosition, // Seeks from the current file pointer position. End, // Seeks from the end of the file. - SeekHole, // Seeks from the start of the next hole in the file. - SeekData, // Seeks from the start of the next non-hole region in the file. }; class IOFile final { diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 867d62916..1b605e9ed 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -137,6 +137,8 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, NpParty) \ SUB(Lib, Zlib) \ SUB(Lib, Hmd) \ + SUB(Lib, SigninDialog) \ + SUB(Lib, Camera) \ CLS(Frontend) \ CLS(Render) \ SUB(Render, Vulkan) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index e5714a81a..5746b648e 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -104,6 +104,8 @@ enum class Class : u8 { Lib_NpParty, ///< The LibSceNpParty implementation Lib_Zlib, ///< The LibSceZlib implementation. Lib_Hmd, ///< The LibSceHmd implementation. + Lib_SigninDialog, ///< The LibSigninDialog implementation. + Lib_Camera, ///< The LibCamera implementation. Frontend, ///< Emulator UI Render, ///< Video Core Render_Vulkan, ///< Vulkan backend diff --git a/src/common/memory_patcher.cpp b/src/common/memory_patcher.cpp index bb2d23c45..cb51828cc 100644 --- a/src/common/memory_patcher.cpp +++ b/src/common/memory_patcher.cpp @@ -23,7 +23,7 @@ namespace MemoryPatcher { -uintptr_t g_eboot_address; +EXPORT uintptr_t g_eboot_address; uint64_t g_eboot_image_size; std::string g_game_serial; std::string patchFile; diff --git a/src/common/memory_patcher.h b/src/common/memory_patcher.h index 29045a6a2..968903a85 100644 --- a/src/common/memory_patcher.h +++ b/src/common/memory_patcher.h @@ -6,9 +6,15 @@ #include #include +#if defined(WIN32) +#define EXPORT __declspec(dllexport) +#else +#define EXPORT __attribute__((visibility("default"))) +#endif + namespace MemoryPatcher { -extern uintptr_t g_eboot_address; +extern EXPORT uintptr_t g_eboot_address; extern uint64_t g_eboot_image_size; extern std::string g_game_serial; extern std::string patchFile; diff --git a/src/common/va_ctx.h b/src/common/va_ctx.h index e0b8c0bab..cffe468ff 100644 --- a/src/common/va_ctx.h +++ b/src/common/va_ctx.h @@ -8,7 +8,7 @@ #define VA_ARGS \ uint64_t rdi, uint64_t rsi, uint64_t rdx, uint64_t rcx, uint64_t r8, uint64_t r9, \ uint64_t overflow_arg_area, __m128 xmm0, __m128 xmm1, __m128 xmm2, __m128 xmm3, \ - __m128 xmm4, __m128 xmm5, __m128 xmm6, __m128 xmm7, ... + __m128 xmm4, __m128 xmm5, __m128 xmm6, __m128 xmm7 #define VA_CTX(ctx) \ alignas(16)::Common::VaCtx ctx{}; \ diff --git a/src/core/address_space.h b/src/core/address_space.h index 7ccc2cd1e..d7f3efc75 100644 --- a/src/core/address_space.h +++ b/src/core/address_space.h @@ -19,8 +19,6 @@ enum class MemoryPermission : u32 { }; DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission) -constexpr VAddr CODE_BASE_OFFSET = 0x100000000ULL; - constexpr VAddr SYSTEM_MANAGED_MIN = 0x00000400000ULL; constexpr VAddr SYSTEM_MANAGED_MAX = 0x07FFFFBFFFULL; constexpr VAddr SYSTEM_RESERVED_MIN = 0x07FFFFC000ULL; diff --git a/src/core/cpu_patches.cpp b/src/core/cpu_patches.cpp index c8106b270..8937ef04b 100644 --- a/src/core/cpu_patches.cpp +++ b/src/core/cpu_patches.cpp @@ -464,9 +464,8 @@ static std::pair TryPatch(u8* code, PatchModule* module) { if (needs_trampoline && instruction.length < 5) { // Trampoline is needed but instruction is too short to patch. - // Return false and length to fall back to the illegal instruction handler, - // or to signal to AOT compilation that this instruction should be skipped and - // handled at runtime. + // Return false and length to signal to AOT compilation that this instruction + // should be skipped and handled at runtime. return std::make_pair(false, instruction.length); } @@ -512,136 +511,137 @@ static std::pair TryPatch(u8* code, PatchModule* module) { #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) { - ZydisDecodedInstruction instruction; - ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT]; - const auto status = - Common::Decoder::Instance()->decodeInstruction(instruction, operands, code_address); + // We need to decode the instruction to find out what it is. Normally we'd use a fully fleshed + // out decoder like Zydis, however Zydis does a bunch of stuff that impact performance that we + // don't care about. We can get information about the instruction a lot faster by writing a mini + // 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: { - bool immediateForm = operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE && - operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; - if (immediateForm) { - LOG_CRITICAL(Core, "EXTRQ immediate form should have been patched at code address: {}", - fmt::ptr(code_address)); - return false; + 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 { - ASSERT_MSG(operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && - 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; + mask = (1ULL << length) - 1; } - 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: { - bool immediateForm = operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE && - operands[3].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; - if (immediateForm) { - LOG_CRITICAL(Core, - "INSERTQ immediate form should have been patched at code address: {}", - fmt::ptr(code_address)); - return false; + 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 { - ASSERT_MSG(operands[2].type == ZYDIS_OPERAND_TYPE_UNUSED && - 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; + mask = (1ULL << length) - 1; } - 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: { - LOG_ERROR(Core, "Unhandled illegal instruction at code address {}: {}", - fmt::ptr(code_address), ZydisMnemonicGetString(instruction.mnemonic)); - return false; + UNREACHABLE(); } } @@ -695,9 +695,22 @@ static bool PatchesAccessViolationHandler(void* context, void* /* fault_address static bool PatchesIllegalInstructionHandler(void* 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); + } 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; } diff --git a/src/core/devices/base_device.h b/src/core/devices/base_device.h index 36614b8f4..0cbbd3a00 100644 --- a/src/core/devices/base_device.h +++ b/src/core/devices/base_device.h @@ -40,6 +40,10 @@ public: 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) { return ORBIS_KERNEL_ERROR_EBADF; } diff --git a/src/core/devices/random_device.cpp b/src/core/devices/random_device.cpp index 50934e3b8..b2754fe58 100644 --- a/src/core/devices/random_device.cpp +++ b/src/core/devices/random_device.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include "common/logging/log.h" #include "random_device.h" diff --git a/src/core/devices/srandom_device.cpp b/src/core/devices/srandom_device.cpp index ab78ddbe2..5e51b1c39 100644 --- a/src/core/devices/srandom_device.cpp +++ b/src/core/devices/srandom_device.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include "common/logging/log.h" #include "srandom_device.h" diff --git a/src/core/devices/urandom_device.cpp b/src/core/devices/urandom_device.cpp index c001aab83..7318a6ff7 100644 --- a/src/core/devices/urandom_device.cpp +++ b/src/core/devices/urandom_device.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include "common/logging/log.h" #include "urandom_device.h" diff --git a/src/core/devtools/widget/frame_dump.cpp b/src/core/devtools/widget/frame_dump.cpp index 646ccb6d6..2445bdcb5 100644 --- a/src/core/devtools/widget/frame_dump.cpp +++ b/src/core/devtools/widget/frame_dump.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include #include diff --git a/src/core/libraries/camera/camera.cpp b/src/core/libraries/camera/camera.cpp new file mode 100644 index 000000000..996d1c895 --- /dev/null +++ b/src/core/libraries/camera/camera.cpp @@ -0,0 +1,517 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/camera/camera.h" +#include "core/libraries/camera/camera_error.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" + +namespace Libraries::Camera { + +s32 PS4_SYSV_ABI sceCameraAccGetData() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraAudioClose() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraAudioGetData() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraAudioGetData2() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraAudioOpen() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraAudioReset() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraChangeAppModuleState() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraClose(s32 handle) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraCloseByHandle() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraDeviceOpen() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetAttribute(s32 handle, OrbisCameraAttribute* pAttribute) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetAutoExposureGain(s32 handle, OrbisCameraChannel channel, u32* pEnable, + void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetAutoWhiteBalance(s32 handle, OrbisCameraChannel channel, u32* pEnable, + void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetCalibData() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetCalibDataFromDevice() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetCalibrationData() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetConfig(s32 handle, OrbisCameraConfig* pConfig) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetContrast(s32 handle, OrbisCameraChannel channel, u32* pContrast, + void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetDefectivePixelCancellation(s32 handle, OrbisCameraChannel channel, + u32* pEnable, void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetDeviceConfig() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetDeviceConfigWithoutHandle() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetDeviceID() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetDeviceIDWithoutOpen() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetDeviceInfo(s32 reserved, OrbisCameraDeviceInfo* pDeviceInfo) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetExposureGain(s32 handle, OrbisCameraChannel channel, + OrbisCameraExposureGain* pExposureGain, void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetFrameData(int handle, OrbisCameraFrameData* pFrameData) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetGamma(s32 handle, OrbisCameraChannel channel, OrbisCameraGamma* pGamma, + void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetHue(s32 handle, OrbisCameraChannel channel, s32* pHue, void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetLensCorrection(s32 handle, OrbisCameraChannel channel, u32* pEnable, + void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetMmapConnectedCount() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetProductInfo() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetRegister() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetRegistryInfo() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetSaturation(s32 handle, OrbisCameraChannel channel, u32* pSaturation, + void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetSharpness(s32 handle, OrbisCameraChannel channel, u32* pSharpness, + void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetVrCaptureInfo() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraGetWhiteBalance(s32 handle, OrbisCameraChannel channel, + OrbisCameraWhiteBalance* pWhiteBalance, void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraInitializeRegistryCalibData() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraIsAttached(s32 index) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraIsConfigChangeDone() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraIsValidFrameData(int handle, OrbisCameraFrameData* pFrameData) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraOpen(Libraries::UserService::OrbisUserServiceUserId userId, s32 type, + s32 index, OrbisCameraOpenParameter* pParam) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraOpenByModuleId() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraRemoveAppModuleFocus() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetAppModuleFocus() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetAttribute(s32 handle, OrbisCameraAttribute* pAttribute) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetAttributeInternal() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetAutoExposureGain(s32 handle, OrbisCameraChannel channel, u32 enable, + void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetAutoWhiteBalance(s32 handle, OrbisCameraChannel channel, u32 enable, + void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetCalibData() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetConfig(s32 handle, OrbisCameraConfig* pConfig) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetConfigInternal() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetContrast(s32 handle, OrbisCameraChannel channel, u32 contrast, + void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetDebugStop() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetDefectivePixelCancellation(s32 handle, OrbisCameraChannel channel, + u32 enable, void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetDefectivePixelCancellationInternal() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetExposureGain(s32 handle, OrbisCameraChannel channel, + OrbisCameraExposureGain* pExposureGain, void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetForceActivate() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetGamma(s32 handle, OrbisCameraChannel channel, OrbisCameraGamma* pGamma, + void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetHue(s32 handle, OrbisCameraChannel channel, s32 hue, void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetLensCorrection(s32 handle, OrbisCameraChannel channel, u32 enable, + void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetLensCorrectionInternal() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetProcessFocus() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetProcessFocusByHandle() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetRegister() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetSaturation(s32 handle, OrbisCameraChannel channel, u32 saturation, + void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetSharpness(s32 handle, OrbisCameraChannel channel, u32 sharpness, + void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetTrackerMode() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetUacModeInternal() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetVideoSync(s32 handle, OrbisCameraVideoSyncParameter* pVideoSync) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetVideoSyncInternal() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraSetWhiteBalance(s32 handle, OrbisCameraChannel channel, + OrbisCameraWhiteBalance* pWhiteBalance, void* pOption) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraStart(s32 handle, OrbisCameraStartParameter* pParam) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraStartByHandle() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraStop(s32 handle) { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCameraStopByHandle() { + LOG_ERROR(Lib_Camera, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceCamera(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("QhjrPkRPUZQ", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraAccGetData); + LIB_FUNCTION("UFonL7xopFM", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraAudioClose); + LIB_FUNCTION("fkZE7Hup2ro", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraAudioGetData); + LIB_FUNCTION("hftC5A1C8OQ", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraAudioGetData2); + LIB_FUNCTION("DhqqFiBU+6g", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraAudioOpen); + LIB_FUNCTION("wyU98EXAYxU", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraAudioReset); + LIB_FUNCTION("Y0pCDajzkVQ", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraChangeAppModuleState); + LIB_FUNCTION("OMS9LlcrvBo", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraClose); + LIB_FUNCTION("ztqH5qNTpTk", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraCloseByHandle); + LIB_FUNCTION("nBH6i2s4Glc", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraDeviceOpen); + LIB_FUNCTION("0btIPD5hg5A", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetAttribute); + LIB_FUNCTION("oEi6vM-3E2c", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraGetAutoExposureGain); + LIB_FUNCTION("qTPRMh4eY60", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraGetAutoWhiteBalance); + LIB_FUNCTION("hHA1frlMxYE", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetCalibData); + LIB_FUNCTION("5Oie5RArfWs", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraGetCalibDataFromDevice); + LIB_FUNCTION("RHYJ7GKOSMg", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraGetCalibrationData); + LIB_FUNCTION("ZaqmGEtYuL0", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetConfig); + LIB_FUNCTION("a5xFueMZIMs", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetContrast); + LIB_FUNCTION("tslCukqFE+E", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraGetDefectivePixelCancellation); + LIB_FUNCTION("DSOLCrc3Kh8", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetDeviceConfig); + LIB_FUNCTION("n+rFeP1XXyM", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraGetDeviceConfigWithoutHandle); + LIB_FUNCTION("jTJCdyv9GLU", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetDeviceID); + LIB_FUNCTION("-H3UwGQvNZI", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraGetDeviceIDWithoutOpen); + LIB_FUNCTION("WZpxnSAM-ds", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetDeviceInfo); + LIB_FUNCTION("ObIste7hqdk", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetExposureGain); + LIB_FUNCTION("mxgMmR+1Kr0", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetFrameData); + LIB_FUNCTION("WVox2rwGuSc", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetGamma); + LIB_FUNCTION("zrIUDKZx0iE", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetHue); + LIB_FUNCTION("XqYRHc4aw3w", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraGetLensCorrection); + LIB_FUNCTION("B260o9pSzM8", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraGetMmapConnectedCount); + LIB_FUNCTION("ULxbwqiYYuU", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetProductInfo); + LIB_FUNCTION("olojYZKYiYs", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetRegister); + LIB_FUNCTION("hawKak+Auw4", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetRegistryInfo); + LIB_FUNCTION("RTDOsWWqdME", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetSaturation); + LIB_FUNCTION("c6Fp9M1EXXc", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetSharpness); + LIB_FUNCTION("IAz2HgZQWzE", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetVrCaptureInfo); + LIB_FUNCTION("HX5524E5tMY", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetWhiteBalance); + LIB_FUNCTION("0wnf2a60FqI", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraInitializeRegistryCalibData); + LIB_FUNCTION("p6n3Npi3YY4", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraIsAttached); + LIB_FUNCTION("wQfd7kfRZvo", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraIsConfigChangeDone); + LIB_FUNCTION("U3BVwQl2R5Q", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraIsValidFrameData); + LIB_FUNCTION("BHn83xrF92E", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraOpen); + LIB_FUNCTION("eTywOSWsEiI", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraOpenByModuleId); + LIB_FUNCTION("py8p6kZcHmA", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraRemoveAppModuleFocus); + LIB_FUNCTION("j5isFVIlZLk", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraSetAppModuleFocus); + LIB_FUNCTION("doPlf33ab-U", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetAttribute); + LIB_FUNCTION("96F7zp1Xo+k", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraSetAttributeInternal); + LIB_FUNCTION("yfSdswDaElo", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraSetAutoExposureGain); + LIB_FUNCTION("zIKL4kZleuc", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraSetAutoWhiteBalance); + LIB_FUNCTION("LEMk5cTHKEA", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetCalibData); + LIB_FUNCTION("VQ+5kAqsE2Q", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetConfig); + LIB_FUNCTION("9+SNhbctk64", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraSetConfigInternal); + LIB_FUNCTION("3i5MEzrC1pg", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetContrast); + LIB_FUNCTION("vejouEusC7g", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetDebugStop); + LIB_FUNCTION("jMv40y2A23g", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraSetDefectivePixelCancellation); + LIB_FUNCTION("vER3cIMBHqI", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraSetDefectivePixelCancellationInternal); + LIB_FUNCTION("wgBMXJJA6K4", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetExposureGain); + LIB_FUNCTION("jeTpU0MqKU0", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetForceActivate); + LIB_FUNCTION("lhEIsHzB8r4", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetGamma); + LIB_FUNCTION("QI8GVJUy2ZY", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetHue); + LIB_FUNCTION("K7W7H4ZRwbc", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraSetLensCorrection); + LIB_FUNCTION("eHa3vhGu2rQ", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraSetLensCorrectionInternal); + LIB_FUNCTION("lS0tM6n+Q5E", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetProcessFocus); + LIB_FUNCTION("NVITuK83Z7o", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraSetProcessFocusByHandle); + LIB_FUNCTION("8MjO05qk5hA", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetRegister); + LIB_FUNCTION("bSKEi2PzzXI", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetSaturation); + LIB_FUNCTION("P-7MVfzvpsM", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetSharpness); + LIB_FUNCTION("3VJOpzKoIeM", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetTrackerMode); + LIB_FUNCTION("nnR7KAIDPv8", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraSetUacModeInternal); + LIB_FUNCTION("wpeyFwJ+UEI", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetVideoSync); + LIB_FUNCTION("8WtmqmE4edw", "libSceCamera", 1, "libSceCamera", 1, 1, + sceCameraSetVideoSyncInternal); + LIB_FUNCTION("k3zPIcgFNv0", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetWhiteBalance); + LIB_FUNCTION("9EpRYMy7rHU", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraStart); + LIB_FUNCTION("cLxF1QtHch0", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraStartByHandle); + LIB_FUNCTION("2G2C0nmd++M", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraStop); + LIB_FUNCTION("+X1Kgnn3bzg", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraStopByHandle); +}; + +} // namespace Libraries::Camera \ No newline at end of file diff --git a/src/core/libraries/camera/camera.h b/src/core/libraries/camera/camera.h new file mode 100644 index 000000000..51aa8b729 --- /dev/null +++ b/src/core/libraries/camera/camera.h @@ -0,0 +1,308 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Camera { + +constexpr int ORBIS_CAMERA_MAX_DEVICE_NUM = 2; +constexpr int ORBIS_CAMERA_MAX_FORMAT_LEVEL_NUM = 4; + +enum OrbisCameraChannel { + ORBIS_CAMERA_CHANNEL_0 = 1, + ORBIS_CAMERA_CHANNEL_1 = 2, + ORBIS_CAMERA_CHANNEL_BOTH = 3, +}; + +struct OrbisCameraOpenParameter { + u32 sizeThis; + u32 reserved1; + u32 reserved2; + u32 reserved3; +}; + +enum OrbisCameraConfigType { + ORBIS_CAMERA_CONFIG_TYPE1 = 0x01, + ORBIS_CAMERA_CONFIG_TYPE2 = 0x02, + ORBIS_CAMERA_CONFIG_TYPE3 = 0x03, + ORBIS_CAMERA_CONFIG_TYPE4 = 0x04, + ORBIS_CAMERA_CONFIG_TYPE5 = 0x05, + ORBIS_CAMERA_CONFIG_EXTENTION = 0x10, +}; + +enum OrbisCameraResolution { + ORBIS_CAMERA_RESOLUTION_1280X800 = 0x0, + ORBIS_CAMERA_RESOLUTION_640X400 = 0x1, + ORBIS_CAMERA_RESOLUTION_320X200 = 0x2, + ORBIS_CAMERA_RESOLUTION_160X100 = 0x3, + ORBIS_CAMERA_RESOLUTION_320X192 = 0x4, + ORBIS_CAMERA_RESOLUTION_SPECIFIED_WIDTH_HEIGHT, + ORBIS_CAMERA_RESOLUTION_UNKNOWN = 0xFF, +}; + +enum OrbisCameraFramerate { + ORBIS_CAMERA_FRAMERATE_UNKNOWN = 0, + ORBIS_CAMERA_FRAMERATE_7_5 = 7, + ORBIS_CAMERA_FRAMERATE_15 = 15, + ORBIS_CAMERA_FRAMERATE_30 = 30, + ORBIS_CAMERA_FRAMERATE_60 = 60, + ORBIS_CAMERA_FRAMERATE_120 = 120, + ORBIS_CAMERA_FRAMERATE_240 = 240, +}; + +enum OrbisCameraBaseFormat { + ORBIS_CAMERA_FORMAT_YUV422 = 0x0, + ORBIS_CAMERA_FORMAT_RAW16, + ORBIS_CAMERA_FORMAT_RAW8, + ORBIS_CAMERA_FORMAT_NO_USE = 0x10, + ORBIS_CAMERA_FORMAT_UNKNOWN = 0xFF, +}; + +enum OrbisCameraScaleFormat { + ORBIS_CAMERA_SCALE_FORMAT_YUV422 = 0x0, + ORBIS_CAMERA_SCALE_FORMAT_Y16 = 0x3, + ORBIS_CAMERA_SCALE_FORMAT_Y8, + ORBIS_CAMERA_SCALE_FORMAT_NO_USE = 0x10, + ORBIS_CAMERA_SCALE_FORMAT_UNKNOWN = 0xFF, +}; + +struct OrbisCameraFormat { + OrbisCameraBaseFormat formatLevel0; + OrbisCameraScaleFormat formatLevel1; + OrbisCameraScaleFormat formatLevel2; + OrbisCameraScaleFormat formatLevel3; +}; + +struct OrbisCameraConfigExtention { + OrbisCameraFormat format; + OrbisCameraResolution resolution; + OrbisCameraFramerate framerate; + u32 width; + u32 height; + u32 reserved1; + void* pBaseOption; +}; + +struct OrbisCameraConfig { + u32 sizeThis; + OrbisCameraConfigType configType; + OrbisCameraConfigExtention configExtention[ORBIS_CAMERA_MAX_DEVICE_NUM]; +}; + +enum OrbisCameraAecAgcTarget { + ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_DEF = 0x00, + ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_2_0 = 0x20, + ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_1_6 = 0x16, + ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_1_4 = 0x14, + ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_1_2 = 0x12, + ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_1_0 = 0x10, + ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_0_8 = 0x08, + ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_0_6 = 0x06, + ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_0_4 = 0x04, + ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_0_2 = 0x02, +}; + +struct OrbisCameraDeviceInfo { + u32 sizeThis; + u32 infoRevision; + u32 deviceRevision; + u32 padding; +}; + +struct OrbisCameraStartParameter { + u32 sizeThis; + u32 formatLevel[ORBIS_CAMERA_MAX_DEVICE_NUM]; + void* pStartOption; +}; + +struct OrbisCameraVideoSyncParameter { + u32 sizeThis; + u32 videoSyncMode; + void* pModeOption; +}; + +struct OrbisCameraFramePosition { + u32 x; + u32 y; + u32 xSize; + u32 ySize; +}; + +struct OrbisCameraAutoExposureGainTarget { + u32 sizeThis; + OrbisCameraAecAgcTarget target; +}; + +struct OrbisCameraExposureGain { + u32 exposureControl; + u32 exposure; + u32 gain; + u32 mode; +}; + +struct OrbisCameraWhiteBalance { + u32 whiteBalanceControl; + u32 gainRed; + u32 gainBlue; + u32 gainGreen; +}; + +struct OrbisCameraGamma { + u32 gammaControl; + u32 value; + u8 reserved[16]; +}; + +struct OrbisCameraMeta { + u32 metaMode; + u32 format[ORBIS_CAMERA_MAX_DEVICE_NUM][ORBIS_CAMERA_MAX_FORMAT_LEVEL_NUM]; + u64 frame[ORBIS_CAMERA_MAX_DEVICE_NUM]; + u64 timestamp[ORBIS_CAMERA_MAX_DEVICE_NUM]; + u32 deviceTimestamp[ORBIS_CAMERA_MAX_DEVICE_NUM]; + OrbisCameraExposureGain exposureGain[ORBIS_CAMERA_MAX_DEVICE_NUM]; + OrbisCameraWhiteBalance whiteBalance[ORBIS_CAMERA_MAX_DEVICE_NUM]; + OrbisCameraGamma gamma[ORBIS_CAMERA_MAX_DEVICE_NUM]; + u32 luminance[ORBIS_CAMERA_MAX_DEVICE_NUM]; + float acceleration_x; + float acceleration_y; + float acceleration_z; + u64 vcounter; + u32 reserved[14]; +}; + +struct OrbisCameraFrameData { + u32 sizeThis; + u32 readMode; + OrbisCameraFramePosition framePosition[ORBIS_CAMERA_MAX_DEVICE_NUM] + [ORBIS_CAMERA_MAX_FORMAT_LEVEL_NUM]; + void* pFramePointerList[ORBIS_CAMERA_MAX_DEVICE_NUM][ORBIS_CAMERA_MAX_FORMAT_LEVEL_NUM]; + u32 frameSize[ORBIS_CAMERA_MAX_DEVICE_NUM][ORBIS_CAMERA_MAX_FORMAT_LEVEL_NUM]; + u32 status[ORBIS_CAMERA_MAX_DEVICE_NUM]; + OrbisCameraMeta meta; + void* pFramePointerListGarlic[ORBIS_CAMERA_MAX_DEVICE_NUM][ORBIS_CAMERA_MAX_FORMAT_LEVEL_NUM]; +}; + +struct OrbisCameraAttribute { + u32 sizeThis; + OrbisCameraChannel channel; + OrbisCameraFramePosition framePosition; + OrbisCameraExposureGain exposureGain; + OrbisCameraWhiteBalance whiteBalance; + OrbisCameraGamma gamma; + u32 saturation; + u32 contrast; + u32 sharpness; + s32 hue; + u32 reserved1; + u32 reserved2; + u32 reserved3; + u32 reserved4; +}; + +s32 PS4_SYSV_ABI sceCameraAccGetData(); +s32 PS4_SYSV_ABI sceCameraAudioClose(); +s32 PS4_SYSV_ABI sceCameraAudioGetData(); +s32 PS4_SYSV_ABI sceCameraAudioGetData2(); +s32 PS4_SYSV_ABI sceCameraAudioOpen(); +s32 PS4_SYSV_ABI sceCameraAudioReset(); +s32 PS4_SYSV_ABI sceCameraChangeAppModuleState(); +s32 PS4_SYSV_ABI sceCameraClose(s32 handle); +s32 PS4_SYSV_ABI sceCameraCloseByHandle(); +s32 PS4_SYSV_ABI sceCameraDeviceOpen(); +s32 PS4_SYSV_ABI sceCameraGetAttribute(s32 handle, OrbisCameraAttribute* pAttribute); +s32 PS4_SYSV_ABI sceCameraGetAutoExposureGain(s32 handle, OrbisCameraChannel channel, u32* pEnable, + void* pOption); +s32 PS4_SYSV_ABI sceCameraGetAutoWhiteBalance(s32 handle, OrbisCameraChannel channel, u32* pEnable, + void* pOption); +s32 PS4_SYSV_ABI sceCameraGetCalibData(); +s32 PS4_SYSV_ABI sceCameraGetCalibDataFromDevice(); +s32 PS4_SYSV_ABI sceCameraGetCalibrationData(); +s32 PS4_SYSV_ABI sceCameraGetConfig(s32 handle, OrbisCameraConfig* pConfig); +s32 PS4_SYSV_ABI sceCameraGetContrast(s32 handle, OrbisCameraChannel channel, u32* pContrast, + void* pOption); +s32 PS4_SYSV_ABI sceCameraGetDefectivePixelCancellation(s32 handle, OrbisCameraChannel channel, + u32* pEnable, void* pOption); +s32 PS4_SYSV_ABI sceCameraGetDeviceConfig(); +s32 PS4_SYSV_ABI sceCameraGetDeviceConfigWithoutHandle(); +s32 PS4_SYSV_ABI sceCameraGetDeviceID(); +s32 PS4_SYSV_ABI sceCameraGetDeviceIDWithoutOpen(); +s32 PS4_SYSV_ABI sceCameraGetDeviceInfo(s32 reserved, OrbisCameraDeviceInfo* pDeviceInfo); +s32 PS4_SYSV_ABI sceCameraGetExposureGain(s32 handle, OrbisCameraChannel channel, + OrbisCameraExposureGain* pExposureGain, void* pOption); +s32 PS4_SYSV_ABI sceCameraGetFrameData(int handle, OrbisCameraFrameData* pFrameData); +s32 PS4_SYSV_ABI sceCameraGetGamma(s32 handle, OrbisCameraChannel channel, OrbisCameraGamma* pGamma, + void* pOption); +s32 PS4_SYSV_ABI sceCameraGetHue(s32 handle, OrbisCameraChannel channel, s32* pHue, void* pOption); +s32 PS4_SYSV_ABI sceCameraGetLensCorrection(s32 handle, OrbisCameraChannel channel, u32* pEnable, + void* pOption); +s32 PS4_SYSV_ABI sceCameraGetMmapConnectedCount(); +s32 PS4_SYSV_ABI sceCameraGetProductInfo(); +s32 PS4_SYSV_ABI sceCameraGetRegister(); +s32 PS4_SYSV_ABI sceCameraGetRegistryInfo(); +s32 PS4_SYSV_ABI sceCameraGetSaturation(s32 handle, OrbisCameraChannel channel, u32* pSaturation, + void* pOption); +s32 PS4_SYSV_ABI sceCameraGetSharpness(s32 handle, OrbisCameraChannel channel, u32* pSharpness, + void* pOption); +s32 PS4_SYSV_ABI sceCameraGetVrCaptureInfo(); +s32 PS4_SYSV_ABI sceCameraGetWhiteBalance(s32 handle, OrbisCameraChannel channel, + OrbisCameraWhiteBalance* pWhiteBalance, void* pOption); +s32 PS4_SYSV_ABI sceCameraInitializeRegistryCalibData(); +s32 PS4_SYSV_ABI sceCameraIsAttached(s32 index); +s32 PS4_SYSV_ABI sceCameraIsConfigChangeDone(); +s32 PS4_SYSV_ABI sceCameraIsValidFrameData(int handle, OrbisCameraFrameData* pFrameData); +s32 PS4_SYSV_ABI sceCameraOpen(Libraries::UserService::OrbisUserServiceUserId userId, s32 type, + s32 index, OrbisCameraOpenParameter* pParam); +s32 PS4_SYSV_ABI sceCameraOpenByModuleId(); +s32 PS4_SYSV_ABI sceCameraRemoveAppModuleFocus(); +s32 PS4_SYSV_ABI sceCameraSetAppModuleFocus(); +s32 PS4_SYSV_ABI sceCameraSetAttribute(s32 handle, OrbisCameraAttribute* pAttribute); +s32 PS4_SYSV_ABI sceCameraSetAttributeInternal(); +s32 PS4_SYSV_ABI sceCameraSetAutoExposureGain(s32 handle, OrbisCameraChannel channel, u32 enable, + void* pOption); +s32 PS4_SYSV_ABI sceCameraSetAutoWhiteBalance(s32 handle, OrbisCameraChannel channel, u32 enable, + void* pOption); +s32 PS4_SYSV_ABI sceCameraSetCalibData(); +s32 PS4_SYSV_ABI sceCameraSetConfig(s32 handle, OrbisCameraConfig* pConfig); +s32 PS4_SYSV_ABI sceCameraSetConfigInternal(); +s32 PS4_SYSV_ABI sceCameraSetContrast(s32 handle, OrbisCameraChannel channel, u32 contrast, + void* pOption); +s32 PS4_SYSV_ABI sceCameraSetDebugStop(); +s32 PS4_SYSV_ABI sceCameraSetDefectivePixelCancellation(s32 handle, OrbisCameraChannel channel, + u32 enable, void* pOption); +s32 PS4_SYSV_ABI sceCameraSetDefectivePixelCancellationInternal(); +s32 PS4_SYSV_ABI sceCameraSetExposureGain(s32 handle, OrbisCameraChannel channel, + OrbisCameraExposureGain* pExposureGain, void* pOption); +s32 PS4_SYSV_ABI sceCameraSetForceActivate(); +s32 PS4_SYSV_ABI sceCameraSetGamma(s32 handle, OrbisCameraChannel channel, OrbisCameraGamma* pGamma, + void* pOption); +s32 PS4_SYSV_ABI sceCameraSetHue(s32 handle, OrbisCameraChannel channel, s32 hue, void* pOption); +s32 PS4_SYSV_ABI sceCameraSetLensCorrection(s32 handle, OrbisCameraChannel channel, u32 enable, + void* pOption); +s32 PS4_SYSV_ABI sceCameraSetLensCorrectionInternal(); +s32 PS4_SYSV_ABI sceCameraSetProcessFocus(); +s32 PS4_SYSV_ABI sceCameraSetProcessFocusByHandle(); +s32 PS4_SYSV_ABI sceCameraSetRegister(); +s32 PS4_SYSV_ABI sceCameraSetSaturation(s32 handle, OrbisCameraChannel channel, u32 saturation, + void* pOption); +s32 PS4_SYSV_ABI sceCameraSetSharpness(s32 handle, OrbisCameraChannel channel, u32 sharpness, + void* pOption); +s32 PS4_SYSV_ABI sceCameraSetTrackerMode(); +s32 PS4_SYSV_ABI sceCameraSetUacModeInternal(); +s32 PS4_SYSV_ABI sceCameraSetVideoSync(s32 handle, OrbisCameraVideoSyncParameter* pVideoSync); +s32 PS4_SYSV_ABI sceCameraSetVideoSyncInternal(); +s32 PS4_SYSV_ABI sceCameraSetWhiteBalance(s32 handle, OrbisCameraChannel channel, + OrbisCameraWhiteBalance* pWhiteBalance, void* pOption); +s32 PS4_SYSV_ABI sceCameraStart(s32 handle, OrbisCameraStartParameter* pParam); +s32 PS4_SYSV_ABI sceCameraStartByHandle(); +s32 PS4_SYSV_ABI sceCameraStop(s32 handle); +s32 PS4_SYSV_ABI sceCameraStopByHandle(); + +void RegisterlibSceCamera(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Camera \ No newline at end of file diff --git a/src/core/libraries/camera/camera_error.h b/src/core/libraries/camera/camera_error.h new file mode 100644 index 000000000..acb04dd02 --- /dev/null +++ b/src/core/libraries/camera/camera_error.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +constexpr int ORBIS_CAMERA_ERROR_PARAM = 0x802E0000; +constexpr int ORBIS_CAMERA_ERROR_ALREADY_INIT = 0x802E0001; +constexpr int ORBIS_CAMERA_ERROR_NOT_INIT = 0x802E0002; +constexpr int ORBIS_CAMERA_ERROR_ALREADY_OPEN = 0x802E0003; +constexpr int ORBIS_CAMERA_ERROR_NOT_OPEN = 0x802E0004; +constexpr int ORBIS_CAMERA_ERROR_ALREADY_START = 0x802E0005; +constexpr int ORBIS_CAMERA_ERROR_NOT_START = 0x802E0006; +constexpr int ORBIS_CAMERA_ERROR_FORMAT_UNKNOWN = 0x802E0007; +constexpr int ORBIS_CAMERA_ERROR_RESOLUTION_UNKNOWN = 0x802E0008; +constexpr int ORBIS_CAMERA_ERROR_BAD_FRAMERATE = 0x802E0009; +constexpr int ORBIS_CAMERA_ERROR_TIMEOUT = 0x802E000A; +constexpr int ORBIS_CAMERA_ERROR_ATTRIBUTE_UNKNOWN = 0x802E000B; +constexpr int ORBIS_CAMERA_ERROR_BUSY = 0x802E000C; +constexpr int ORBIS_CAMERA_ERROR_UNKNOWN_CONFIG = 0x802E000D; +constexpr int ORBIS_CAMERA_ERROR_ALREADY_READ = 0x802E000F; +constexpr int ORBIS_CAMERA_ERROR_NOT_CONNECTED = 0x802E0010; +constexpr int ORBIS_CAMERA_ERROR_NOT_SUPPORTED = 0x802E0011; +constexpr int ORBIS_CAMERA_ERROR_INVALID_CONFIG = 0x802E0013; +constexpr int ORBIS_CAMERA_ERROR_MAX_HANDLE = 0x802E0014; +constexpr int ORBIS_CAMERA_ERROR_MAX_PROCESS = 0x802E00FB; +constexpr int ORBIS_CAMERA_ERROR_COPYOUT_FAILED = 0x802E00FC; +constexpr int ORBIS_CAMERA_ERROR_COPYIN_FAILED = 0x802E00FD; +constexpr int ORBIS_CAMERA_ERROR_KPROC_CREATE = 0x802E00FE; +constexpr int ORBIS_CAMERA_ERROR_FATAL = 0x802E00FF; \ No newline at end of file diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 25ac4921c..f2f40e0e3 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -505,9 +505,10 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti(u32* cmdbuf, u32 size, u32 da u32 flags) { LOG_TRACE(Lib_GnmDriver, "called"); - if ((!sceKernelIsNeoMode() || !UseNeoCompatSequences) && !cmdbuf && (size == 16) && - (shader_stage < ShaderStages::Max) && (vertex_sgpr_offset < 0x10u) && - (instance_sgpr_offset < 0x10u)) { + if ((!sceKernelIsNeoMode() || !UseNeoCompatSequences) && cmdbuf && (size == 16) && + (vertex_sgpr_offset < 0x10u) && (instance_sgpr_offset < 0x10u) && + (shader_stage == ShaderStages::Vs || shader_stage == ShaderStages::Es || + shader_stage == ShaderStages::Ls)) { cmdbuf = WriteHeader(cmdbuf, 2); cmdbuf = WriteBody(cmdbuf, 0u); @@ -535,10 +536,33 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti(u32* cmdbuf, u32 size, u32 da return -1; } -int PS4_SYSV_ABI sceGnmDrawIndexIndirectMulti() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - UNREACHABLE(); - return ORBIS_OK; +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) { + 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( + 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() { diff --git a/src/core/libraries/gnmdriver/gnmdriver.h b/src/core/libraries/gnmdriver/gnmdriver.h index 94d06c85f..a3d4968d3 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.h +++ b/src/core/libraries/gnmdriver/gnmdriver.h @@ -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 vertex_sgpr_offset, u32 instance_sgpr_offset, 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(); s32 PS4_SYSV_ABI sceGnmDrawIndexOffset(u32* cmdbuf, u32 size, u32 index_offset, u32 index_count, u32 flags); diff --git a/src/core/libraries/kernel/aio.cpp b/src/core/libraries/kernel/aio.cpp index e017010cb..1d746860b 100644 --- a/src/core/libraries/kernel/aio.cpp +++ b/src/core/libraries/kernel/aio.cpp @@ -19,7 +19,7 @@ namespace Libraries::Kernel { static s32* id_state; static s32 id_index; -s32 sceKernelAioInitializeImpl(void* p, s32 size) { +s32 PS4_SYSV_ABI sceKernelAioInitializeImpl(void* p, s32 size) { return 0; } diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index bc34dff98..ad372325c 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -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 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 append = (flags & ORBIS_KERNEL_O_APPEND) != 0; - bool fsync = (flags & ORBIS_KERNEL_O_FSYNC) != 0; - bool sync = (flags & ORBIS_KERNEL_O_SYNC) != 0; + // Flags fsync and sync behave the same + bool sync = (flags & ORBIS_KERNEL_O_SYNC) != 0 || (flags & ORBIS_KERNEL_O_FSYNC) != 0; bool create = (flags & ORBIS_KERNEL_O_CREAT) != 0; bool truncate = (flags & ORBIS_KERNEL_O_TRUNC) != 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 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}; u32 handle = h->CreateHandle(); auto* file = h->GetFile(handle); @@ -94,84 +104,127 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) { } } - if (directory) { - file->type = Core::FileSys::FileType::Directory; - file->m_guest_name = path; - file->m_host_name = mnt->GetHostPath(file->m_guest_name); - if (!std::filesystem::is_directory(file->m_host_name)) { // directory doesn't exist + bool read_only = false; + file->m_guest_name = path; + file->m_host_name = mnt->GetHostPath(file->m_guest_name, &read_only); + bool exists = std::filesystem::exists(file->m_host_name); + s32 e = 0; + + if (create) { + if (excl && exists) { + // Error if file exists 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; - } 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 { - file->m_guest_name = path; - file->m_host_name = mnt->GetHostPath(file->m_guest_name); - bool exists = std::filesystem::exists(file->m_host_name); - int e = 0; + file->type = Core::FileSys::FileType::Regular; - if (create) { - if (excl && exists) { - // 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 + if (truncate && read_only) { + // Can't open files with truncate flag in a read only directory h->DeleteHandle(handle); - *__Error() = POSIX_ENOENT; + *__Error() = POSIX_EROFS; 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) { // Read only 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) { // Write only - if (append) { - 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); - } + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write); } else if (rdwr) { // Read and write - if (append) { - 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; + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite); } } + + 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; return handle; } @@ -365,10 +418,10 @@ s64 PS4_SYSV_ABI posix_lseek(s32 fd, s64 offset, s32 whence) { origin = Common::FS::SeekOrigin::CurrentPosition; } else if (whence == 2) { origin = Common::FS::SeekOrigin::End; - } else if (whence == 3) { - origin = Common::FS::SeekOrigin::SeekHole; - } else if (whence == 4) { - origin = Common::FS::SeekOrigin::SeekData; + } else if (whence == 3 || whence == 4) { + // whence parameter belongs to an unsupported POSIX extension + *__Error() = POSIX_ENOTTY; + return -1; } else { // whence parameter is invalid *__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); - if (dir_name.empty() || !std::filesystem::is_directory(dir_name)) { - *__Error() = POSIX_ENOTDIR; + if (ro) { + *__Error() = POSIX_EROFS; return -1; } - if (ro) { - *__Error() = POSIX_EROFS; + if (dir_name.empty() || !std::filesystem::is_directory(dir_name)) { + *__Error() = POSIX_ENOTDIR; 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) { LOG_INFO(Kernel_Fs, "(PARTIAL) path = {}", path); auto* mnt = Common::Singleton::Instance(); - bool ro = false; - const auto path_name = mnt->GetHostPath(path, &ro); + const auto path_name = mnt->GetHostPath(path); std::memset(sb, 0, sizeof(OrbisKernelStat)); const bool is_dir = std::filesystem::is_directory(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; // TODO incomplete } - if (ro) { - sb->st_mode &= ~0000555u; - } return ORBIS_OK; } @@ -877,7 +926,7 @@ s32 PS4_SYSV_ABI sceKernelGetdirentries(s32 fd, char* buf, s32 nbytes, s64* base 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) { *__Error() = POSIX_EINVAL; 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}; 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) { ErrSceToPosix(result); return -1; @@ -908,7 +957,16 @@ s64 PS4_SYSV_ABI posix_pwrite(s32 fd, void* buf, size_t nbytes, s64 offset) { *__Error() = POSIX_EIO; return -1; } - return file->f.WriteRaw(buf, nbytes); + size_t total_written = 0; + for (int i = 0; i < iovcnt; i++) { + total_written += file->f.WriteRaw(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) { @@ -920,6 +978,15 @@ s64 PS4_SYSV_ABI sceKernelPwrite(s32 fd, void* buf, size_t nbytes, s64 offset) { 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) { if (path == nullptr) { *__Error() = POSIX_EINVAL; @@ -1017,7 +1084,10 @@ void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("sfKygSjIbI8", "libkernel", 1, "libkernel", 1, 1, getdirentries); LIB_FUNCTION("taRWhTJFTgE", "libkernel", 1, "libkernel", 1, 1, sceKernelGetdirentries); 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("mBd4AfLP+u8", "libkernel", 1, "libkernel", 1, 1, sceKernelPwritev); LIB_FUNCTION("AUXVxWeJU-A", "libkernel", 1, "libkernel", 1, 1, sceKernelUnlink); } diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp index 33602bfe8..c7eafe799 100644 --- a/src/core/libraries/kernel/kernel.cpp +++ b/src/core/libraries/kernel/kernel.cpp @@ -24,11 +24,16 @@ #include "core/libraries/kernel/threads/exception.h" #include "core/libraries/kernel/time.h" #include "core/libraries/libs.h" +#include "core/libraries/network/sys_net.h" #ifdef _WIN64 #include +#else +#include #endif #include +#include +#include #include "aio.h" namespace Libraries::Kernel { @@ -149,23 +154,23 @@ struct OrbisKernelUuid { u8 clockSeqLow; u8 node[6]; }; +static_assert(sizeof(OrbisKernelUuid) == 0x10); int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) { + if (!orbisUuid) { + return ORBIS_KERNEL_ERROR_EINVAL; + } #ifdef _WIN64 UUID uuid; - UuidCreate(&uuid); - orbisUuid->timeLow = uuid.Data1; - orbisUuid->timeMid = uuid.Data2; - orbisUuid->timeHiAndVersion = uuid.Data3; - orbisUuid->clockSeqHiAndReserved = uuid.Data4[0]; - orbisUuid->clockSeqLow = uuid.Data4[1]; - for (int i = 0; i < 6; i++) { - orbisUuid->node[i] = uuid.Data4[2 + i]; + if (UuidCreate(&uuid) != RPC_S_OK) { + return ORBIS_KERNEL_ERROR_EFAULT; } #else - LOG_ERROR(Kernel, "sceKernelUuidCreate: Add linux"); + uuid_t uuid; + uuid_generate(uuid); #endif - return 0; + std::memcpy(orbisUuid, &uuid, sizeof(OrbisKernelUuid)); + return ORBIS_OK; } int PS4_SYSV_ABI kernel_ioctl(int fd, u64 cmd, VA_ARGS) { @@ -196,10 +201,6 @@ const char* PS4_SYSV_ABI sceKernelGetFsSandboxRandomWord() { return path; } -int PS4_SYSV_ABI posix_connect() { - return -1; -} - int PS4_SYSV_ABI _sigprocmask() { return ORBIS_OK; } @@ -208,6 +209,24 @@ int PS4_SYSV_ABI posix_getpagesize() { return 16_KB; } +int PS4_SYSV_ABI posix_getsockname(Libraries::Net::OrbisNetId s, + Libraries::Net::OrbisNetSockaddr* addr, u32* paddrlen) { + auto* netcall = Common::Singleton::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) { + LOG_ERROR(Lib_Net, "return code : {:#x}", (u32)returncode); + return 0; + } + *Libraries::Kernel::__Error() = 0x20; + LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode); + return -1; +} void RegisterKernel(Core::Loader::SymbolsResolver* sym) { service_thread = std::jthread{KernelServiceThread}; @@ -225,7 +244,6 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) { LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard); LIB_FUNCTION("PfccT7qURYE", "libkernel", 1, "libkernel", 1, 1, kernel_ioctl); 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("Xjoosiw+XPI", "libkernel", 1, "libkernel", 1, 1, sceKernelUuidCreate); LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail); @@ -234,6 +252,24 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("k+AXqu2-eBc", "libScePosix", 1, "libkernel", 1, 1, posix_getpagesize); LIB_FUNCTION("NWtTN10cJzE", "libSceLibcInternalExt", 1, "libSceLibcInternal", 1, 1, 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, posix_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 diff --git a/src/core/libraries/kernel/kernel.h b/src/core/libraries/kernel/kernel.h index 58911727d..aaa22aec1 100644 --- a/src/core/libraries/kernel/kernel.h +++ b/src/core/libraries/kernel/kernel.h @@ -3,9 +3,6 @@ #pragma once -#include -#include -#include "common/string_literal.h" #include "common/types.h" #include "core/libraries/kernel/orbis_error.h" @@ -18,27 +15,23 @@ namespace Libraries::Kernel { void ErrSceToPosix(int result); int ErrnoToSceKernelError(int e); void SetPosixErrno(int e); +int* PS4_SYSV_ABI __Error(); -template -struct WrapperImpl; +template +struct OrbisWrapperImpl; -template -struct WrapperImpl { - static constexpr StringLiteral Name{name}; +template +struct OrbisWrapperImpl { static R PS4_SYSV_ABI wrap(Args... args) { u32 ret = f(args...); if (ret != 0) { - // LOG_ERROR(Lib_Kernel, "Function {} returned {}", std::string_view{name.value}, ret); ret += ORBIS_KERNEL_ERROR_UNKNOWN; } return ret; } }; -template -constexpr auto OrbisWrapper = WrapperImpl::wrap; - -#define ORBIS(func) WrapperImpl<#func, decltype(&func), func>::wrap +#define ORBIS(func) (Libraries::Kernel::OrbisWrapperImpl::wrap) int* PS4_SYSV_ABI __Error(); diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp index 8a0c91479..dd0e07302 100644 --- a/src/core/libraries/kernel/memory.cpp +++ b/src/core/libraries/kernel/memory.cpp @@ -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, size_t infoSize) { LOG_INFO(Kernel_Vmm, "called addr = {}, flags = {:#x}", fmt::ptr(addr), flags); - if (!addr) { - return ORBIS_KERNEL_ERROR_EACCES; - } auto* memory = Core::Memory::Instance(); return memory->VirtualQuery(std::bit_cast(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) { LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}, flags = {:#x}, alignment = {:#x}", fmt::ptr(*addr), len, flags, alignment); - if (addr == nullptr) { LOG_ERROR(Kernel_Vmm, "Address is invalid!"); 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(); const VAddr in_addr = reinterpret_cast(*addr); const auto map_flags = static_cast(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, @@ -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!"); return ORBIS_KERNEL_ERROR_EINVAL; } + if (!Common::Is16KBAligned(directMemoryStart)) { LOG_ERROR(Kernel_Vmm, "Start address is not 16KB aligned!"); return ORBIS_KERNEL_ERROR_EINVAL; } + if (alignment != 0) { if ((!std::has_single_bit(alignment) && !Common::Is16KBAligned(alignment))) { 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(*addr); const auto mem_prot = static_cast(prot); const auto map_flags = static_cast(flags); auto* memory = Core::Memory::Instance(); const auto ret = - memory->MapMemory(addr, in_addr, len, mem_prot, map_flags, Core::VMAType::Direct, "", false, - directMemoryStart, alignment); + memory->MapMemory(addr, in_addr, len, mem_prot, map_flags, Core::VMAType::Direct, name, + false, directMemoryStart, alignment); LOG_INFO(Kernel_Vmm, "out_addr = {}", fmt::ptr(*addr)); 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, s64 directMemoryStart, u64 alignment) { 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, @@ -210,17 +217,16 @@ s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t 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) { LOG_ERROR(Kernel_Vmm, "name is invalid!"); 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(*addr_in_out); const auto mem_prot = static_cast(prot); const auto map_flags = static_cast(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, 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) { @@ -304,7 +310,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_MAP_DIRECT: { result = sceKernelMapNamedDirectMemory(&entries[i].start, entries[i].length, entries[i].protection, flags, - static_cast(entries[i].offset), 0, ""); + static_cast(entries[i].offset), 0, "anon"); LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, offset = {:#x}, type = {}, " "result = {}", @@ -326,7 +332,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn } case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_MAP_FLEXIBLE: { result = sceKernelMapNamedFlexibleMemory(&entries[i].start, entries[i].length, - entries[i].protection, flags, ""); + entries[i].protection, flags, "anon"); LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, type = {}, " "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) { - 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) { LOG_ERROR(Kernel_Vmm, "name is invalid!"); 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(); memory->NameVirtualRange(std::bit_cast(addr), len, name); return ORBIS_OK; @@ -377,13 +383,12 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_ LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!"); return ORBIS_KERNEL_ERROR_EINVAL; } - const bool is_in_range = searchEnd - searchStart >= len; - if (len <= 0 || !Common::Is64KBAligned(len) || !is_in_range) { - LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!"); + if (len <= 0 || !Common::Is64KBAligned(len)) { + LOG_ERROR(Kernel_Vmm, "Provided length {:#x} is invalid!", len); return ORBIS_KERNEL_ERROR_EINVAL; } if (alignment != 0 && !Common::Is64KBAligned(alignment)) { - LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!"); + LOG_ERROR(Kernel_Vmm, "Alignment {:#x} is invalid!", alignment); return ORBIS_KERNEL_ERROR_EINVAL; } if (physAddrOut == nullptr) { @@ -391,8 +396,21 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_ return ORBIS_KERNEL_ERROR_EINVAL; } + const bool is_in_range = searchEnd - searchStart >= len; + if (searchEnd <= searchStart || searchEnd < len || !is_in_range) { + LOG_ERROR(Kernel_Vmm, + "Provided address range is too small!" + " searchStart = {:#x}, searchEnd = {:#x}, length = {:#x}", + searchStart, searchEnd, len); + return ORBIS_KERNEL_ERROR_ENOMEM; + } + auto* memory = Core::Memory::Instance(); PAddr phys_addr = memory->PoolExpand(searchStart, searchEnd, len, alignment); + if (phys_addr == -1) { + return ORBIS_KERNEL_ERROR_ENOMEM; + } + *physAddrOut = static_cast(phys_addr); LOG_INFO(Kernel_Vmm, @@ -407,10 +425,6 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t ali LOG_INFO(Kernel_Vmm, "addrIn = {}, len = {:#x}, alignment = {:#x}, flags = {:#x}", fmt::ptr(addrIn), len, alignment, flags); - if (addrIn == nullptr) { - LOG_ERROR(Kernel_Vmm, "Address is invalid!"); - return ORBIS_KERNEL_ERROR_EINVAL; - } if (len == 0 || !Common::Is2MBAligned(len)) { LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 2MB aligned!"); return ORBIS_KERNEL_ERROR_EINVAL; @@ -463,9 +477,61 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags) const VAddr pool_addr = reinterpret_cast(addr); auto* memory = Core::Memory::Instance(); - memory->PoolDecommit(pool_addr, len); - return ORBIS_OK; + return memory->PoolDecommit(pool_addr, len); +} + +s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry* entries, s32 count, + s32* num_processed, s32 flags) { + if (entries == nullptr) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + s32 result = ORBIS_OK; + s32 processed = 0; + + for (s32 i = 0; i < count; i++, processed++) { + OrbisKernelMemoryPoolBatchEntry entry = entries[i]; + switch (entry.opcode) { + case OrbisKernelMemoryPoolOpcode::Commit: { + result = sceKernelMemoryPoolCommit(entry.commit_params.addr, entry.commit_params.len, + entry.commit_params.type, entry.commit_params.prot, + entry.flags); + break; + } + case OrbisKernelMemoryPoolOpcode::Decommit: { + result = sceKernelMemoryPoolDecommit(entry.decommit_params.addr, + entry.decommit_params.len, entry.flags); + break; + } + case OrbisKernelMemoryPoolOpcode::Protect: { + result = sceKernelMProtect(entry.protect_params.addr, entry.protect_params.len, + entry.protect_params.prot); + break; + } + case OrbisKernelMemoryPoolOpcode::TypeProtect: { + result = sceKernelMTypeProtect( + entry.type_protect_params.addr, entry.type_protect_params.len, + entry.type_protect_params.type, entry.type_protect_params.prot); + break; + } + case OrbisKernelMemoryPoolOpcode::Move: { + UNREACHABLE_MSG("Unimplemented sceKernelMemoryPoolBatch opcode Move"); + } + default: { + result = ORBIS_KERNEL_ERROR_EINVAL; + break; + } + } + + if (result != ORBIS_OK) { + break; + } + } + + if (num_processed != nullptr) { + *num_processed = processed; + } + return result; } int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, size_t offset, @@ -599,6 +665,7 @@ void RegisterMemory(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("pU-QydtGcGY", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolReserve); LIB_FUNCTION("Vzl66WmfLvk", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolCommit); LIB_FUNCTION("LXo1tpFqJGs", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolDecommit); + LIB_FUNCTION("YN878uKRBbE", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolBatch); LIB_FUNCTION("BPE9s9vQQXo", "libkernel", 1, "libkernel", 1, 1, posix_mmap); LIB_FUNCTION("BPE9s9vQQXo", "libScePosix", 1, "libkernel", 1, 1, posix_mmap); diff --git a/src/core/libraries/kernel/memory.h b/src/core/libraries/kernel/memory.h index 400b6c3fc..3e2bf8de5 100644 --- a/src/core/libraries/kernel/memory.h +++ b/src/core/libraries/kernel/memory.h @@ -47,6 +47,8 @@ enum MemoryOpTypes : u32 { ORBIS_KERNEL_MAP_OP_TYPE_PROTECT = 4 }; +constexpr u32 ORBIS_KERNEL_MAXIMUM_NAME_LENGTH = 32; + struct OrbisQueryInfo { uintptr_t start; uintptr_t end; @@ -59,15 +61,15 @@ struct OrbisVirtualQueryInfo { size_t offset; s32 protection; s32 memory_type; - union { - BitField<0, 1, u32> is_flexible; - BitField<1, 1, u32> is_direct; - BitField<2, 1, u32> is_stack; - BitField<3, 1, u32> is_pooled; - BitField<4, 1, u32> is_committed; - }; - std::array name; + u8 is_flexible : 1; + u8 is_direct : 1; + u8 is_stack : 1; + u8 is_pooled : 1; + u8 is_committed : 1; + char name[ORBIS_KERNEL_MAXIMUM_NAME_LENGTH]; }; +static_assert(sizeof(OrbisVirtualQueryInfo) == 72, + "OrbisVirtualQueryInfo struct size is incorrect"); struct OrbisKernelBatchMapEntry { void* start; @@ -79,6 +81,48 @@ struct OrbisKernelBatchMapEntry { int operation; }; +enum class OrbisKernelMemoryPoolOpcode : u32 { + Commit = 1, + Decommit = 2, + Protect = 3, + TypeProtect = 4, + Move = 5, +}; + +struct OrbisKernelMemoryPoolBatchEntry { + OrbisKernelMemoryPoolOpcode opcode; + u32 flags; + union { + struct { + void* addr; + u64 len; + u8 prot; + u8 type; + } commit_params; + struct { + void* addr; + u64 len; + } decommit_params; + struct { + void* addr; + u64 len; + u8 prot; + } protect_params; + struct { + void* addr; + u64 len; + u8 prot; + u8 type; + } type_protect_params; + struct { + void* dest_addr; + void* src_addr; + u64 len; + } move_params; + uintptr_t padding[3]; + }; +}; + u64 PS4_SYSV_ABI sceKernelGetDirectMemorySize(); int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u64 len, u64 alignment, int memoryType, s64* physAddrOut); @@ -128,6 +172,8 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t ali void** addrOut); s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int prot, int flags); s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags); +s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry* entries, s32 count, + s32* num_processed, s32 flags); int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len); diff --git a/src/core/libraries/kernel/threads.h b/src/core/libraries/kernel/threads.h index 409136968..bcccf1695 100644 --- a/src/core/libraries/kernel/threads.h +++ b/src/core/libraries/kernel/threads.h @@ -17,6 +17,12 @@ int PS4_SYSV_ABI posix_pthread_attr_init(PthreadAttrT* attr); int PS4_SYSV_ABI posix_pthread_attr_destroy(PthreadAttrT* attr); +int PS4_SYSV_ABI posix_pthread_attr_getaffinity_np(const PthreadAttrT* pattr, size_t cpusetsize, + Cpuset* cpusetp); + +int PS4_SYSV_ABI posix_pthread_attr_setaffinity_np(PthreadAttrT* pattr, size_t cpusetsize, + const Cpuset* cpusetp); + int PS4_SYSV_ABI posix_pthread_create(PthreadT* thread, const PthreadAttrT* attr, PthreadEntryFunc start_routine, void* arg); @@ -35,7 +41,7 @@ public: this->func = std::move(func); PthreadAttrT attr{}; posix_pthread_attr_init(&attr); - posix_pthread_create(&thread, &attr, RunWrapper, this); + posix_pthread_create(&thread, &attr, HOST_CALL(RunWrapper), this); posix_pthread_attr_destroy(&attr); } diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index c4127ecf2..61310bfb5 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -6,6 +6,7 @@ #include "core/debug_state.h" #include "core/libraries/kernel/kernel.h" #include "core/libraries/kernel/posix_error.h" +#include "core/libraries/kernel/threads.h" #include "core/libraries/kernel/threads/pthread.h" #include "core/libraries/kernel/threads/thread_state.h" #include "core/libraries/libs.h" @@ -289,7 +290,12 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt /* Create thread */ new_thread->native_thr = Core::NativeThread(); int ret = new_thread->native_thr.Create(RunThread, new_thread, &new_thread->attr); + 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) { *thread = nullptr; } @@ -521,6 +527,85 @@ int PS4_SYSV_ABI posix_pthread_setcancelstate(PthreadCancelState state, 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; + } + + 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 + /* + u64 mask = cpuset->bits; + #ifdef _WIN64 + DWORD_PTR affinity_mask = static_cast(mask); + if (!SetThreadAffinityMask(reinterpret_cast(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(handle), sizeof(cpu_set_t), &cpu_set); + if (result != 0) { + return POSIX_EINVAL; + } + #endif + */ + return 0; +} + +int PS4_SYSV_ABI posix_pthread_getaffinity_np(PthreadT thread, size_t cpusetsize, Cpuset* cpusetp) { + if (thread == nullptr || cpusetp == nullptr) { + return POSIX_EINVAL; + } + auto* attr_ptr = &thread->attr; + return posix_pthread_attr_getaffinity_np(&attr_ptr, cpusetsize, cpusetp); +} + +int PS4_SYSV_ABI posix_pthread_setaffinity_np(PthreadT thread, size_t cpusetsize, + const Cpuset* cpusetp) { + if (thread == nullptr || cpusetp == nullptr) { + return POSIX_EINVAL; + } + auto* attr_ptr = &thread->attr; + if (const auto ret = posix_pthread_attr_setaffinity_np(&attr_ptr, cpusetsize, cpusetp)) { + return ret; + } + return thread->SetAffinity(thread->attr.cpuset); +} + +int PS4_SYSV_ABI scePthreadGetaffinity(PthreadT thread, u64* mask) { + Cpuset cpuset; + const int ret = posix_pthread_getaffinity_np(thread, sizeof(Cpuset), &cpuset); + if (ret == 0) { + *mask = cpuset.bits; + } + return ret; +} + +int PS4_SYSV_ABI scePthreadSetaffinity(PthreadT thread, const u64 mask) { + const Cpuset cpuset = {.bits = mask}; + return posix_pthread_setaffinity_np(thread, sizeof(Cpuset), &cpuset); +} + void RegisterThread(Core::Loader::SymbolsResolver* sym) { // Posix LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once); @@ -544,6 +629,8 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("Z4QosVuAsA0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_once); 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("Jb2uGFMr688", "libkernel", 1, "libkernel", 1, 1, posix_pthread_getaffinity_np); + LIB_FUNCTION("5KWrg7-ZqvE", "libkernel", 1, "libkernel", 1, 1, posix_pthread_setaffinity_np); // Orbis LIB_FUNCTION("14bOACANTBo", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_once)); @@ -566,6 +653,8 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("W0Hpm2X0uPE", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_setprio)); LIB_FUNCTION("rNhWz+lvOMU", "libkernel", 1, "libkernel", 1, 1, _sceKernelSetThreadDtors); LIB_FUNCTION("6XG4B33N09g", "libkernel", 1, "libkernel", 1, 1, sched_yield); + LIB_FUNCTION("rcrVFJsQWRY", "libkernel", 1, "libkernel", 1, 1, ORBIS(scePthreadGetaffinity)); + LIB_FUNCTION("bt3CTBKmGyI", "libkernel", 1, "libkernel", 1, 1, ORBIS(scePthreadSetaffinity)); } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/pthread.h b/src/core/libraries/kernel/threads/pthread.h index 089156776..ebcc4aed3 100644 --- a/src/core/libraries/kernel/threads/pthread.h +++ b/src/core/libraries/kernel/threads/pthread.h @@ -159,6 +159,7 @@ enum class SchedPolicy : u32 { struct Cpuset { u64 bits; + u64 _reserved; }; struct PthreadAttr { @@ -269,7 +270,7 @@ struct Pthread { bool no_cancel; bool cancel_async; bool cancelling; - Cpuset sigmask; + u64 sigmask; bool unblock_sigcancel; bool in_sigsuspend; bool force_exit; @@ -332,6 +333,8 @@ struct Pthread { return true; } } + + int SetAffinity(const Cpuset* cpuset); }; using PthreadT = Pthread*; diff --git a/src/core/libraries/kernel/threads/pthread_attr.cpp b/src/core/libraries/kernel/threads/pthread_attr.cpp index a8e60ccf8..71f6438a6 100644 --- a/src/core/libraries/kernel/threads/pthread_attr.cpp +++ b/src/core/libraries/kernel/threads/pthread_attr.cpp @@ -243,7 +243,7 @@ int PS4_SYSV_ABI posix_pthread_attr_getaffinity_np(const PthreadAttrT* pattr, si if (attr->cpuset != nullptr) memcpy(cpusetp, attr->cpuset, std::min(cpusetsize, attr->cpusetsize)); else - memset(cpusetp, -1, sizeof(Cpuset)); + memset(cpusetp, -1, cpusetsize); return 0; } @@ -259,30 +259,31 @@ int PS4_SYSV_ABI posix_pthread_attr_setaffinity_np(PthreadAttrT* pattr, size_t c if (cpusetsize == 0 || cpusetp == nullptr) { if (attr->cpuset != nullptr) { free(attr->cpuset); - attr->cpuset = NULL; + attr->cpuset = nullptr; attr->cpusetsize = 0; } return 0; } if (attr->cpuset == nullptr) { - attr->cpuset = (Cpuset*)calloc(1, sizeof(Cpuset)); + attr->cpuset = static_cast(calloc(1, sizeof(Cpuset))); attr->cpusetsize = sizeof(Cpuset); } - memcpy(attr->cpuset, cpusetp, sizeof(Cpuset)); + memcpy(attr->cpuset, cpusetp, std::min(cpusetsize, sizeof(Cpuset))); return 0; } -int PS4_SYSV_ABI scePthreadAttrGetaffinity(PthreadAttrT* param_1, Cpuset* mask) { +int PS4_SYSV_ABI scePthreadAttrGetaffinity(PthreadAttrT* attr, u64* mask) { Cpuset cpuset; - const int ret = posix_pthread_attr_getaffinity_np(param_1, 0x10, &cpuset); + const int ret = posix_pthread_attr_getaffinity_np(attr, sizeof(Cpuset), &cpuset); if (ret == 0) { - *mask = cpuset; + *mask = cpuset.bits; } return ret; } -int PS4_SYSV_ABI scePthreadAttrSetaffinity(PthreadAttrT* attr, const Cpuset mask) { - return posix_pthread_attr_setaffinity_np(attr, 0x10, &mask); +int PS4_SYSV_ABI scePthreadAttrSetaffinity(PthreadAttrT* attr, const u64 mask) { + const Cpuset cpuset = {.bits = mask}; + return posix_pthread_attr_setaffinity_np(attr, sizeof(Cpuset), &cpuset); } void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) { diff --git a/src/core/libraries/libc_internal/printf.h b/src/core/libraries/libc_internal/printf.h index fe63481a0..9c22e922c 100644 --- a/src/core/libraries/libc_internal/printf.h +++ b/src/core/libraries/libc_internal/printf.h @@ -56,7 +56,6 @@ #include #include -#include #include #include #include diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 3f5baf640..5ef4b259d 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -8,6 +8,7 @@ #include "core/libraries/audio/audioout.h" #include "core/libraries/audio3d/audio3d.h" #include "core/libraries/avplayer/avplayer.h" +#include "core/libraries/camera/camera.h" #include "core/libraries/disc_map/disc_map.h" #include "core/libraries/game_live_streaming/gamelivestreaming.h" #include "core/libraries/gnmdriver/gnmdriver.h" @@ -45,6 +46,7 @@ #include "core/libraries/save_data/savedata.h" #include "core/libraries/screenshot/screenshot.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/msgdialog.h" #include "core/libraries/system/posix.h" @@ -120,6 +122,8 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::Hmd::RegisterlibSceHmd(sym); Libraries::DiscMap::RegisterlibSceDiscMap(sym); Libraries::Ulobjmgr::RegisterlibSceUlobjmgr(sym); + Libraries::SigninDialog::RegisterlibSceSigninDialog(sym); + Libraries::Camera::RegisterlibSceCamera(sym); } } // namespace Libraries diff --git a/src/core/libraries/libs.h b/src/core/libraries/libs.h index aa5ba4a97..d9c8216a5 100644 --- a/src/core/libraries/libs.h +++ b/src/core/libraries/libs.h @@ -3,13 +3,9 @@ #pragma once -#include - -#include "common/logging/log.h" #include "core/loader/elf.h" #include "core/loader/symbols_resolver.h" - -#define W(foo) foo +#include "core/tls.h" #define LIB_FUNCTION(nid, lib, libversion, mod, moduleVersionMajor, moduleVersionMinor, function) \ { \ @@ -21,11 +17,11 @@ sr.module_version_major = moduleVersionMajor; \ sr.module_version_minor = moduleVersionMinor; \ sr.type = Core::Loader::SymbolType::Function; \ - auto func = reinterpret_cast(function); \ + auto func = reinterpret_cast(HOST_CALL(function)); \ sym->AddSymbol(sr, func); \ } -#define LIB_OBJ(nid, lib, libversion, mod, moduleVersionMajor, moduleVersionMinor, function) \ +#define LIB_OBJ(nid, lib, libversion, mod, moduleVersionMajor, moduleVersionMinor, obj) \ { \ Core::Loader::SymbolResolver sr{}; \ sr.name = nid; \ @@ -35,8 +31,7 @@ sr.module_version_major = moduleVersionMajor; \ sr.module_version_minor = moduleVersionMinor; \ sr.type = Core::Loader::SymbolType::Object; \ - auto func = reinterpret_cast(function); \ - sym->AddSymbol(sr, func); \ + sym->AddSymbol(sr, reinterpret_cast(obj)); \ } namespace Libraries { diff --git a/src/core/libraries/network/net.cpp b/src/core/libraries/network/net.cpp index 161fc5214..0ef4a84f5 100644 --- a/src/core/libraries/network/net.cpp +++ b/src/core/libraries/network/net.cpp @@ -10,16 +10,24 @@ #include #endif +#include #include "common/assert.h" #include "common/logging/log.h" +#include "common/singleton.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.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 { static thread_local int32_t net_errno = 0; +static bool g_isNetInitialized = true; // TODO init it properly + int PS4_SYSV_ABI in6addr_any() { LOG_ERROR(Lib_Net, "(STUBBED) called"); 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) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; + if (!g_isNetInitialized) { + 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() { @@ -121,8 +166,45 @@ int PS4_SYSV_ABI sceNetBandwidthControlSetPolicy() { } int PS4_SYSV_ABI sceNetBind(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; + if (!g_isNetInitialized) { + 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() { @@ -465,9 +547,46 @@ int PS4_SYSV_ABI sceNetConfigWlanSetDeviceConfig() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNetConnect() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetConnect(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen) { + if (!g_isNetInitialized) { + 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() { @@ -640,8 +759,15 @@ int PS4_SYSV_ABI sceNetGetIfnameNumList() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNetGetMacAddress() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); +int PS4_SYSV_ABI sceNetGetMacAddress(Libraries::NetCtl::OrbisNetEtherAddr* addr, int flags) { + if (addr == nullptr) { + LOG_ERROR(Lib_Net, "addr is null!"); + return ORBIS_NET_EINVAL; + } + auto* netinfo = Common::Singleton::Instance(); + netinfo->RetrieveEthernetAddr(); + memcpy(addr->data, netinfo->GetEthernetAddr().data(), 6); + return ORBIS_OK; } @@ -655,9 +781,46 @@ int PS4_SYSV_ABI sceNetGetNameToIndex() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNetGetpeername() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetGetpeername(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen) { + if (!g_isNetInitialized) { + 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() { @@ -681,13 +844,88 @@ int PS4_SYSV_ABI sceNetGetSockInfo6() { } int PS4_SYSV_ABI sceNetGetsockname(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; + if (!g_isNetInitialized) { + 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) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; + LOG_INFO(Lib_Net, "s={} level={} optname={}", s, level, optname); + if (!g_isNetInitialized) { + 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() { @@ -781,9 +1019,46 @@ int PS4_SYSV_ABI sceNetIoctl() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNetListen() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetListen(OrbisNetId s, int backlog) { + if (!g_isNetInitialized) { + 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() { @@ -829,20 +1104,131 @@ int PS4_SYSV_ABI sceNetPppoeStop() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNetRecv() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetRecv(OrbisNetId s, void* buf, u64 len, int flags) { + if (!g_isNetInitialized) { + 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, - OrbisNetSockaddr* addr, u32* paddrlen) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetRecvfrom(OrbisNetId s, void* buf, u64 len, int flags, OrbisNetSockaddr* addr, + u32* paddrlen) { + if (!g_isNetInitialized) { + 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() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetRecvmsg(OrbisNetId s, OrbisNetMsghdr* msg, int flags) { + if (!g_isNetInitialized) { + 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() { @@ -915,19 +1301,131 @@ int PS4_SYSV_ABI sceNetResolverStartNtoaMultipleRecordsEx() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNetSend() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetSend(OrbisNetId s, const void* buf, u64 len, int flags) { + if (!g_isNetInitialized) { + 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() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetSendmsg(OrbisNetId s, const OrbisNetMsghdr* msg, int flags) { + if (!g_isNetInitialized) { + 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() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetSendto(OrbisNetId s, const void* buf, u64 len, int flags, + const OrbisNetSockaddr* addr, u32 addrlen) { + 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() { @@ -950,9 +1448,48 @@ int PS4_SYSV_ABI sceNetSetDnsInfoToKernel() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNetSetsockopt() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetSetsockopt(OrbisNetId s, int level, int optname, const void* optval, + u32 optlen) { + LOG_INFO(Lib_Net, "s={} level={} optname={} optlen={}", s, level, optname, optlen); + 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() { @@ -1035,24 +1572,172 @@ int PS4_SYSV_ABI sceNetShowRouteWithMemory() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNetShutdown() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetShutdown(OrbisNetId s, int how) { + if (!g_isNetInitialized) { + 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) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +OrbisNetId PS4_SYSV_ABI sceNetSocket(const char* name, int family, int type, int protocol) { + if (!g_isNetInitialized) { + 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() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetSocketAbort(OrbisNetId s, int flags) { + if (!g_isNetInitialized) { + 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() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetSocketClose(OrbisNetId s) { + if (!g_isNetInitialized) { + 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() { diff --git a/src/core/libraries/network/net.h b/src/core/libraries/network/net.h index beef38b56..812ee6bd7 100644 --- a/src/core/libraries/network/net.h +++ b/src/core/libraries/network/net.h @@ -4,6 +4,7 @@ #pragma once #include "common/types.h" +#include "netctl.h" namespace Core::Loader { class SymbolsResolver; @@ -19,6 +20,63 @@ class SymbolsResolver; 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; struct OrbisNetSockaddr { @@ -27,6 +85,30 @@ struct OrbisNetSockaddr { 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_loopback(); 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 sceNetConfigWlanScan(); 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 sceNetDhcpdStart(); int PS4_SYSV_ABI sceNetDhcpdStop(); @@ -151,10 +233,10 @@ int PS4_SYSV_ABI sceNetGetIfList(); int PS4_SYSV_ABI sceNetGetIfListOnce(); int PS4_SYSV_ABI sceNetGetIfName(); 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 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 sceNetGetRouteInfo(); int PS4_SYSV_ABI sceNetGetSockInfo(); @@ -177,7 +259,7 @@ int PS4_SYSV_ABI sceNetInfoDumpStop(); int PS4_SYSV_ABI sceNetInit(); int PS4_SYSV_ABI sceNetInitParam(); 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 sceNetMemoryFree(); 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 sceNetPppoeStart(); int PS4_SYSV_ABI sceNetPppoeStop(); -int PS4_SYSV_ABI sceNetRecv(); -int PS4_SYSV_ABI sceNetRecvfrom(OrbisNetId s, void* buf, size_t len, int flags, - OrbisNetSockaddr* addr, u32* paddrlen); -int PS4_SYSV_ABI sceNetRecvmsg(); +int PS4_SYSV_ABI sceNetRecv(OrbisNetId s, void* buf, u64 len, int flags); +int PS4_SYSV_ABI sceNetRecvfrom(OrbisNetId s, void* buf, u64 len, int flags, OrbisNetSockaddr* addr, + u32* paddrlen); +int PS4_SYSV_ABI sceNetRecvmsg(OrbisNetId s, OrbisNetMsghdr* msg, int flags); int PS4_SYSV_ABI sceNetResolverAbort(); int PS4_SYSV_ABI sceNetResolverConnect(); int PS4_SYSV_ABI sceNetResolverConnectAbort(); @@ -205,14 +287,16 @@ int PS4_SYSV_ABI sceNetResolverStartNtoa(); int PS4_SYSV_ABI sceNetResolverStartNtoa6(); int PS4_SYSV_ABI sceNetResolverStartNtoaMultipleRecords(); int PS4_SYSV_ABI sceNetResolverStartNtoaMultipleRecordsEx(); -int PS4_SYSV_ABI sceNetSend(); -int PS4_SYSV_ABI sceNetSendmsg(); -int PS4_SYSV_ABI sceNetSendto(); +int PS4_SYSV_ABI sceNetSend(OrbisNetId s, const void* buf, u64 len, int flags); +int PS4_SYSV_ABI sceNetSendmsg(OrbisNetId s, const OrbisNetMsghdr* msg, int flags); +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 sceNetSetDns6InfoToKernel(); int PS4_SYSV_ABI sceNetSetDnsInfo(); 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 sceNetShowIfconfigForBuffer(); int PS4_SYSV_ABI sceNetShowIfconfigWithMemory(); @@ -229,10 +313,10 @@ int PS4_SYSV_ABI sceNetShowRoute6ForBuffer(); int PS4_SYSV_ABI sceNetShowRoute6WithMemory(); int PS4_SYSV_ABI sceNetShowRouteForBuffer(); int PS4_SYSV_ABI sceNetShowRouteWithMemory(); -int PS4_SYSV_ABI sceNetShutdown(); -int PS4_SYSV_ABI sceNetSocket(const char* name, int family, int type, int protocol); -int PS4_SYSV_ABI sceNetSocketAbort(); -int PS4_SYSV_ABI sceNetSocketClose(); +int PS4_SYSV_ABI sceNetShutdown(OrbisNetId s, int how); +OrbisNetId PS4_SYSV_ABI sceNetSocket(const char* name, int family, int type, int protocol); +int PS4_SYSV_ABI sceNetSocketAbort(OrbisNetId s, int flags); +int PS4_SYSV_ABI sceNetSocketClose(OrbisNetId s); int PS4_SYSV_ABI sceNetSyncCreate(); int PS4_SYSV_ABI sceNetSyncDestroy(); int PS4_SYSV_ABI sceNetSyncGet(); diff --git a/src/core/libraries/network/net_error.h b/src/core/libraries/network/net_error.h new file mode 100644 index 000000000..ab65300c0 --- /dev/null +++ b/src/core/libraries/network/net_error.h @@ -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; diff --git a/src/core/libraries/network/net_util.cpp b/src/core/libraries/network/net_util.cpp new file mode 100644 index 000000000..d0f0a81da --- /dev/null +++ b/src/core/libraries/network/net_util.cpp @@ -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 +#include +#include +typedef SOCKET net_socket; +typedef int socklen_t; +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +typedef int net_socket; +#endif +#if defined(__APPLE__) +#include +#include +#endif + +#include +#include +#include +#include +#include +#include "net_util.h" + +namespace NetUtil { + +const std::array& NetUtilInternal::GetEthernetAddr() const { + return ether_address; +} + +bool NetUtilInternal::RetrieveEthernetAddr() { + std::scoped_lock lock{m_mutex}; +#ifdef _WIN32 + std::vector adapter_infos(sizeof(IP_ADAPTER_INFO)); + ULONG size_infos = sizeof(IP_ADAPTER_INFO); + + if (GetAdaptersInfo(reinterpret_cast(adapter_infos.data()), &size_infos) == + ERROR_BUFFER_OVERFLOW) + adapter_infos.resize(size_infos); + + if (GetAdaptersInfo(reinterpret_cast(adapter_infos.data()), &size_infos) == + NO_ERROR && + size_infos) { + PIP_ADAPTER_INFO info = reinterpret_cast(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(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 \ No newline at end of file diff --git a/src/core/libraries/network/net_util.h b/src/core/libraries/network/net_util.h new file mode 100644 index 000000000..be9dc15a1 --- /dev/null +++ b/src/core/libraries/network/net_util.h @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/types.h" + +namespace NetUtil { + +class NetUtilInternal { +public: + explicit NetUtilInternal() = default; + ~NetUtilInternal() = default; + +private: + std::array ether_address{}; + std::mutex m_mutex; + +public: + const std::array& GetEthernetAddr() const; + bool RetrieveEthernetAddr(); +}; +} // namespace NetUtil \ No newline at end of file diff --git a/src/core/libraries/network/netctl.cpp b/src/core/libraries/network/netctl.cpp index 00d980663..38225c48c 100644 --- a/src/core/libraries/network/netctl.cpp +++ b/src/core/libraries/network/netctl.cpp @@ -12,11 +12,13 @@ #include #endif +#include #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/network/net_ctl_codes.h" #include "core/libraries/network/netctl.h" +#include "net_util.h" namespace Libraries::NetCtl { @@ -162,6 +164,14 @@ int PS4_SYSV_ABI sceNetCtlGetInfo(int code, OrbisNetCtlInfo* info) { case ORBIS_NET_CTL_INFO_DEVICE: info->device = ORBIS_NET_CTL_DEVICE_WIRED; break; + case ORBIS_NET_CTL_INFO_ETHER_ADDR: { + auto* netinfo = Common::Singleton::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: info->link = ORBIS_NET_CTL_LINK_DISCONNECTED; break; @@ -183,6 +193,7 @@ int PS4_SYSV_ABI sceNetCtlGetInfo(int code, OrbisNetCtlInfo* info) { } break; } + default: LOG_ERROR(Lib_NetCtl, "{} unsupported code", code); } diff --git a/src/core/libraries/network/netctl.h b/src/core/libraries/network/netctl.h index 4992fffa9..203c75822 100644 --- a/src/core/libraries/network/netctl.h +++ b/src/core/libraries/network/netctl.h @@ -49,8 +49,26 @@ union OrbisNetCtlInfo { // GetInfo codes 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_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_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 sceNetBweClearEventIpcInt(); diff --git a/src/core/libraries/network/p2p_sockets.cpp b/src/core/libraries/network/p2p_sockets.cpp new file mode 100644 index 000000000..4f678dace --- /dev/null +++ b/src/core/libraries/network/p2p_sockets.cpp @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "net.h" +#include "net_error.h" +#include "sockets.h" + +namespace Libraries::Net { + +int P2PSocket::Close() { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return 0; +} +int P2PSocket::SetSocketOptions(int level, int optname, const void* optval, u32 optlen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return 0; +} +int P2PSocket::GetSocketOptions(int level, int optname, void* optval, u32* optlen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return 0; +} + +int P2PSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return 0; +} + +int P2PSocket::Listen(int backlog) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return 0; +} + +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 0; +} + +int P2PSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return 0; +} + +} // namespace Libraries::Net \ No newline at end of file diff --git a/src/core/libraries/network/posix_sockets.cpp b/src/core/libraries/network/posix_sockets.cpp new file mode 100644 index 000000000..2df375262 --- /dev/null +++ b/src/core/libraries/network/posix_sockets.cpp @@ -0,0 +1,395 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#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(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() { + std::scoped_lock lock{m_mutex}; +#ifdef _WIN32 + auto out = closesocket(sock); +#else + auto out = ::close(sock); +#endif + return ConvertReturnErrorCode(out); +} + +int PosixSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) { + std::scoped_lock lock{m_mutex}; + sockaddr addr2; + convertOrbisNetSockaddrToPosix(addr, &addr2); + return ConvertReturnErrorCode(::bind(sock, &addr2, sizeof(sockaddr_in))); +} + +int PosixSocket::Listen(int backlog) { + std::scoped_lock lock{m_mutex}; + return ConvertReturnErrorCode(::listen(sock, backlog)); +} + +int PosixSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to, + u32 tolen) { + std::scoped_lock lock{m_mutex}; + 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) { + std::scoped_lock lock{m_mutex}; + 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) { + std::scoped_lock lock{m_mutex}; + 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(new_socket); + } + return nullptr; +} + +int PosixSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) { + std::scoped_lock lock{m_mutex}; + sockaddr addr2; + convertOrbisNetSockaddrToPosix(addr, &addr2); + return ::connect(sock, &addr2, sizeof(sockaddr_in)); +} + +int PosixSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) { + std::scoped_lock lock{m_mutex}; + 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) { + std::scoped_lock lock{m_mutex}; + level = ConvertLevels(level); + ::linger native_linger; + 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_LINGER: { + if (socket_type != ORBIS_NET_SOCK_STREAM) { + return ORBIS_NET_EPROCUNAVAIL; + } + if (optlen < sizeof(OrbisNetLinger)) { + LOG_ERROR(Lib_Net, "size missmatched! optlen = {} OrbisNetLinger={}", optlen, + sizeof(OrbisNetLinger)); + return ORBIS_NET_ERROR_EINVAL; + } + + const void* native_val = &native_linger; + u32 native_len = sizeof(native_linger); + native_linger.l_onoff = reinterpret_cast(optval)->l_onoff; + native_linger.l_linger = reinterpret_cast(optval)->l_linger; + return ConvertReturnErrorCode( + setsockopt(sock, level, SO_LINGER, (const char*)native_val, native_len)); + } + + 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); + case ORBIS_NET_IP_HDRINCL: { + if (socket_type != ORBIS_NET_SOCK_RAW) { + return ORBIS_NET_EPROCUNAVAIL; + } + return ConvertReturnErrorCode( + setsockopt(sock, level, optname, (const char*)optval, optlen)); + } + } + } 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) { + std::scoped_lock lock{m_mutex}; + 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 \ No newline at end of file diff --git a/src/core/libraries/network/sockets.h b/src/core/libraries/network/sockets.h new file mode 100644 index 000000000..c54e11e66 --- /dev/null +++ b/src/core/libraries/network/sockets.h @@ -0,0 +1,120 @@ +// 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 +#include +#include +typedef SOCKET net_socket; +typedef int socklen_t; +#else +#include +#include +#include +#include +#include +#include +#include +#include +typedef int net_socket; +#endif +#include +#include +#include +#include "net.h" + +namespace Libraries::Net { + +struct Socket; + +typedef std::shared_ptr SocketPtr; + +struct OrbisNetLinger { + s32 l_onoff; + s32 l_linger; +}; +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; + std::mutex m_mutex; +}; + +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; + int socket_type; + explicit PosixSocket(int domain, int type, int protocol) + : Socket(domain, type, protocol), sock(socket(domain, type, protocol)) { + socket_type = type; + } + 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 NetSockets; + NetSockets socks; + int next_sock_id = 0; +}; +} // namespace Libraries::Net \ No newline at end of file diff --git a/src/core/libraries/network/sys_net.cpp b/src/core/libraries/network/sys_net.cpp new file mode 100644 index 000000000..087632159 --- /dev/null +++ b/src/core/libraries/network/sys_net.cpp @@ -0,0 +1,228 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#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::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::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::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::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::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::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::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(family, type, protocol); + break; + case ORBIS_NET_SOCK_DGRAM_P2P: + case ORBIS_NET_SOCK_STREAM_P2P: + sock = std::make_shared(family, type, protocol); + break; + default: + UNREACHABLE_MSG("Unknown type {}", type); + } + auto* netcall = Common::Singleton::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::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::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::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 \ No newline at end of file diff --git a/src/core/libraries/network/sys_net.h b/src/core/libraries/network/sys_net.h new file mode 100644 index 000000000..4366ea0f8 --- /dev/null +++ b/src/core/libraries/network/sys_net.h @@ -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 \ No newline at end of file diff --git a/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp b/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp index a6ca8744d..edb5caa07 100644 --- a/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp +++ b/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp @@ -49,13 +49,11 @@ void SaveDialogResult::CopyTo(OrbisSaveDataDialogResult& result) const { result.mode = this->mode; result.result = this->result; result.buttonId = this->button_id; - if (mode == SaveDataDialogMode::LIST || ElfInfo::Instance().FirmwareVer() >= ElfInfo::FW_45) { - if (result.dirName != nullptr) { - result.dirName->data.FromString(this->dir_name); - } - if (result.param != nullptr && this->param.GetString(SaveParams::MAINTITLE).has_value()) { - result.param->FromSFO(this->param); - } + if (result.dirName != nullptr) { + result.dirName->data.FromString(this->dir_name); + } + if (result.param != nullptr && this->param.GetString(SaveParams::MAINTITLE).has_value()) { + result.param->FromSFO(this->param); } 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); using std::swap; - swap(state, other.state); - swap(status, other.status); - swap(result, other.result); + state = other.state; + other.state = nullptr; + status = other.status; + other.status = nullptr; + result = other.result; + other.result = nullptr; if (status && *status == Status::RUNNING) { first_render = true; AddLayer(this); diff --git a/src/core/libraries/save_data/dialog/savedatadialog_ui.h b/src/core/libraries/save_data/dialog/savedatadialog_ui.h index aa67e1f5f..dc97268f4 100644 --- a/src/core/libraries/save_data/dialog/savedatadialog_ui.h +++ b/src/core/libraries/save_data/dialog/savedatadialog_ui.h @@ -300,7 +300,8 @@ public: ~SaveDialogUi() override; SaveDialogUi(const SaveDialogUi& other) = delete; 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); diff --git a/src/core/libraries/signin_dialog/signindialog.cpp b/src/core/libraries/signin_dialog/signindialog.cpp new file mode 100644 index 000000000..0e4eb63a2 --- /dev/null +++ b/src/core/libraries/signin_dialog/signindialog.cpp @@ -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 diff --git a/src/core/libraries/signin_dialog/signindialog.h b/src/core/libraries/signin_dialog/signindialog.h new file mode 100644 index 000000000..8726ad1f6 --- /dev/null +++ b/src/core/libraries/signin_dialog/signindialog.h @@ -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 diff --git a/src/core/libraries/videodec/videodec.cpp b/src/core/libraries/videodec/videodec.cpp index 02ea61509..ae7d17560 100644 --- a/src/core/libraries/videodec/videodec.cpp +++ b/src/core/libraries/videodec/videodec.cpp @@ -17,10 +17,12 @@ int PS4_SYSV_ABI sceVideodecCreateDecoder(const OrbisVideodecConfigInfo* pCfgInf LOG_INFO(Lib_Videodec, "called"); if (!pCfgInfoIn || !pRsrcInfoIn || !pCtrlOut) { + LOG_ERROR(Lib_Videodec, "Invalid arguments"); return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER; } if (pCfgInfoIn->thisSize != sizeof(OrbisVideodecConfigInfo) || pRsrcInfoIn->thisSize != sizeof(OrbisVideodecResourceInfo)) { + LOG_ERROR(Lib_Videodec, "Invalid struct size"); return ORBIS_VIDEODEC_ERROR_STRUCT_SIZE; } @@ -37,15 +39,18 @@ int PS4_SYSV_ABI sceVideodecDecode(OrbisVideodecCtrl* pCtrlIn, OrbisVideodecPictureInfo* pPictureInfoOut) { LOG_TRACE(Lib_Videodec, "called"); if (!pCtrlIn || !pInputDataIn || !pPictureInfoOut) { + LOG_ERROR(Lib_Videodec, "Invalid arguments"); return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER; } if (pCtrlIn->thisSize != sizeof(OrbisVideodecCtrl) || pFrameBufferInOut->thisSize != sizeof(OrbisVideodecFrameBuffer)) { + LOG_ERROR(Lib_Videodec, "Invalid struct size"); return ORBIS_VIDEODEC_ERROR_STRUCT_SIZE; } VdecDecoder* decoder = (VdecDecoder*)pCtrlIn->handle; if (!decoder) { + LOG_ERROR(Lib_Videodec, "Invalid decoder handle"); return ORBIS_VIDEODEC_ERROR_HANDLE; } return decoder->Decode(*pInputDataIn, *pFrameBufferInOut, *pPictureInfoOut); @@ -56,6 +61,7 @@ int PS4_SYSV_ABI sceVideodecDeleteDecoder(OrbisVideodecCtrl* pCtrlIn) { VdecDecoder* decoder = (VdecDecoder*)pCtrlIn->handle; if (!decoder) { + LOG_ERROR(Lib_Videodec, "Invalid decoder handle"); return ORBIS_VIDEODEC_ERROR_HANDLE; } delete decoder; @@ -68,15 +74,18 @@ int PS4_SYSV_ABI sceVideodecFlush(OrbisVideodecCtrl* pCtrlIn, LOG_INFO(Lib_Videodec, "called"); if (!pFrameBufferInOut || !pPictureInfoOut) { + LOG_ERROR(Lib_Videodec, "Invalid arguments"); return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER; } if (pFrameBufferInOut->thisSize != sizeof(OrbisVideodecFrameBuffer) || pPictureInfoOut->thisSize != sizeof(OrbisVideodecPictureInfo)) { + LOG_ERROR(Lib_Videodec, "Invalid struct size"); return ORBIS_VIDEODEC_ERROR_STRUCT_SIZE; } VdecDecoder* decoder = (VdecDecoder*)pCtrlIn->handle; if (!decoder) { + LOG_ERROR(Lib_Videodec, "Invalid decoder handle"); return ORBIS_VIDEODEC_ERROR_HANDLE; } return decoder->Flush(*pFrameBufferInOut, *pPictureInfoOut); @@ -92,10 +101,12 @@ int PS4_SYSV_ABI sceVideodecQueryResourceInfo(const OrbisVideodecConfigInfo* pCf LOG_INFO(Lib_Videodec, "called"); if (!pCfgInfoIn || !pRsrcInfoOut) { + LOG_ERROR(Lib_Videodec, "Invalid arguments"); return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER; } if (pCfgInfoIn->thisSize != sizeof(OrbisVideodecConfigInfo) || pRsrcInfoOut->thisSize != sizeof(OrbisVideodecResourceInfo)) { + LOG_ERROR(Lib_Videodec, "Invalid struct size"); return ORBIS_VIDEODEC_ERROR_STRUCT_SIZE; } diff --git a/src/core/libraries/videodec/videodec2.cpp b/src/core/libraries/videodec/videodec2.cpp index a7e520b41..4f9379151 100644 --- a/src/core/libraries/videodec/videodec2.cpp +++ b/src/core/libraries/videodec/videodec2.cpp @@ -17,9 +17,11 @@ sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemIn LOG_INFO(Lib_Vdec2, "called"); if (!computeMemInfo) { + LOG_ERROR(Lib_Vdec2, "Invalid arguments"); return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; } if (computeMemInfo->thisSize != sizeof(OrbisVideodec2ComputeMemoryInfo)) { + LOG_ERROR(Lib_Vdec2, "Invalid struct size"); return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; } @@ -47,10 +49,12 @@ sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decode LOG_INFO(Lib_Vdec2, "called"); if (!decoderCfgInfo || !decoderMemInfo) { + LOG_ERROR(Lib_Vdec2, "Invalid arguments"); return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; } if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) || decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) { + LOG_ERROR(Lib_Vdec2, "Invalid struct size"); return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; } @@ -74,10 +78,12 @@ s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo LOG_INFO(Lib_Vdec2, "called"); if (!decoderCfgInfo || !decoderMemInfo || !decoder) { + LOG_ERROR(Lib_Vdec2, "Invalid arguments"); return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; } if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) || decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) { + LOG_ERROR(Lib_Vdec2, "Invalid struct size"); return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; } @@ -89,6 +95,7 @@ s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder) { LOG_INFO(Lib_Vdec2, "called"); if (!decoder) { + LOG_ERROR(Lib_Vdec2, "Invalid arguments"); return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; } @@ -103,13 +110,16 @@ s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder, LOG_TRACE(Lib_Vdec2, "called"); if (!decoder) { + LOG_ERROR(Lib_Vdec2, "Invalid decoder instance"); return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; } if (!inputData || !frameBuffer || !outputInfo) { + LOG_ERROR(Lib_Vdec2, "Invalid arguments"); return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; } if (inputData->thisSize != sizeof(OrbisVideodec2InputData) || frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer)) { + LOG_ERROR(Lib_Vdec2, "Invalid struct size"); return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; } @@ -122,13 +132,16 @@ s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder, LOG_INFO(Lib_Vdec2, "called"); if (!decoder) { + LOG_ERROR(Lib_Vdec2, "Invalid decoder instance"); return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; } if (!frameBuffer || !outputInfo) { + LOG_ERROR(Lib_Vdec2, "Invalid arguments"); return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; } if (frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer) || outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) { + LOG_ERROR(Lib_Vdec2, "Invalid struct size"); return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; } @@ -139,6 +152,7 @@ s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder) { LOG_INFO(Lib_Vdec2, "called"); if (!decoder) { + LOG_ERROR(Lib_Vdec2, "Invalid decoder instance"); return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; } @@ -150,12 +164,15 @@ s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outp LOG_TRACE(Lib_Vdec2, "called"); if (!outputInfo) { + LOG_ERROR(Lib_Vdec2, "Invalid arguments"); return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; } if (outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) { + LOG_ERROR(Lib_Vdec2, "Invalid struct size"); return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; } if (outputInfo->pictureCount == 0 || gPictureInfos.empty()) { + LOG_ERROR(Lib_Vdec2, "No picture info available"); return ORBIS_OK; } @@ -163,6 +180,7 @@ s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outp OrbisVideodec2AvcPictureInfo* picInfo = static_cast(p1stPictureInfoOut); if (picInfo->thisSize != sizeof(OrbisVideodec2AvcPictureInfo)) { + LOG_ERROR(Lib_Vdec2, "Invalid struct size"); return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; } *picInfo = gPictureInfos.back(); diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 0f86376af..eced87968 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -127,7 +127,7 @@ void Linker::Execute(const std::vector args) { } } params.entry_addr = module->GetEntryAddress(); - RunMainEntry(¶ms); + ExecuteGuest(RunMainEntry, ¶ms); }); } @@ -366,7 +366,8 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) { if (!addr) { // Module was just loaded by above code. Allocate TLS block for it. const u32 init_image_size = module->tls.init_image_size; - u8* dest = reinterpret_cast(heap_api->heap_malloc(module->tls.image_size)); + u8* dest = reinterpret_cast( + Core::ExecuteGuest(heap_api->heap_malloc, module->tls.image_size)); const u8* src = reinterpret_cast(module->tls.image_virtual_addr); std::memcpy(dest, src, init_image_size); std::memset(dest + init_image_size, 0, module->tls.image_size - init_image_size); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 494ffa70c..6438670d3 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -75,7 +75,8 @@ u64 MemoryManager::ClampRangeSize(VAddr virtual_addr, u64 size) { // Clamp size to the remaining size of the current VMA. 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; ++vma; @@ -96,6 +97,8 @@ u64 MemoryManager::ClampRangeSize(VAddr virtual_addr, u64 size) { bool MemoryManager::TryWriteBacking(void* address, const void* data, u32 num_bytes) { const VAddr virtual_addr = std::bit_cast(address); 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) { return false; } @@ -106,31 +109,42 @@ bool MemoryManager::TryWriteBacking(void* address, const void* data, u32 num_byt PAddr MemoryManager::PoolExpand(PAddr search_start, PAddr search_end, size_t size, u64 alignment) { std::scoped_lock lk{mutex}; + alignment = alignment > 0 ? alignment : 64_KB; auto dmem_area = FindDmemArea(search_start); + auto mapping_start = search_start > dmem_area->second.base + ? Common::AlignUp(search_start, alignment) + : Common::AlignUp(dmem_area->second.base, alignment); + auto mapping_end = mapping_start + size; - const auto is_suitable = [&] { - const auto aligned_base = alignment > 0 ? Common::AlignUp(dmem_area->second.base, alignment) - : dmem_area->second.base; - const auto alignment_size = aligned_base - dmem_area->second.base; - const auto remaining_size = - dmem_area->second.size >= alignment_size ? dmem_area->second.size - alignment_size : 0; - return dmem_area->second.is_free && remaining_size >= size; - }; - while (!is_suitable() && dmem_area->second.GetEnd() <= search_end) { + // Find the first free, large enough dmem area in the range. + while (!dmem_area->second.is_free || dmem_area->second.GetEnd() < mapping_end) { + // The current dmem_area isn't suitable, move to the next one. dmem_area++; - } - ASSERT_MSG(is_suitable(), "Unable to find free direct memory area: size = {:#x}", size); + if (dmem_area == dmem_map.end()) { + break; + } - // Align free position - PAddr free_addr = dmem_area->second.base; - free_addr = alignment > 0 ? Common::AlignUp(free_addr, alignment) : free_addr; + // Update local variables based on the new dmem_area + mapping_start = Common::AlignUp(dmem_area->second.base, alignment); + mapping_end = mapping_start + size; + } + + if (dmem_area == dmem_map.end()) { + // There are no suitable mappings in this range + LOG_ERROR(Kernel_Vmm, "Unable to find free direct memory area: size = {:#x}", size); + return -1; + } // Add the allocated region to the list and commit its pages. - auto& area = CarveDmemArea(free_addr, size)->second; + auto& area = CarveDmemArea(mapping_start, size)->second; area.is_free = false; area.is_pooled = true; - return free_addr; + + // Track how much dmem was allocated for pools. + pool_budget += size; + + return mapping_start; } PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size, u64 alignment, @@ -145,10 +159,12 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size, auto mapping_end = mapping_start + size; // Find the first free, large enough dmem area in the range. - while ((!dmem_area->second.is_free || dmem_area->second.GetEnd() < mapping_end) && - dmem_area != dmem_map.end()) { + while (!dmem_area->second.is_free || dmem_area->second.GetEnd() < mapping_end) { // The current dmem_area isn't suitable, move to the next one. dmem_area++; + if (dmem_area == dmem_map.end()) { + break; + } // Update local variables based on the new dmem_area mapping_start = Common::AlignUp(dmem_area->second.base, alignment); @@ -172,7 +188,6 @@ void MemoryManager::Free(PAddr phys_addr, size_t size) { std::scoped_lock lk{mutex}; 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. std::vector> remove_list; @@ -202,26 +217,32 @@ void MemoryManager::Free(PAddr phys_addr, size_t size) { int MemoryManager::PoolReserve(void** out_addr, VAddr virtual_addr, size_t size, MemoryMapFlags flags, u64 alignment) { std::scoped_lock lk{mutex}; - - virtual_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr; alignment = alignment > 0 ? alignment : 2_MB; - VAddr mapped_addr = alignment > 0 ? Common::AlignUp(virtual_addr, alignment) : virtual_addr; + VAddr min_address = Common::AlignUp(impl.SystemManagedVirtualBase(), alignment); + VAddr mapped_addr = Common::AlignUp(virtual_addr, alignment); // Fixed mapping means the virtual address must exactly match the provided one. if (True(flags & MemoryMapFlags::Fixed)) { - auto& vma = FindVMA(mapped_addr)->second; - // If the VMA is mapped, unmap the region first. - if (vma.IsMapped()) { + // Make sure we're mapping to a valid address + mapped_addr = mapped_addr > min_address ? mapped_addr : min_address; + auto vma = FindVMA(mapped_addr)->second; + size_t remaining_size = vma.base + vma.size - mapped_addr; + // If the VMA is mapped or there's not enough space, unmap the region first. + if (vma.IsMapped() || remaining_size < size) { UnmapMemoryImpl(mapped_addr, size); vma = FindVMA(mapped_addr)->second; } - const size_t remaining_size = vma.base + vma.size - mapped_addr; - ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size); } - // Find the first free area starting with provided virtual address. if (False(flags & MemoryMapFlags::Fixed)) { + // When MemoryMapFlags::Fixed is not specified, and mapped_addr is 0, + // search from address 0x200000000 instead. + mapped_addr = mapped_addr == 0 ? 0x200000000 : mapped_addr; 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 @@ -229,9 +250,8 @@ int MemoryManager::PoolReserve(void** out_addr, VAddr virtual_addr, size_t size, auto& new_vma = new_vma_handle->second; new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce); new_vma.prot = MemoryProt::NoAccess; - new_vma.name = ""; + new_vma.name = "anon"; new_vma.type = VMAType::PoolReserved; - MergeAdjacent(vma_map, new_vma_handle); *out_addr = std::bit_cast(mapped_addr); return ORBIS_OK; @@ -247,19 +267,22 @@ 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. if (True(flags & MemoryMapFlags::Fixed)) { - auto& vma = FindVMA(mapped_addr)->second; - // If the VMA is mapped, unmap the region first. - if (vma.IsMapped()) { + auto vma = FindVMA(mapped_addr)->second; + size_t remaining_size = vma.base + vma.size - mapped_addr; + // If the VMA is mapped or there's not enough space, unmap the region first. + if (vma.IsMapped() || remaining_size < size) { UnmapMemoryImpl(mapped_addr, size); vma = FindVMA(mapped_addr)->second; } - const size_t remaining_size = vma.base + vma.size - mapped_addr; - ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size); } // Find the first free area starting with provided virtual address. if (False(flags & MemoryMapFlags::Fixed)) { 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 @@ -267,7 +290,7 @@ int MemoryManager::Reserve(void** out_addr, VAddr virtual_addr, size_t size, Mem auto& new_vma = new_vma_handle->second; new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce); new_vma.prot = MemoryProt::NoAccess; - new_vma.name = ""; + new_vma.name = "anon"; new_vma.type = VMAType::Reserved; MergeAdjacent(vma_map, new_vma_handle); @@ -280,29 +303,51 @@ int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot) const u64 alignment = 64_KB; - // When virtual addr is zero, force it to virtual_base. The guest cannot pass Fixed - // flag so we will take the branch that searches for free (or reserved) mappings. - virtual_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr; + // Input addresses to PoolCommit are treated as fixed. VAddr mapped_addr = Common::AlignUp(virtual_addr, alignment); - // This should return SCE_KERNEL_ERROR_ENOMEM but shouldn't normally happen. - const auto& vma = FindVMA(mapped_addr)->second; - const size_t remaining_size = vma.base + vma.size - mapped_addr; - ASSERT_MSG(!vma.IsMapped() && remaining_size >= size); + auto& vma = FindVMA(mapped_addr)->second; + if (vma.type != VMAType::PoolReserved) { + // If we're attempting to commit non-pooled memory, return EINVAL + LOG_ERROR(Kernel_Vmm, "Attempting to commit non-pooled memory at {:#x}", mapped_addr); + return ORBIS_KERNEL_ERROR_EINVAL; + } - // Perform the mapping. - void* out_addr = impl.Map(mapped_addr, size, alignment, -1, false); - TRACK_ALLOC(out_addr, size, "VMEM"); + if (!vma.Contains(mapped_addr, size)) { + // If there's not enough space to commit, return EINVAL + LOG_ERROR(Kernel_Vmm, + "Pooled region {:#x} to {:#x} is not large enough to commit from {:#x} to {:#x}", + vma.base, vma.base + vma.size, mapped_addr, mapped_addr + size); + return ORBIS_KERNEL_ERROR_EINVAL; + } - auto& new_vma = CarveVMA(mapped_addr, size)->second; + if (pool_budget <= size) { + // If there isn't enough pooled memory to perform the mapping, return ENOMEM + LOG_ERROR(Kernel_Vmm, "Not enough pooled memory to perform mapping"); + return ORBIS_KERNEL_ERROR_ENOMEM; + } else { + // Track how much pooled memory this commit will take + pool_budget -= size; + } + + // Carve out the new VMA representing this mapping + const auto new_vma_handle = CarveVMA(mapped_addr, size); + auto& new_vma = new_vma_handle->second; new_vma.disallow_merge = false; new_vma.prot = prot; - new_vma.name = ""; + new_vma.name = "anon"; new_vma.type = Core::VMAType::Pooled; new_vma.is_exec = false; new_vma.phys_base = 0; - rasterizer->MapMemory(mapped_addr, size); + // Perform the mapping + void* out_addr = impl.Map(mapped_addr, size, alignment, -1, false); + TRACK_ALLOC(out_addr, size, "VMEM"); + + if (IsValidGpuMapping(mapped_addr, size)) { + rasterizer->MapMemory(mapped_addr, size); + } + return ORBIS_OK; } @@ -325,15 +370,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. if (True(flags & MemoryMapFlags::Fixed)) { - // This should return SCE_KERNEL_ERROR_ENOMEM but shouldn't normally happen. - const auto& vma = FindVMA(mapped_addr)->second; - const size_t remaining_size = vma.base + vma.size - mapped_addr; - ASSERT_MSG(!vma.IsMapped() && remaining_size >= size); + auto vma = FindVMA(mapped_addr)->second; + 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. + // 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. if (False(flags & MemoryMapFlags::Fixed)) { 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. @@ -353,7 +417,10 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M if (type == VMAType::Flexible) { flexible_usage += size; } - rasterizer->MapMemory(mapped_addr, size); + + if (IsValidGpuMapping(mapped_addr, size)) { + rasterizer->MapMemory(mapped_addr, size); + } return ORBIS_OK; } @@ -366,12 +433,18 @@ int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, Mem // Find first free area to map the file. if (False(flags & MemoryMapFlags::Fixed)) { 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)) { const auto& vma = FindVMA(virtual_addr)->second; 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. @@ -389,7 +462,7 @@ int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, Mem return ORBIS_OK; } -void MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) { +s32 MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) { std::scoped_lock lk{mutex}; const auto it = FindVMA(virtual_addr); @@ -404,7 +477,19 @@ void MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) { const auto start_in_vma = virtual_addr - vma_base_addr; const auto type = vma_base.type; - rasterizer->UnmapMemory(virtual_addr, size); + if (type != VMAType::PoolReserved && type != VMAType::Pooled) { + LOG_ERROR(Kernel_Vmm, "Attempting to decommit non-pooled memory!"); + return ORBIS_KERNEL_ERROR_EINVAL; + } + + if (type == VMAType::Pooled) { + // Track how much pooled memory is decommitted + pool_budget += size; + } + + if (IsValidGpuMapping(virtual_addr, size)) { + rasterizer->UnmapMemory(virtual_addr, size); + } // Mark region as free and attempt to coalesce it with neighbours. const auto new_it = CarveVMA(virtual_addr, size); @@ -413,13 +498,17 @@ void MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) { vma.prot = MemoryProt::NoAccess; vma.phys_base = 0; vma.disallow_merge = false; - vma.name = ""; + vma.name = "anon"; MergeAdjacent(vma_map, new_it); - // Unmap the memory region. - impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base, is_exec, - false, false); - TRACK_FREE(virtual_addr, "VMEM"); + if (type != VMAType::PoolReserved) { + // Unmap the memory region. + impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base, + is_exec, false, false); + TRACK_FREE(virtual_addr, "VMEM"); + } + + return ORBIS_OK; } s32 MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) { @@ -444,7 +533,10 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma if (type == VMAType::Flexible) { 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. const auto new_it = CarveVMA(virtual_addr, adjusted_size); @@ -471,6 +563,8 @@ s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, u64 size) { do { auto it = FindVMA(virtual_addr + unmapped_bytes); 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 = UnmapBytesFromEntry(virtual_addr + unmapped_bytes, vma_base, size - unmapped_bytes); ASSERT_MSG(unmapped > 0, "Failed to unmap memory, progress is impossible"); @@ -485,7 +579,10 @@ int MemoryManager::QueryProtection(VAddr addr, void** start, void** end, u32* pr const auto it = FindVMA(addr); 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) { *start = reinterpret_cast(vma.base); @@ -552,17 +649,23 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s s32 MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) { std::scoped_lock lk{mutex}; s64 protected_bytes = 0; + + auto aligned_addr = Common::AlignDown(addr, 16_KB); + auto aligned_size = Common::AlignUp(size + addr - aligned_addr, 16_KB); do { - auto it = FindVMA(addr + protected_bytes); + auto it = FindVMA(aligned_addr + protected_bytes); 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; - result = ProtectBytes(addr + protected_bytes, vma_base, size - protected_bytes, prot); + result = ProtectBytes(aligned_addr + protected_bytes, vma_base, + aligned_size - protected_bytes, prot); if (result < 0) { // ProtectBytes returned an error, return it return result; } protected_bytes += result; - } while (protected_bytes < size); + } while (protected_bytes < aligned_size); return ORBIS_OK; } @@ -571,8 +674,16 @@ int MemoryManager::VirtualQuery(VAddr addr, int flags, ::Libraries::Kernel::OrbisVirtualQueryInfo* info) { std::scoped_lock lk{mutex}; - auto it = FindVMA(addr); - if (it->second.type == VMAType::Free && flags == 1) { + // FindVMA on addresses before the vma_map return garbage data. + 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; } if (it->second.type == VMAType::Free) { @@ -585,15 +696,17 @@ int MemoryManager::VirtualQuery(VAddr addr, int flags, info->end = vma.base + vma.size; info->offset = vma.phys_base; info->protection = static_cast(vma.prot); - info->is_flexible.Assign(vma.type == VMAType::Flexible); - info->is_direct.Assign(vma.type == VMAType::Direct); - info->is_stack.Assign(vma.type == VMAType::Stack); - info->is_pooled.Assign(vma.type == VMAType::PoolReserved || vma.type == VMAType::Pooled); - info->is_committed.Assign(vma.IsMapped()); - vma.name.copy(info->name.data(), std::min(info->name.size(), vma.name.size())); + info->is_flexible = vma.type == VMAType::Flexible ? 1 : 0; + info->is_direct = vma.type == VMAType::Direct ? 1 : 0; + info->is_stack = vma.type == VMAType::Stack ? 1 : 0; + info->is_pooled = vma.type == VMAType::PoolReserved || vma.type == VMAType::Pooled ? 1 : 0; + info->is_committed = vma.IsMapped() ? 1 : 0; + + strncpy(info->name, vma.name.data(), ::Libraries::Kernel::ORBIS_KERNEL_MAXIMUM_NAME_LENGTH); + if (vma.type == VMAType::Direct) { 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; } else { info->memory_type = ::Libraries::Kernel::SCE_KERNEL_WB_ONION; @@ -607,11 +720,11 @@ int MemoryManager::DirectMemoryQuery(PAddr addr, bool find_next, std::scoped_lock lk{mutex}; 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++; } - 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!"); return ORBIS_KERNEL_ERROR_EACCES; } @@ -691,36 +804,56 @@ VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment) 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); - 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 (it->second.IsFree() && it->second.Contains(virtual_addr, size)) { return virtual_addr; } + // Search for the first free VMA that fits our mapping. - const auto is_suitable = [&] { + while (it != vma_map.end()) { if (!it->second.IsFree()) { - return false; + it++; + continue; } + const auto& vma = it->second; virtual_addr = Common::AlignUp(vma.base, alignment); // Sometimes the alignment itself might be larger than the VMA. 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; - return remaining_size >= size; - }; - while (!is_suitable()) { - ++it; + if (remaining_size >= size) { + return virtual_addr; + } + 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) { 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; ASSERT_MSG(vma.base <= virtual_addr, "Adding a mapping to already mapped region"); @@ -749,7 +882,7 @@ MemoryManager::VMAHandle MemoryManager::CarveVMA(VAddr virtual_addr, size_t size MemoryManager::DMemHandle MemoryManager::CarveDmemArea(PAddr addr, size_t size) { 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; ASSERT_MSG(area.base <= addr, "Adding an allocation to already allocated region"); @@ -804,7 +937,7 @@ int MemoryManager::GetDirectMemoryType(PAddr addr, int* directMemoryTypeOut, 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!"); return ORBIS_KERNEL_ERROR_ENOENT; } diff --git a/src/core/memory.h b/src/core/memory.h index a6a55e288..4920aa397 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -157,6 +157,12 @@ public: return impl.SystemReservedVirtualBase(); } + bool IsValidGpuMapping(VAddr virtual_addr, u64 size) { + // The PS4's GPU can only handle 40 bit addresses. + const VAddr max_gpu_address{0x10000000000}; + return virtual_addr + size < max_gpu_address; + } + bool IsValidAddress(const void* addr) const noexcept { const VAddr virtual_addr = reinterpret_cast(addr); const auto end_it = std::prev(vma_map.end()); @@ -186,13 +192,13 @@ public: int PoolCommit(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); int MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, MemoryMapFlags flags, uintptr_t fd, size_t offset); - void PoolDecommit(VAddr virtual_addr, size_t size); + s32 PoolDecommit(VAddr virtual_addr, size_t size); s32 UnmapMemory(VAddr virtual_addr, size_t size); @@ -268,6 +274,7 @@ private: size_t total_direct_size{}; size_t total_flexible_size{}; size_t flexible_usage{}; + size_t pool_budget{}; Vulkan::Rasterizer* rasterizer{}; friend class ::Core::Devtools::Widget::MemoryMapViewer; diff --git a/src/core/module.cpp b/src/core/module.cpp index cbe44457c..f31bbed6c 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -19,8 +19,7 @@ namespace Core { using EntryFunc = PS4_SYSV_ABI int (*)(size_t args, const void* argp, void* param); -static u64 LoadOffset = CODE_BASE_OFFSET; -static constexpr u64 CODE_BASE_INCR = 0x010000000u; +static constexpr u64 ModuleLoadBase = 0x800000000; static u64 GetAlignedSize(const elf_program_header& phdr) { 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) - : memory{memory_}, file{file_}, name{file.stem().string()} { + : memory{memory_}, file{file_}, name{file.filename().string()} { elf.Open(file); if (elf.IsElfFile()) { LoadModuleToMemory(max_tls_index); @@ -113,10 +112,8 @@ void Module::LoadModuleToMemory(u32& max_tls_index) { // Map module segments (and possible TLS trampolines) void** out_addr = reinterpret_cast(&base_virtual_addr); - memory->MapMemory(out_addr, memory->SystemReservedVirtualBase() + LoadOffset, - aligned_base_size + TrampolineSize, MemoryProt::CpuReadWrite, - MemoryMapFlags::Fixed, VMAType::Code, name, true); - LoadOffset += CODE_BASE_INCR * (1 + aligned_base_size / CODE_BASE_INCR); + memory->MapMemory(out_addr, ModuleLoadBase, aligned_base_size + TrampolineSize, + MemoryProt::CpuReadWrite, MemoryMapFlags::NoFlags, VMAType::Code, name, true); LOG_INFO(Core_Linker, "Loading module {} to {}", name, fmt::ptr(*out_addr)); #ifdef ARCH_X86_64 @@ -229,7 +226,7 @@ void Module::LoadModuleToMemory(u32& max_tls_index) { LOG_INFO(Core_Linker, "program entry addr ..........: {:#018x}", entry_addr); if (MemoryPatcher::g_eboot_address == 0) { - if (name == "eboot") { + if (name == "eboot.bin") { MemoryPatcher::g_eboot_address = base_virtual_addr; MemoryPatcher::g_eboot_image_size = base_size; MemoryPatcher::OnGameLoaded(); diff --git a/src/core/tls.h b/src/core/tls.h index 6edd6a297..e9e2b9e6a 100644 --- a/src/core/tls.h +++ b/src/core/tls.h @@ -3,6 +3,7 @@ #pragma once +#include #include "common/types.h" namespace Xbyak { @@ -41,10 +42,31 @@ Tcb* GetTcbBase(); /// Makes sure TLS is initialized for the thread before entering guest. void EnsureThreadInitialized(); +template +__attribute__((optnone)) void ClearStack() { + volatile void* buf = alloca(size); + memset(const_cast(buf), 0, size); + buf = nullptr; +} + template ReturnType ExecuteGuest(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), CallArgs&&... args) { EnsureThreadInitialized(); + // clear stack to avoid trash from EnsureThreadInitialized + ClearStack<13_KB>(); return func(std::forward(args)...); } +template +struct HostCallWrapperImpl; + +template +struct HostCallWrapperImpl { + static ReturnType PS4_SYSV_ABI wrap(Args... args) { + return func(args...); + } +}; + +#define HOST_CALL(func) (Core::HostCallWrapperImpl::wrap) + } // namespace Core diff --git a/src/emulator.cpp b/src/emulator.cpp index 1a71b99cb..ebb34054b 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -10,13 +10,16 @@ #include "common/logging/log.h" #ifdef ENABLE_QT_GUI #include -#include "common/memory_patcher.h" #endif #include "common/assert.h" #ifdef ENABLE_DISCORD_RPC #include "common/discord_rpc_handler.h" #endif +#ifdef _WIN32 +#include +#endif #include "common/elf_info.h" +#include "common/memory_patcher.h" #include "common/ntapi.h" #include "common/path_util.h" #include "common/polyfill_thread.h" @@ -46,27 +49,10 @@ Emulator::Emulator() { #ifdef _WIN32 Common::NtApi::Initialize(); SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS); -#endif - - // Create stdin/stdout/stderr - Common::Singleton::Instance()->CreateStdHandles(); - - // Defer until after logging is initialized. - memory = Core::Memory::Instance(); - controller = Common::Singleton::Instance(); - linker = Common::Singleton::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"); + // need to init this in order for winsock2 to work + WORD versionWanted = MAKEWORD(2, 2); + WSADATA wsaData; + WSAStartup(versionWanted, &wsaData); #endif } @@ -91,58 +77,93 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector::Instance(); - mnt->Mount(game_folder, "/app0"); + mnt->Mount(game_folder, "/app0", true); // 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 title; std::string app_version; u32 fw_version; Common::PSFAttributes psf_attributes{}; - - 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)) { + if (param_sfo_exists) { auto* param_sfo = Common::Singleton::Instance(); - const bool success = param_sfo->Open(param_sfo_path); - ASSERT_MSG(success, "Failed to open param.sfo"); + ASSERT_MSG(param_sfo->Open(param_sfo_path), "Failed to open param.sfo"); + const auto content_id = param_sfo->GetString("CONTENT_ID"); ASSERT_MSG(content_id.has_value(), "Failed to get CONTENT_ID"); + id = std::string(*content_id, 7, 9); - - if (Config::getSeparateLogFilesEnabled()) { - Common::Log::Initialize(id + ".log"); - Common::Log::Start(); + title = param_sfo->GetString("TITLE").value_or("Unknown title"); + fw_version = param_sfo->GetInteger("SYSTEM_VER").value_or(0x4700000); + app_version = param_sfo->GetString("APP_VER").value_or("Unknown version"); + 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()); - 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()); + // Initialize logging as soon as possible + if (!id.empty() && Config::getSeparateLogFilesEnabled()) { + Common::Log::Initialize(id + ".log"); + } else { + Common::Log::Initialize(); + } + Common::Log::Start(); + 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(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::Instance()->CreateStdHandles(); + + // Initialize components + memory = Core::Memory::Instance(); + controller = Common::Singleton::Instance(); + linker = Common::Singleton::Instance(); + + // Load renderdoc module + VideoCore::LoadRenderDoc(); + + // Initialize patcher and trophies + if (!id.empty()) { + MemoryPatcher::g_game_serial = id; Libraries::NpTrophy::game_serial = id; + const auto trophyDir = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / id / "TrophyFiles"; if (!std::filesystem::exists(trophyDir)) { @@ -151,41 +172,9 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorstart(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(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.game_serial = id; game_info.title = title; @@ -194,6 +183,11 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorGetHostPath("/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 window_title = ""; if (Common::g_is_release) { @@ -224,11 +218,15 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorMount(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; - if (!std::filesystem::exists(mount_temp_dir)) { - std::filesystem::create_directory(mount_temp_dir); + if (std::filesystem::exists(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"); const auto& mount_download_dir = @@ -273,6 +271,25 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorstart(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); window->InitTimers(); diff --git a/src/input/input_mouse.cpp b/src/input/input_mouse.cpp index c84d14b3f..5eb0aab3e 100644 --- a/src/input/input_mouse.cpp +++ b/src/input/input_mouse.cpp @@ -60,7 +60,7 @@ Uint32 MousePolling(void* param, Uint32 id, Uint32 interval) { float angle = atan2(d_y, d_x); float a_x = cos(angle) * output_speed, a_y = sin(angle) * output_speed; - if (d_x != 0 && d_y != 0) { + if (d_x != 0 || d_y != 0) { controller->Axis(0, axis_x, GetAxis(-0x80, 0x7f, a_x)); controller->Axis(0, axis_y, GetAxis(-0x80, 0x7f, a_y)); } else { diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 36037fd4c..8eeec3536 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -24,7 +24,6 @@ #include "main_window.h" #include "settings_dialog.h" -#include "video_core/renderer_vulkan/vk_instance.h" #ifdef ENABLE_DISCORD_RPC #include "common/discord_rpc_handler.h" #endif @@ -53,7 +52,6 @@ bool MainWindow::Init() { CreateConnects(); SetLastUsedTheme(); SetLastIconSizeBullet(); - GetPhysicalDevices(); // show ui setMinimumSize(720, 405); std::string window_title = ""; @@ -368,19 +366,6 @@ void MainWindow::CheckUpdateMain(bool checkSave) { } #endif -void MainWindow::GetPhysicalDevices() { - Vulkan::Instance instance(false, false); - auto physical_devices = instance.GetPhysicalDevices(); - for (const vk::PhysicalDevice physical_device : physical_devices) { - auto prop = physical_device.getProperties(); - QString name = QString::fromUtf8(prop.deviceName, -1); - if (prop.apiVersion < Vulkan::TargetVulkanApiVersion) { - name += tr(" * Unsupported Vulkan Version"); - } - m_physical_devices.push_back(name); - } -} - void MainWindow::CreateConnects() { connect(this, &MainWindow::WindowResized, this, &MainWindow::HandleResize); connect(ui->mw_searchbar, &QLineEdit::textChanged, this, &MainWindow::SearchGameTable); @@ -421,7 +406,7 @@ void MainWindow::CreateConnects() { &MainWindow::StartGame); connect(ui->configureAct, &QAction::triggered, this, [this]() { - auto settingsDialog = new SettingsDialog(m_physical_devices, m_compat_info, this); + auto settingsDialog = new SettingsDialog(m_compat_info, this); connect(settingsDialog, &SettingsDialog::LanguageChanged, this, &MainWindow::OnLanguageChanged); @@ -454,7 +439,7 @@ void MainWindow::CreateConnects() { }); connect(ui->settingsButton, &QPushButton::clicked, this, [this]() { - auto settingsDialog = new SettingsDialog(m_physical_devices, m_compat_info, this); + auto settingsDialog = new SettingsDialog(m_compat_info, this); connect(settingsDialog, &SettingsDialog::LanguageChanged, this, &MainWindow::OnLanguageChanged); diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index 5d05bfca4..a5ec08d36 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -60,7 +60,6 @@ private: void toggleFullscreen(); void CreateRecentGameActions(); void CreateDockWindows(); - void GetPhysicalDevices(); void LoadGameLists(); #ifdef ENABLE_UPDATER @@ -96,8 +95,6 @@ private: QScopedPointer m_elf_viewer; // Status Bar. QScopedPointer statusBar; - // Available GPU devices - std::vector m_physical_devices; PSF psf; diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 5ee802b0c..914cc5470 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include #include @@ -25,6 +26,7 @@ #include "common/logging/filter.h" #include "settings_dialog.h" #include "ui_settings_dialog.h" +#include "video_core/renderer_vulkan/vk_instance.h" QStringList languageNames = {"Arabic", "Czech", "Danish", @@ -67,8 +69,9 @@ QMap chooseHomeTabMap; int backgroundImageOpacitySlider_backup; int bgm_volume_backup; -SettingsDialog::SettingsDialog(std::span physical_devices, - std::shared_ptr m_compat_info, +static std::vector m_physical_devices; + +SettingsDialog::SettingsDialog(std::shared_ptr m_compat_info, QWidget* parent) : QDialog(parent), ui(new Ui::SettingsDialog) { ui->setupUi(this); @@ -89,9 +92,23 @@ SettingsDialog::SettingsDialog(std::span physical_devices, {tr("Input"), "Input"}, {tr("Paths"), "Paths"}, {tr("Debug"), "Debug"}}; + if (m_physical_devices.empty()) { + // Populate cache of physical devices. + Vulkan::Instance instance(false, false); + auto physical_devices = instance.GetPhysicalDevices(); + for (const vk::PhysicalDevice physical_device : physical_devices) { + auto prop = physical_device.getProperties(); + QString name = QString::fromUtf8(prop.deviceName, -1); + if (prop.apiVersion < Vulkan::TargetVulkanApiVersion) { + name += tr(" * Unsupported Vulkan Version"); + } + m_physical_devices.push_back(name); + } + } + // Add list of available GPUs ui->graphicsAdapterBox->addItem(tr("Auto Select")); // -1, auto selection - for (const auto& device : physical_devices) { + for (const auto& device : m_physical_devices) { ui->graphicsAdapterBox->addItem(device); } diff --git a/src/qt_gui/settings_dialog.h b/src/qt_gui/settings_dialog.h index 09aa2b855..cdf9be80e 100644 --- a/src/qt_gui/settings_dialog.h +++ b/src/qt_gui/settings_dialog.h @@ -20,8 +20,7 @@ class SettingsDialog; class SettingsDialog : public QDialog { Q_OBJECT public: - explicit SettingsDialog(std::span physical_devices, - std::shared_ptr m_compat_info, + explicit SettingsDialog(std::shared_ptr m_compat_info, QWidget* parent = nullptr); ~SettingsDialog(); diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index e0bc7e4fe..c657888bf 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -384,23 +384,23 @@ Nothing - Không có gì + Không chạy được Boots - Giày ủng + Chạy được Menus - Menu + Vào được menu Ingame - Trong game + Vào được trò chơi Playable - Có thể chơi + Chơi được @@ -411,7 +411,7 @@ D-Pad - D-Pad + D-Pad Up @@ -447,7 +447,7 @@ Common Config - Common Config + Cài Đặt Chung Use per-game configs @@ -551,26 +551,26 @@ Save - Save + Lưu Apply - Apply + Áp dụng Restore Defaults - Restore Defaults + Khôi Phục Mặc Định Cancel - Cancel + Hủy EditorDialog Edit Keyboard + Mouse and Controller input bindings - Edit Keyboard + Mouse and Controller input bindings + Tùy chỉnh phím được gán cho Bàn phím + Chuột và Tay cầm Use Per-Game configs @@ -578,7 +578,7 @@ Error - Error + Lỗi Could not open the file for reading @@ -590,15 +590,15 @@ Save Changes - Save Changes + Lưu Thay Đổi Do you want to save changes? - Do you want to save changes? + Bạn có muốn lưu thay đổi? Help - Help + Trợ giúp Do you want to reset your custom default config to the original default config? @@ -706,15 +706,15 @@ h - h + giờ m - m + phút s - s + giây Compatibility is untested @@ -722,23 +722,23 @@ Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + Trò chơi không được khởi chạy đúng cách / khiến giả lập bị văng Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + Trò chơi có thể khởi chạy, nhưng chẳng hiện gì cả Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + Trò chơi hiển thị được hình ảnh, nhưng không thể tiếp tục từ menu Game has game-breaking glitches or unplayable performance - Game has game-breaking glitches or unplayable performance + Trò chơi có lỗi ảnh hưởng đến trải nghiệm, hoặc hiệu năng khi chơi không ổn định Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + Trò chơi có thể được hoàn thành từ đầu đến cuối, hiệu năng ổn định và không có lỗi ảnh hưởng đến trải nghiệm Click to see details on github @@ -1170,19 +1170,19 @@ Save - Save + Lưu Apply - Apply + Áp dụng Restore Defaults - Restore Defaults + Khôi Phục Mặc Định Cancel - Cancel + Hủy @@ -1193,7 +1193,7 @@ Boot Game - Boot Game + Khởi Chạy Trò Chơi Check for Updates @@ -1201,7 +1201,7 @@ About shadPS4 - About shadPS4 + Thông Tin Về shadPS4 Configure... @@ -1213,23 +1213,23 @@ Open shadPS4 Folder - Open shadPS4 Folder + Mở Thư Mục Của shadPS4 Exit - Exit + Thoát Exit shadPS4 - Exit shadPS4 + Thoát shadPS4 Exit the application. - Exit the application. + Thoát ứng dụng. Show Game List - Show Game List + Hiển Thị Danh Sách Trò Chơi Game List Refresh diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 936f82cd6..9ebb842cc 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -270,6 +270,10 @@ void SetupCapabilities(const Info& info, const Profile& profile, EmitContext& ct if (info.has_image_query) { 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) { ctx.AddCapability(spv::Capability::GroupNonUniform); } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp index 211899714..c3799fb4b 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp @@ -75,6 +75,14 @@ Id ImageAtomicU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id va const auto [scope, semantics]{AtomicArgs(ctx)}; 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 Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id offset, Id value) { @@ -187,6 +195,40 @@ Id EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords 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) { // TODO: This is not yet implemented throw NotImplementedException("SPIR-V Instruction"); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index e4071bb95..83e8afd78 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -529,7 +529,7 @@ void EmitStoreBufferBoundsCheck(EmitContext& ctx, Id index, Id buffer_size, auto // Bounds checking enabled, wrap in a conditional branch. auto compare_index = index; 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_label = ctx.OpLabel(); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 079f1005d..269f372d5 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -482,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 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 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 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); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 8433251ff..2640030df 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -869,6 +869,7 @@ void EmitContext::DefineImagesAndSamplers() { } if (std::ranges::any_of(info.images, &ImageResource::is_atomic)) { image_u32 = TypePointer(spv::StorageClass::Image, U32[1]); + image_f32 = TypePointer(spv::StorageClass::Image, F32[1]); } if (info.samplers.empty()) { return; diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index 784748658..38d55e0e4 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -207,6 +207,7 @@ public: Id invocation_id{}; Id subgroup_local_invocation_id{}; Id image_u32{}; + Id image_f32{}; Id shared_memory_u8{}; Id shared_memory_u16{}; diff --git a/src/shader_recompiler/frontend/translate/translate.cpp b/src/shader_recompiler/frontend/translate/translate.cpp index c5a5814a4..e49f95d9a 100644 --- a/src/shader_recompiler/frontend/translate/translate.cpp +++ b/src/shader_recompiler/frontend/translate/translate.cpp @@ -517,7 +517,9 @@ void Translator::EmitFetch(const GcnInst& inst) { const auto values = ir.CompositeConstruct(ir.GetAttribute(attr, 0), ir.GetAttribute(attr, 1), 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++) { ir.SetVectorReg(dst_reg++, IR::F32{ir.CompositeExtract(swizzled, i)}); } diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index ed7788d8c..5639bc56a 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -115,8 +115,12 @@ void Translator::EmitVectorMemory(const GcnInst& inst) { return IMAGE_ATOMIC(AtomicOp::Smin, inst); case Opcode::IMAGE_ATOMIC_UMIN: return IMAGE_ATOMIC(AtomicOp::Umin, inst); + case Opcode::IMAGE_ATOMIC_FMIN: + return IMAGE_ATOMIC(AtomicOp::Fmin, inst); case Opcode::IMAGE_ATOMIC_SMAX: return IMAGE_ATOMIC(AtomicOp::Smax, inst); + case Opcode::IMAGE_ATOMIC_FMAX: + return IMAGE_ATOMIC(AtomicOp::Fmax, inst); case Opcode::IMAGE_ATOMIC_UMAX: return IMAGE_ATOMIC(AtomicOp::Umax, inst); 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_O: case Opcode::IMAGE_SAMPLE_L_O: + case Opcode::IMAGE_SAMPLE_B_O: case Opcode::IMAGE_SAMPLE_LZ_O: case Opcode::IMAGE_SAMPLE_C_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); case AtomicOp::Umax: 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: return ir.ImageAtomicAnd(handle, body, value, info); case AtomicOp::Or: diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index 8dcf9c5c4..ba28d7e43 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -62,7 +62,14 @@ struct BufferResource { } bool IsStorage(const AmdGpu::Buffer& buffer, const Profile& profile) const noexcept { - return buffer.GetSize() > profile.max_ubo_size || is_written; + // When using uniform buffers, a size is required at compilation time, so we need to + // either compile a lot of shader specializations to handle each size or just force it to + // the maximum possible size always. However, for some vendors the shader-supplied size is + // used for bounds checking uniform buffer accesses, so the latter would effectively turn + // off buffer robustness behavior. Instead, force storage buffers which are bounds checked + // using the actual buffer size. We are assuming the performance hit from this is + // acceptable. + return true; // buffer.GetSize() > profile.max_ubo_size || is_written; } [[nodiscard]] constexpr AmdGpu::Buffer GetSharp(const Info& info) const noexcept; @@ -196,6 +203,7 @@ struct Info { bool has_discard{}; bool has_image_gather{}; bool has_image_query{}; + bool uses_atomic_float_min_max{}; bool uses_lane_id{}; bool uses_group_quad{}; bool uses_group_ballot{}; @@ -280,6 +288,11 @@ constexpr AmdGpu::Image ImageResource::GetSharp(const Info& info) const noexcept // Fall back to null image if unbound. 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; } diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index e1ebf2206..01d945178 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -1870,6 +1870,16 @@ Value IREmitter::ImageAtomicUMax(const Value& handle, const Value& coords, const 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, bool is_signed, TextureInstInfo info) { return is_signed ? ImageAtomicSMax(handle, coords, value, info) diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index d978b3b4f..8f8a12736 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -321,6 +321,10 @@ public: const Value& value, TextureInstInfo info); [[nodiscard]] Value ImageAtomicUMax(const Value& handle, const Value& coords, 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, const Value& value, bool is_signed, TextureInstInfo info); [[nodiscard]] Value ImageAtomicInc(const Value& handle, const Value& coords, const Value& value, diff --git a/src/shader_recompiler/ir/microinstruction.cpp b/src/shader_recompiler/ir/microinstruction.cpp index 580156f5b..a57310fb9 100644 --- a/src/shader_recompiler/ir/microinstruction.cpp +++ b/src/shader_recompiler/ir/microinstruction.cpp @@ -94,6 +94,8 @@ bool Inst::MayHaveSideEffects() const noexcept { case Opcode::ImageAtomicUMin32: case Opcode::ImageAtomicSMax32: case Opcode::ImageAtomicUMax32: + case Opcode::ImageAtomicFMax32: + case Opcode::ImageAtomicFMin32: case Opcode::ImageAtomicInc32: case Opcode::ImageAtomicDec32: case Opcode::ImageAtomicAnd32: diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index 6f186808c..ab6dbfde9 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -420,6 +420,8 @@ OPCODE(ImageAtomicSMin32, U32, Opaq OPCODE(ImageAtomicUMin32, U32, Opaque, Opaque, U32, ) OPCODE(ImageAtomicSMax32, 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(ImageAtomicDec32, U32, Opaque, Opaque, U32, ) OPCODE(ImageAtomicAnd32, U32, Opaque, Opaque, U32, ) diff --git a/src/shader_recompiler/ir/passes/lower_buffer_format_to_raw.cpp b/src/shader_recompiler/ir/passes/lower_buffer_format_to_raw.cpp index 658a495bc..65be02541 100644 --- a/src/shader_recompiler/ir/passes/lower_buffer_format_to_raw.cpp +++ b/src/shader_recompiler/ir/passes/lower_buffer_format_to_raw.cpp @@ -206,7 +206,8 @@ static void LowerBufferFormatInst(IR::Block& block, IR::Inst& inst, Info& info) .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()) + .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), }; diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index 778da149f..cc0bf83d3 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -101,6 +101,8 @@ bool IsImageAtomicInstruction(const IR::Inst& inst) { case IR::Opcode::ImageAtomicUMin32: case IR::Opcode::ImageAtomicSMax32: case IR::Opcode::ImageAtomicUMax32: + case IR::Opcode::ImageAtomicFMax32: + case IR::Opcode::ImageAtomicFMin32: case IR::Opcode::ImageAtomicInc32: case IR::Opcode::ImageAtomicDec32: 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!"); 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); const bool is_written = inst.GetOpcode() == IR::Opcode::ImageWrite; diff --git a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp index d739b2da5..f53a0f4d4 100644 --- a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp +++ b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp @@ -71,6 +71,10 @@ void Visit(Info& info, const IR::Inst& inst) { case IR::Opcode::ImageQueryLod: info.has_image_query = true; break; + case IR::Opcode::ImageAtomicFMax32: + case IR::Opcode::ImageAtomicFMin32: + info.uses_atomic_float_min_max = true; + break; case IR::Opcode::LaneId: info.uses_lane_id = true; break; diff --git a/src/shader_recompiler/ir/reinterpret.h b/src/shader_recompiler/ir/reinterpret.h index b65b19928..99819cbb9 100644 --- a/src/shader_recompiler/ir/reinterpret.h +++ b/src/shader_recompiler/ir/reinterpret.h @@ -34,6 +34,18 @@ inline F32 ApplyReadNumberConversion(IREmitter& ir, const F32& value, case AmdGpu::NumberConversion::UnormToUbnorm: // Convert 0...1 to -1...1 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(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::max())); + return ir.FPDiv(left, max); + } + case AmdGpu::NumberConversion::Sint16ToSnormNz: { + const IR::U32 additon = ir.IAdd(ir.IMul(ir.BitCast(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::max())); + return ir.FPDiv(left, max); + } default: UNREACHABLE(); } @@ -66,6 +78,20 @@ inline F32 ApplyWriteNumberConversion(IREmitter& ir, const F32& value, case AmdGpu::NumberConversion::UnormToUbnorm: // Convert -1...1 to 0...1 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::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(raw); + } + case AmdGpu::NumberConversion::Sint16ToSnormNz: { + const IR::F32 max = ir.Imm32(float(std::numeric_limits::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(raw); + } default: UNREACHABLE(); } diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index 9aac6230a..853e4854d 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -29,6 +29,7 @@ struct Profile { bool supports_native_cube_calc{}; bool supports_trinary_minmax{}; bool supports_robust_buffer_access{}; + bool supports_image_fp32_atomic_min_max{}; bool has_broken_spirv_clamp{}; bool lower_left_origin_mode{}; bool needs_manual_interpolation{}; diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 517392b98..b8ed42f5b 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -169,10 +169,10 @@ static constexpr u32 MaxColorBuffers = 8; struct PsColorBuffer { AmdGpu::NumberFormat num_format : 4; - AmdGpu::NumberConversion num_conversion : 2; + AmdGpu::NumberConversion num_conversion : 3; AmdGpu::Liverpool::ShaderExportFormat export_format : 4; u32 needs_unorm_fixup : 1; - u32 pad : 21; + u32 pad : 20; AmdGpu::CompMapping swizzle; auto operator<=>(const PsColorBuffer&) const noexcept = default; diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 967b952c6..598288085 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -455,14 +455,14 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); const auto offset = draw_indirect->data_offset; - const auto size = sizeof(DrawIndirectArgs); + const auto stride = sizeof(DrawIndirectArgs); if (DebugState.DumpingCurrentReg()) { DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); } if (rasterizer) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin(fmt::format("gfx:{}:DrawIndirect", cmd_address)); - rasterizer->DrawIndirect(false, indirect_args_addr, offset, size, 1, 0); + rasterizer->DrawIndirect(false, indirect_args_addr, offset, stride, 1, 0); rasterizer->ScopeMarkerEnd(); } break; @@ -471,7 +471,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); const auto offset = draw_index_indirect->data_offset; - const auto size = sizeof(DrawIndexedIndirectArgs); + const auto stride = sizeof(DrawIndexedIndirectArgs); if (DebugState.DumpingCurrentReg()) { DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); } @@ -479,25 +479,46 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); rasterizer->ScopeMarkerBegin( fmt::format("gfx:{}:DrawIndexIndirect", cmd_address)); - rasterizer->DrawIndirect(true, indirect_args_addr, offset, size, 1, 0); + rasterizer->DrawIndirect(true, indirect_args_addr, offset, stride, 1, 0); rasterizer->ScopeMarkerEnd(); } break; } - case PM4ItOpcode::DrawIndexIndirectCountMulti: { + case PM4ItOpcode::DrawIndexIndirectMulti: { const auto* draw_index_indirect = reinterpret_cast(header); const auto offset = draw_index_indirect->data_offset; if (DebugState.DumpingCurrentReg()) { DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); } + if (rasterizer) { + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarkerBegin( + fmt::format("gfx:{}:DrawIndexIndirectMulti", cmd_address)); + rasterizer->DrawIndirect(true, indirect_args_addr, offset, + draw_index_indirect->stride, + draw_index_indirect->count, 0); + rasterizer->ScopeMarkerEnd(); + } + break; + } + case PM4ItOpcode::DrawIndexIndirectCountMulti: { + const auto* draw_index_indirect = + reinterpret_cast(header); + const auto offset = draw_index_indirect->data_offset; + if (DebugState.DumpingCurrentReg()) { + DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); + } if (rasterizer) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin( fmt::format("gfx:{}:DrawIndexIndirectCountMulti", cmd_address)); - rasterizer->DrawIndirect( - true, indirect_args_addr, offset, draw_index_indirect->stride, - draw_index_indirect->count, draw_index_indirect->countAddr); + rasterizer->DrawIndirect(true, indirect_args_addr, offset, + draw_index_indirect->stride, + draw_index_indirect->count, + draw_index_indirect->count_indirect_enable.Value() + ? draw_index_indirect->count_addr + : 0); rasterizer->ScopeMarkerEnd(); } break; @@ -843,6 +864,11 @@ Liverpool::Task Liverpool::ProcessCompute(const u32* acb, u32 acb_dwords, u32 vq } break; } + case PM4ItOpcode::SetQueueReg: { + const auto* set_data = reinterpret_cast(header); + UNREACHABLE_MSG("Encountered compute SetQueueReg: vqid = {}, reg_offset = {:#x}", + set_data->vqid.Value(), set_data->reg_offset.Value()); + } case PM4ItOpcode::DispatchDirect: { const auto* dispatch_direct = reinterpret_cast(header); auto& cs_program = GetCsRegs(); diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 8f9292f1c..c4bebd05f 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -924,15 +924,11 @@ struct Liverpool { } [[nodiscard]] NumberFormat GetNumberFmt() const { - // There is a small difference between T# and CB number types, account for it. - return RemapNumberFormat(info.number_type == NumberFormat::SnormNz - ? NumberFormat::Srgb - : info.number_type.Value(), - info.format); + return RemapNumberFormat(GetFixedNumberFormat(), info.format); } [[nodiscard]] NumberConversion GetNumberConversion() const { - return MapNumberConversion(info.number_type); + return MapNumberConversion(GetFixedNumberFormat(), info.format); } [[nodiscard]] CompMapping Swizzle() const { @@ -973,6 +969,13 @@ struct Liverpool { const auto mrt_swizzle = mrt_swizzles[swap_idx][components_idx]; return RemapSwizzle(info.format, mrt_swizzle); } + + private: + [[nodiscard]] NumberFormat GetFixedNumberFormat() const { + // There is a small difference between T# and CB number types, account for it. + return info.number_type == NumberFormat::SnormNz ? NumberFormat::Srgb + : info.number_type.Value(); + } }; enum ContextRegs : u32 { diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index ae1d32e00..cd175f6c9 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -211,6 +211,21 @@ struct PM4CmdSetData { } }; +struct PM4CmdSetQueueReg { + PM4Type3Header header; + union { + u32 raw; + BitField<0, 8, u32> reg_offset; ///< Offset in DWords from the register base address + BitField<15, 1, u32> defer_exec; ///< Defer execution + BitField<16, 10, u32> vqid; ///< Queue ID + }; + u32 data[0]; + + [[nodiscard]] u32 Size() const { + return header.count << 2u; + } +}; + struct PM4CmdNop { PM4Type3Header header; u32 data_block[0]; @@ -860,6 +875,24 @@ struct PM4CmdDrawIndexIndirect { }; struct PM4CmdDrawIndexIndirectMulti { + PM4Type3Header header; ///< header + u32 data_offset; ///< Byte aligned offset where the required data structure starts + union { + u32 dw2; + BitField<0, 16, u32> base_vtx_loc; ///< Offset where the CP will write the + ///< BaseVertexLocation it fetched from memory + }; + union { + u32 dw3; + BitField<0, 16, u32> start_inst_loc; ///< Offset where the CP will write the + ///< StartInstanceLocation it fetched from memory + }; + u32 count; ///< Count of data structures to loop through before going to next packet + u32 stride; ///< Stride in memory from one data structure to the next + u32 draw_initiator; ///< Draw Initiator Register +}; + +struct PM4CmdDrawIndexIndirectCountMulti { PM4Type3Header header; ///< header u32 data_offset; ///< Byte aligned offset where the required data structure starts union { @@ -874,14 +907,14 @@ struct PM4CmdDrawIndexIndirectMulti { }; union { u32 dw4; - BitField<0, 16, u32> drawIndexLoc; ///< register offset to write the Draw Index count + BitField<0, 16, u32> draw_index_loc; ///< register offset to write the Draw Index count BitField<30, 1, u32> - countIndirectEnable; ///< Indicates the data structure count is in memory + count_indirect_enable; ///< Indicates the data structure count is in memory BitField<31, 1, u32> - drawIndexEnable; ///< Enables writing of Draw Index count to DRAW_INDEX_LOC + draw_index_enable; ///< Enables writing of Draw Index count to DRAW_INDEX_LOC }; u32 count; ///< Count of data structures to loop through before going to next packet - u64 countAddr; ///< DWord aligned Address[31:2]; Valid if countIndirectEnable is set + u64 count_addr; ///< DWord aligned Address[31:2]; Valid if countIndirectEnable is set u32 stride; ///< Stride in memory from one data structure to the next u32 draw_initiator; ///< Draw Initiator Register }; diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index 64a85c812..9060074fb 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -68,7 +68,7 @@ struct Buffer { } NumberConversion GetNumberConversion() const noexcept { - return MapNumberConversion(NumberFormat(num_format)); + return MapNumberConversion(NumberFormat(num_format), DataFormat(data_format)); } u32 GetStride() const noexcept { @@ -219,6 +219,19 @@ struct Image { return image; } + static constexpr Image NullDepth() { + Image image{}; + image.data_format = u64(DataFormat::Format32); + image.num_format = u64(NumberFormat::Float); + image.dst_sel_x = u64(CompSwizzle::Red); + image.dst_sel_y = u64(CompSwizzle::Green); + image.dst_sel_z = u64(CompSwizzle::Blue); + image.dst_sel_w = u64(CompSwizzle::Alpha); + image.tiling_index = u64(TilingMode::Texture_MicroTiled); + image.type = u64(ImageType::Color2D); + return image; + } + bool Valid() const { return (type & 0x8u) != 0; } @@ -292,7 +305,7 @@ struct Image { } NumberConversion GetNumberConversion() const noexcept { - return MapNumberConversion(NumberFormat(num_format)); + return MapNumberConversion(NumberFormat(num_format), DataFormat(data_format)); } TilingMode GetTilingMode() const { diff --git a/src/video_core/amdgpu/types.h b/src/video_core/amdgpu/types.h index d1cf19076..ab0df689e 100644 --- a/src/video_core/amdgpu/types.h +++ b/src/video_core/amdgpu/types.h @@ -197,6 +197,8 @@ enum class NumberConversion : u32 { UintToUscaled = 1, SintToSscaled = 2, UnormToUbnorm = 3, + Sint8ToSnormNz = 5, + Sint16ToSnormNz = 6, }; struct CompMapping { @@ -287,6 +289,7 @@ inline NumberFormat RemapNumberFormat(const NumberFormat format, const DataForma case NumberFormat::Uscaled: return NumberFormat::Uint; case NumberFormat::Sscaled: + case NumberFormat::SnormNz: return NumberFormat::Sint; case NumberFormat::Ubnorm: return NumberFormat::Unorm; @@ -336,14 +339,28 @@ inline CompMapping RemapSwizzle(const DataFormat format, const CompMapping swizz } } -inline NumberConversion MapNumberConversion(const NumberFormat format) { - switch (format) { +inline NumberConversion MapNumberConversion(const NumberFormat num_fmt, const DataFormat data_fmt) { + switch (num_fmt) { case NumberFormat::Uscaled: return NumberConversion::UintToUscaled; case NumberFormat::Sscaled: return NumberConversion::SintToSscaled; case NumberFormat::Ubnorm: return NumberConversion::UnormToUbnorm; + case NumberFormat::SnormNz: { + switch (data_fmt) { + case DataFormat::Format8: + case DataFormat::Format8_8: + case DataFormat::Format8_8_8_8: + return NumberConversion::Sint8ToSnormNz; + case DataFormat::Format16: + case DataFormat::Format16_16: + case DataFormat::Format16_16_16_16: + return NumberConversion::Sint16ToSnormNz; + default: + UNREACHABLE_MSG("data_fmt = {}", u32(data_fmt)); + } + } default: return NumberConversion::None; } diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index cdf736a89..c993ef3e5 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -177,8 +177,8 @@ void BufferCache::BindVertexBuffers(const Vulkan::GraphicsPipeline& pipeline) { if (instance.IsVertexInputDynamicState()) { cmdbuf.bindVertexBuffers(0, num_buffers, host_buffers.data(), host_offsets.data()); } else { - cmdbuf.bindVertexBuffers2EXT(0, num_buffers, host_buffers.data(), host_offsets.data(), - host_sizes.data(), host_strides.data()); + cmdbuf.bindVertexBuffers2(0, num_buffers, host_buffers.data(), host_offsets.data(), + host_sizes.data(), host_strides.data()); } } @@ -238,7 +238,15 @@ void BufferCache::InlineData(VAddr address, const void* value, u32 num_bytes, bo .bufferMemoryBarrierCount = 1, .pBufferMemoryBarriers = &pre_barrier, }); - cmdbuf.updateBuffer(buffer->Handle(), buffer->Offset(address), num_bytes, value); + // vkCmdUpdateBuffer can only copy up to 65536 bytes at a time. + static constexpr u32 UpdateBufferMaxSize = 65536; + const auto dst_offset = buffer->Offset(address); + for (u32 offset = 0; offset < num_bytes; offset += UpdateBufferMaxSize) { + const auto* update_src = static_cast(value) + offset; + const auto update_dst = dst_offset + offset; + const auto update_size = std::min(num_bytes - offset, UpdateBufferMaxSize); + cmdbuf.updateBuffer(buffer->Handle(), update_dst, update_size, update_src); + } cmdbuf.pipelineBarrier2(vk::DependencyInfo{ .dependencyFlags = vk::DependencyFlagBits::eByRegion, .bufferMemoryBarrierCount = 1, diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 072807124..e31b95844 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -210,7 +210,8 @@ bool Instance::CreateDevice() { vk::PhysicalDeviceRobustness2FeaturesEXT, vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT, vk::PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT, - vk::PhysicalDevicePortabilitySubsetFeaturesKHR>(); + vk::PhysicalDevicePortabilitySubsetFeaturesKHR, + vk::PhysicalDeviceShaderAtomicFloat2FeaturesEXT>(); features = feature_chain.get().features; const vk::StructureChain properties_chain = physical_device.getProperties2< @@ -260,6 +261,8 @@ bool Instance::CreateDevice() { robustness2_features = feature_chain.get(); LOG_INFO(Render_Vulkan, "- robustBufferAccess2: {}", robustness2_features.robustBufferAccess2); + LOG_INFO(Render_Vulkan, "- robustImageAccess2: {}", + robustness2_features.robustImageAccess2); LOG_INFO(Render_Vulkan, "- nullDescriptor: {}", robustness2_features.nullDescriptor); } custom_border_color = add_extension(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); @@ -272,6 +275,13 @@ bool Instance::CreateDevice() { image_load_store_lod = add_extension(VK_AMD_SHADER_IMAGE_LOAD_STORE_LOD_EXTENSION_NAME); amd_gcn_shader = add_extension(VK_AMD_GCN_SHADER_EXTENSION_NAME); amd_shader_trinary_minmax = add_extension(VK_AMD_SHADER_TRINARY_MINMAX_EXTENSION_NAME); + shader_atomic_float2 = add_extension(VK_EXT_SHADER_ATOMIC_FLOAT_2_EXTENSION_NAME); + if (shader_atomic_float2) { + shader_atomic_float2_features = + feature_chain.get(); + LOG_INFO(Render_Vulkan, "- shaderImageFloat32AtomicMinMax: {}", + shader_atomic_float2_features.shaderImageFloat32AtomicMinMax); + } const bool calibrated_timestamps = TRACY_GPU_ENABLED ? add_extension(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME) : false; @@ -329,7 +339,9 @@ bool Instance::CreateDevice() { .independentBlend = features.independentBlend, .geometryShader = features.geometryShader, .tessellationShader = features.tessellationShader, + .dualSrcBlend = features.dualSrcBlend, .logicOp = features.logicOp, + .multiDrawIndirect = features.multiDrawIndirect, .depthBiasClamp = features.depthBiasClamp, .fillModeNonSolid = features.fillModeNonSolid, .depthBounds = features.depthBounds, @@ -385,6 +397,7 @@ bool Instance::CreateDevice() { }, vk::PhysicalDeviceRobustness2FeaturesEXT{ .robustBufferAccess2 = robustness2_features.robustBufferAccess2, + .robustImageAccess2 = robustness2_features.robustImageAccess2, .nullDescriptor = robustness2_features.nullDescriptor, }, vk::PhysicalDeviceVertexInputDynamicStateFeaturesEXT{ @@ -401,6 +414,10 @@ bool Instance::CreateDevice() { vk::PhysicalDeviceLegacyVertexAttributesFeaturesEXT{ .legacyVertexAttributes = true, }, + vk::PhysicalDeviceShaderAtomicFloat2FeaturesEXT{ + .shaderImageFloat32AtomicMinMax = + shader_atomic_float2_features.shaderImageFloat32AtomicMinMax, + }, #ifdef __APPLE__ portability_features, #endif @@ -430,6 +447,9 @@ bool Instance::CreateDevice() { if (!legacy_vertex_attributes) { device_chain.unlink(); } + if (!shader_atomic_float2) { + device_chain.unlink(); + } auto [device_result, dev] = physical_device.createDeviceUnique(device_chain.get()); if (device_result != vk::Result::eSuccess) { diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index bf9af1f24..573473869 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -165,6 +165,12 @@ public: return amd_shader_trinary_minmax; } + /// Returns true when the shaderImageFloat32AtomicMinMax feature of + /// VK_EXT_shader_atomic_float2 is supported. + bool IsShaderAtomicFloatImage32MinMaxSupported() const { + return shader_atomic_float2 && shader_atomic_float2_features.shaderImageFloat32AtomicMinMax; + } + /// Returns true when geometry shaders are supported by the device bool IsGeometryStageSupported() const { return features.geometryShader; @@ -336,6 +342,7 @@ private: vk::PhysicalDevicePortabilitySubsetFeaturesKHR portability_features; vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT dynamic_state_3_features; vk::PhysicalDeviceRobustness2FeaturesEXT robustness2_features; + vk::PhysicalDeviceShaderAtomicFloat2FeaturesEXT shader_atomic_float2_features; vk::DriverIdKHR driver_id; vk::UniqueDebugUtilsMessengerEXT debug_callback{}; std::string vendor_name; @@ -360,6 +367,7 @@ private: bool image_load_store_lod{}; bool amd_gcn_shader{}; bool amd_shader_trinary_minmax{}; + bool shader_atomic_float2{}; bool portability_subset{}; }; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 0b991cda0..d7ad47a3c 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -205,7 +205,9 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, .supports_image_load_store_lod = instance_.IsImageLoadStoreLodSupported(), .supports_native_cube_calc = instance_.IsAmdGcnShaderSupported(), .supports_trinary_minmax = instance_.IsAmdShaderTrinaryMinMaxSupported(), - .supports_robust_buffer_access = instance_.IsRobustBufferAccess2Supported(), + // TODO: Emitted bounds checks cause problems with phi control flow; needs to be fixed. + .supports_robust_buffer_access = true, // instance_.IsRobustBufferAccess2Supported(), + .supports_image_fp32_atomic_min_max = instance_.IsShaderAtomicFloatImage32MinMaxSupported(), .needs_manual_interpolation = instance.IsFragmentShaderBarycentricSupported() && instance.GetDriverID() == vk::DriverId::eNvidiaProprietary, .needs_lds_barriers = instance.GetDriverID() == vk::DriverId::eNvidiaProprietary || diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index 6bd4b26fa..09dd23cb6 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -270,7 +270,25 @@ Frame* Presenter::PrepareLastFrame() { return frame; } -Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) { +static vk::Format GetFrameViewFormat(const Libraries::VideoOut::PixelFormat format) { + switch (format) { + case Libraries::VideoOut::PixelFormat::A8B8G8R8Srgb: + return vk::Format::eR8G8B8A8Srgb; + case Libraries::VideoOut::PixelFormat::A8R8G8B8Srgb: + return vk::Format::eB8G8R8A8Srgb; + case Libraries::VideoOut::PixelFormat::A2R10G10B10: + case Libraries::VideoOut::PixelFormat::A2R10G10B10Srgb: + case Libraries::VideoOut::PixelFormat::A2R10G10B10Bt2020Pq: + return vk::Format::eA2R10G10B10UnormPack32; + default: + break; + } + UNREACHABLE_MSG("Unknown format={}", static_cast(format)); + return {}; +} + +Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, + const Libraries::VideoOut::PixelFormat format, bool is_eop) { // Request a free presentation frame. Frame* frame = GetRenderFrame(); @@ -324,7 +342,7 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) cmdbuf); VideoCore::ImageViewInfo info{}; - info.format = image.info.pixel_format; + info.format = GetFrameViewFormat(format); // Exclude alpha from output frame to avoid blending with UI. info.mapping = vk::ComponentMapping{ .r = vk::ComponentSwizzle::eIdentity, diff --git a/src/video_core/renderer_vulkan/vk_presenter.h b/src/video_core/renderer_vulkan/vk_presenter.h index ad2708474..8ed2052ee 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.h +++ b/src/video_core/renderer_vulkan/vk_presenter.h @@ -70,11 +70,12 @@ public: auto desc = VideoCore::TextureCache::VideoOutDesc{attribute, cpu_address}; const auto image_id = texture_cache.FindImage(desc); texture_cache.UpdateImage(image_id, is_eop ? nullptr : &flip_scheduler); - return PrepareFrameInternal(image_id, is_eop); + return PrepareFrameInternal(image_id, attribute.attrib.pixel_format, is_eop); } Frame* PrepareBlankFrame(bool is_eop) { - return PrepareFrameInternal(VideoCore::NULL_IMAGE_ID, is_eop); + return PrepareFrameInternal(VideoCore::NULL_IMAGE_ID, + Libraries::VideoOut::PixelFormat::Unknown, is_eop); } VideoCore::Image& RegisterVideoOutSurface( @@ -119,7 +120,8 @@ public: } private: - Frame* PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop = true); + Frame* PrepareFrameInternal(VideoCore::ImageId image_id, + Libraries::VideoOut::PixelFormat format, bool is_eop = true); Frame* GetRenderFrame(); void SetExpectedGameSize(s32 width, s32 height); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index b04b4a07e..e7b42a34b 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -618,8 +618,9 @@ void Rasterizer::BindTextures(const Shader::Info& stage, Shader::Backend::Bindin if (instance.IsNullDescriptorSupported()) { image_infos.emplace_back(VK_NULL_HANDLE, VK_NULL_HANDLE, vk::ImageLayout::eGeneral); } else { - auto& null_image = texture_cache.GetImageView(VideoCore::NULL_IMAGE_VIEW_ID); - image_infos.emplace_back(VK_NULL_HANDLE, *null_image.image_view, + auto& null_image_view = + texture_cache.FindTexture(VideoCore::NULL_IMAGE_ID, desc.view_info); + image_infos.emplace_back(VK_NULL_HANDLE, *null_image_view.image_view, vk::ImageLayout::eGeneral); } } else { @@ -696,14 +697,19 @@ void Rasterizer::BindTextures(const Shader::Info& stage, Shader::Backend::Bindin void Rasterizer::BeginRendering(const GraphicsPipeline& pipeline, RenderState& state) { int cb_index = 0; - for (auto& [image_id, desc] : cb_descs) { + for (auto attach_idx = 0u; attach_idx < state.num_color_attachments; ++attach_idx) { + if (state.color_attachments[attach_idx].imageView == VK_NULL_HANDLE) { + continue; + } + + auto& [image_id, desc] = cb_descs[cb_index++]; if (auto& old_img = texture_cache.GetImage(image_id); old_img.binding.needs_rebind) { auto& view = texture_cache.FindRenderTarget(desc); ASSERT(view.image_id != image_id); image_id = bound_images.emplace_back(view.image_id); auto& image = texture_cache.GetImage(view.image_id); - state.color_attachments[cb_index].imageView = *view.image_view; - state.color_attachments[cb_index].imageLayout = image.last_state.layout; + state.color_attachments[attach_idx].imageView = *view.image_view; + state.color_attachments[attach_idx].imageLayout = image.last_state.layout; const auto mip = view.info.range.base.level; state.width = std::min(state.width, std::max(image.info.size.width >> mip, 1u)); @@ -722,8 +728,7 @@ void Rasterizer::BeginRendering(const GraphicsPipeline& pipeline, RenderState& s desc.view_info.range); } image.usage.render_target = 1u; - state.color_attachments[cb_index].imageLayout = image.last_state.layout; - ++cb_index; + state.color_attachments[attach_idx].imageLayout = image.last_state.layout; } if (db_desc) { diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 26928eaf7..39322f449 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -16,14 +16,15 @@ using VideoOutFormat = Libraries::VideoOut::PixelFormat; static vk::Format ConvertPixelFormat(const VideoOutFormat format) { switch (format) { - case VideoOutFormat::A8R8G8B8Srgb: - return vk::Format::eB8G8R8A8Srgb; case VideoOutFormat::A8B8G8R8Srgb: + // Remaining formats are mapped to RGBA for internal consistency and changed to BGRA in the + // frame image view. + case VideoOutFormat::A8R8G8B8Srgb: return vk::Format::eR8G8B8A8Srgb; case VideoOutFormat::A2R10G10B10: case VideoOutFormat::A2R10G10B10Srgb: case VideoOutFormat::A2R10G10B10Bt2020Pq: - return vk::Format::eA2R10G10B10UnormPack32; + return vk::Format::eA2B10G10R10UnormPack32; default: break; } diff --git a/src/video_core/texture_cache/image_view.h b/src/video_core/texture_cache/image_view.h index 23c703d23..6a17490bf 100644 --- a/src/video_core/texture_cache/image_view.h +++ b/src/video_core/texture_cache/image_view.h @@ -34,8 +34,6 @@ struct ImageViewInfo { struct Image; -constexpr Common::SlotId NULL_IMAGE_VIEW_ID{0}; - struct ImageView { ImageView(const Vulkan::Instance& instance, const ImageViewInfo& info, Image& image, ImageId image_id); diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 047bb3dfe..82f4d6413 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -8,6 +8,7 @@ #include "common/debug.h" #include "video_core/buffer_cache/buffer_cache.h" #include "video_core/page_manager.h" +#include "video_core/renderer_vulkan/liverpool_to_vk.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/texture_cache/host_compatibility.h" @@ -23,31 +24,41 @@ TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& BufferCache& buffer_cache_, PageManager& tracker_) : instance{instance_}, scheduler{scheduler_}, buffer_cache{buffer_cache_}, tracker{tracker_}, tile_manager{instance, scheduler} { + // Create basic null image at fixed image ID. + const auto null_id = GetNullImage(vk::Format::eR8G8B8A8Unorm); + ASSERT(null_id.index == NULL_IMAGE_ID.index); +} + +TextureCache::~TextureCache() = default; + +ImageId TextureCache::GetNullImage(const vk::Format format) { + const auto existing_image = null_images.find(format); + if (existing_image != null_images.end()) { + return existing_image->second; + } + ImageInfo info{}; - info.pixel_format = vk::Format::eR8G8B8A8Unorm; + info.pixel_format = format; info.type = vk::ImageType::e2D; - info.tiling_idx = u32(AmdGpu::TilingMode::Texture_MicroTiled); + info.tiling_idx = static_cast(AmdGpu::TilingMode::Texture_MicroTiled); info.num_bits = 32; info.UpdateSize(); + const ImageId null_id = slot_images.insert(instance, scheduler, info); - ASSERT(null_id.index == NULL_IMAGE_ID.index); auto& img = slot_images[null_id]; + const vk::Image& null_image = img.image; - Vulkan::SetObjectName(instance.GetDevice(), null_image, "Null Image"); + Vulkan::SetObjectName(instance.GetDevice(), null_image, + fmt::format("Null Image ({})", vk::to_string(format))); + img.flags = ImageFlagBits::Empty; img.track_addr = img.info.guest_address; img.track_addr_end = img.info.guest_address + img.info.guest_size; - ImageViewInfo view_info; - const auto null_view_id = - slot_image_views.insert(instance, view_info, slot_images[null_id], null_id); - ASSERT(null_view_id.index == NULL_IMAGE_VIEW_ID.index); - const vk::ImageView& null_image_view = slot_image_views[null_view_id].image_view.get(); - Vulkan::SetObjectName(instance.GetDevice(), null_image_view, "Null Image View"); + null_images.emplace(format, null_id); + return null_id; } -TextureCache::~TextureCache() = default; - void TextureCache::MarkAsMaybeDirty(ImageId image_id, Image& image) { if (image.hash == 0) { // Initialize hash @@ -296,7 +307,7 @@ ImageId TextureCache::FindImage(BaseDesc& desc, FindFlags flags) { const auto& info = desc.info; if (info.guest_address == 0) [[unlikely]] { - return NULL_IMAGE_ID; + return GetNullImage(info.pixel_format); } std::scoped_lock lock{mutex}; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index f262768ea..b6bf88958 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -246,6 +246,9 @@ private: } } + /// Gets or creates a null image for a particular format. + ImageId GetNullImage(vk::Format format); + /// Create an image from the given parameters [[nodiscard]] ImageId InsertImage(const ImageInfo& info, VAddr cpu_addr); @@ -285,6 +288,7 @@ private: Common::SlotVector slot_images; Common::SlotVector slot_image_views; tsl::robin_map samplers; + tsl::robin_map null_images; PageTable page_table; std::mutex mutex;