diff --git a/CMakeLists.txt b/CMakeLists.txt index e36c1f280..f55767611 100644 --- 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 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/memory_patcher.cpp b/src/common/memory_patcher.cpp index 2a8b26acb..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; @@ -169,7 +169,8 @@ void OnGameLoaded() { if (type == "mask_jump32") patchMask = MemoryPatcher::PatchMask::Mask_Jump32; - if (type == "mask" || type == "mask_jump32" && !maskOffsetStr.empty()) { + if ((type == "mask" || type == "mask_jump32") && + !maskOffsetStr.empty()) { maskOffsetValue = std::stoi(maskOffsetStr, 0, 10); } diff --git a/src/common/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/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/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index bc34dff98..bcfa15a62 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -877,7 +877,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 +893,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 +908,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 +929,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 +1035,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..959a8605a 100644 --- a/src/core/libraries/kernel/kernel.cpp +++ b/src/core/libraries/kernel/kernel.cpp @@ -24,6 +24,7 @@ #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 @@ -196,10 +197,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; } @@ -225,7 +222,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 +230,25 @@ 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, + Libraries::Net::sys_getsockname); + LIB_FUNCTION("KuOmgKoqCdY", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_bind); + LIB_FUNCTION("5jRCs2axtr4", "libScePosix", 1, "libkernel", 1, 1, + Libraries::Net::sceNetInetNtop); // TODO fix it to sys_ ... + LIB_FUNCTION("4n51s0zEf0c", "libScePosix", 1, "libkernel", 1, 1, + Libraries::Net::sceNetInetPton); // TODO fix it to sys_ ... } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/kernel.h b/src/core/libraries/kernel/kernel.h index 58911727d..4d68aa357 100644 --- a/src/core/libraries/kernel/kernel.h +++ b/src/core/libraries/kernel/kernel.h @@ -18,6 +18,7 @@ namespace Libraries::Kernel { void ErrSceToPosix(int result); int ErrnoToSceKernelError(int e); void SetPosixErrno(int e); +int* PS4_SYSV_ABI __Error(); template struct WrapperImpl; diff --git a/src/core/libraries/network/net.cpp b/src/core/libraries/network/net.cpp index 161fc5214..1f024277f 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,87 @@ 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; + 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 +1018,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 +1103,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 +1300,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 +1447,47 @@ 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) { + 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 +1570,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..e9b710bb3 --- /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 -1; +} +int P2PSocket::SetSocketOptions(int level, int optname, const void* optval, u32 optlen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} +int P2PSocket::GetSocketOptions(int level, int optname, void* optval, u32* optlen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} + +int P2PSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} + +int P2PSocket::Listen(int backlog) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} + +int P2PSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to, + u32 tolen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} + +int P2PSocket::ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, u32* fromlen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} + +SocketPtr P2PSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return nullptr; +} + +int P2PSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} + +int P2PSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} + +} // namespace Libraries::Net \ 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..140e4fd22 --- /dev/null +++ b/src/core/libraries/network/posix_sockets.cpp @@ -0,0 +1,359 @@ +// 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() { +#ifdef _WIN32 + auto out = closesocket(sock); +#else + auto out = ::close(sock); +#endif + return ConvertReturnErrorCode(out); +} + +int PosixSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) { + sockaddr addr2; + convertOrbisNetSockaddrToPosix(addr, &addr2); + return ConvertReturnErrorCode(::bind(sock, &addr2, sizeof(sockaddr_in))); +} + +int PosixSocket::Listen(int backlog) { + return ConvertReturnErrorCode(::listen(sock, backlog)); +} + +int PosixSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to, + u32 tolen) { + if (to != nullptr) { + sockaddr addr; + convertOrbisNetSockaddrToPosix(to, &addr); + return ConvertReturnErrorCode( + sendto(sock, (const char*)msg, len, flags, &addr, sizeof(sockaddr_in))); + } else { + return ConvertReturnErrorCode(send(sock, (const char*)msg, len, flags)); + } +} + +int PosixSocket::ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, + u32* fromlen) { + if (from != nullptr) { + sockaddr addr; + int res = recvfrom(sock, (char*)buf, len, flags, &addr, (socklen_t*)fromlen); + convertPosixSockaddrToOrbis(&addr, from); + *fromlen = sizeof(OrbisNetSockaddrIn); + return ConvertReturnErrorCode(res); + } else { + return ConvertReturnErrorCode(recv(sock, (char*)buf, len, flags)); + } +} + +SocketPtr PosixSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) { + sockaddr addr2; + net_socket new_socket = ::accept(sock, &addr2, (socklen_t*)addrlen); +#ifdef _WIN32 + if (new_socket != INVALID_SOCKET) { +#else + if (new_socket >= 0) { +#endif + convertPosixSockaddrToOrbis(&addr2, addr); + *addrlen = sizeof(OrbisNetSockaddrIn); + return std::make_shared(new_socket); + } + return nullptr; +} + +int PosixSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) { + sockaddr addr2; + convertOrbisNetSockaddrToPosix(addr, &addr2); + return ::connect(sock, &addr2, sizeof(sockaddr_in)); +} + +int PosixSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) { + sockaddr addr; + convertOrbisNetSockaddrToPosix(name, &addr); + if (name != nullptr) { + *namelen = sizeof(sockaddr_in); + } + int res = getsockname(sock, &addr, (socklen_t*)namelen); + if (res >= 0) { + convertPosixSockaddrToOrbis(&addr, name); + *namelen = sizeof(OrbisNetSockaddrIn); + } + return res; +} + +#define CASE_SETSOCKOPT(opt) \ + case ORBIS_NET_##opt: \ + return ConvertReturnErrorCode(setsockopt(sock, level, opt, (const char*)optval, optlen)) + +#define CASE_SETSOCKOPT_VALUE(opt, value) \ + case opt: \ + if (optlen != sizeof(*value)) { \ + return ORBIS_NET_ERROR_EFAULT; \ + } \ + memcpy(value, optval, optlen); \ + return 0 + +int PosixSocket::SetSocketOptions(int level, int optname, const void* optval, u32 optlen) { + level = ConvertLevels(level); + if (level == SOL_SOCKET) { + switch (optname) { + CASE_SETSOCKOPT(SO_REUSEADDR); + CASE_SETSOCKOPT(SO_KEEPALIVE); + CASE_SETSOCKOPT(SO_BROADCAST); + CASE_SETSOCKOPT(SO_LINGER); + CASE_SETSOCKOPT(SO_SNDBUF); + CASE_SETSOCKOPT(SO_RCVBUF); + CASE_SETSOCKOPT(SO_SNDTIMEO); + CASE_SETSOCKOPT(SO_RCVTIMEO); + CASE_SETSOCKOPT(SO_ERROR); + CASE_SETSOCKOPT(SO_TYPE); + CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_REUSEPORT, &sockopt_so_reuseport); + CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_ONESBCAST, &sockopt_so_onesbcast); + CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_USECRYPTO, &sockopt_so_usecrypto); + CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_USESIGNATURE, &sockopt_so_usesignature); + case ORBIS_NET_SO_NAME: + return ORBIS_NET_ERROR_EINVAL; // don't support set for name + case ORBIS_NET_SO_NBIO: { + if (optlen != sizeof(sockopt_so_nbio)) { + return ORBIS_NET_ERROR_EFAULT; + } + memcpy(&sockopt_so_nbio, optval, optlen); +#ifdef _WIN32 + static_assert(sizeof(u_long) == sizeof(sockopt_so_nbio), + "type used for ioctlsocket value does not have the expected size"); + return ConvertReturnErrorCode(ioctlsocket(sock, FIONBIO, (u_long*)&sockopt_so_nbio)); +#else + return ConvertReturnErrorCode(ioctl(sock, FIONBIO, &sockopt_so_nbio)); +#endif + } + } + } else if (level == IPPROTO_IP) { + switch (optname) { + CASE_SETSOCKOPT(IP_HDRINCL); + CASE_SETSOCKOPT(IP_TOS); + CASE_SETSOCKOPT(IP_TTL); + CASE_SETSOCKOPT(IP_MULTICAST_IF); + CASE_SETSOCKOPT(IP_MULTICAST_TTL); + CASE_SETSOCKOPT(IP_MULTICAST_LOOP); + CASE_SETSOCKOPT(IP_ADD_MEMBERSHIP); + CASE_SETSOCKOPT(IP_DROP_MEMBERSHIP); + CASE_SETSOCKOPT_VALUE(ORBIS_NET_IP_TTLCHK, &sockopt_ip_ttlchk); + CASE_SETSOCKOPT_VALUE(ORBIS_NET_IP_MAXTTL, &sockopt_ip_maxttl); + } + } else if (level == IPPROTO_TCP) { + switch (optname) { + CASE_SETSOCKOPT(TCP_NODELAY); + CASE_SETSOCKOPT(TCP_MAXSEG); + CASE_SETSOCKOPT_VALUE(ORBIS_NET_TCP_MSS_TO_ADVERTISE, &sockopt_tcp_mss_to_advertise); + } + } + + UNREACHABLE_MSG("Unknown level ={} optname ={}", level, optname); + return 0; +} + +#define CASE_GETSOCKOPT(opt) \ + case ORBIS_NET_##opt: { \ + socklen_t optlen_temp = *optlen; \ + auto retval = \ + ConvertReturnErrorCode(getsockopt(sock, level, opt, (char*)optval, &optlen_temp)); \ + *optlen = optlen_temp; \ + return retval; \ + } +#define CASE_GETSOCKOPT_VALUE(opt, value) \ + case opt: \ + if (*optlen < sizeof(value)) { \ + *optlen = sizeof(value); \ + return ORBIS_NET_ERROR_EFAULT; \ + } \ + *optlen = sizeof(value); \ + *(decltype(value)*)optval = value; \ + return 0; + +int PosixSocket::GetSocketOptions(int level, int optname, void* optval, u32* optlen) { + level = ConvertLevels(level); + if (level == SOL_SOCKET) { + switch (optname) { + CASE_GETSOCKOPT(SO_REUSEADDR); + CASE_GETSOCKOPT(SO_KEEPALIVE); + CASE_GETSOCKOPT(SO_BROADCAST); + CASE_GETSOCKOPT(SO_LINGER); + CASE_GETSOCKOPT(SO_SNDBUF); + CASE_GETSOCKOPT(SO_RCVBUF); + CASE_GETSOCKOPT(SO_SNDTIMEO); + CASE_GETSOCKOPT(SO_RCVTIMEO); + CASE_GETSOCKOPT(SO_ERROR); + CASE_GETSOCKOPT(SO_TYPE); + CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_NBIO, sockopt_so_nbio); + CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_REUSEPORT, sockopt_so_reuseport); + CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_ONESBCAST, sockopt_so_onesbcast); + CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_USECRYPTO, sockopt_so_usecrypto); + CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_USESIGNATURE, sockopt_so_usesignature); + CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_NAME, + (char)0); // writes an empty string to the output buffer + } + } else if (level == IPPROTO_IP) { + switch (optname) { + CASE_GETSOCKOPT(IP_HDRINCL); + CASE_GETSOCKOPT(IP_TOS); + CASE_GETSOCKOPT(IP_TTL); + CASE_GETSOCKOPT(IP_MULTICAST_IF); + CASE_GETSOCKOPT(IP_MULTICAST_TTL); + CASE_GETSOCKOPT(IP_MULTICAST_LOOP); + CASE_GETSOCKOPT(IP_ADD_MEMBERSHIP); + CASE_GETSOCKOPT(IP_DROP_MEMBERSHIP); + CASE_GETSOCKOPT_VALUE(ORBIS_NET_IP_TTLCHK, sockopt_ip_ttlchk); + CASE_GETSOCKOPT_VALUE(ORBIS_NET_IP_MAXTTL, sockopt_ip_maxttl); + } + } else if (level == IPPROTO_TCP) { + switch (optname) { + CASE_GETSOCKOPT(TCP_NODELAY); + CASE_GETSOCKOPT(TCP_MAXSEG); + CASE_GETSOCKOPT_VALUE(ORBIS_NET_TCP_MSS_TO_ADVERTISE, sockopt_tcp_mss_to_advertise); + } + } + UNREACHABLE_MSG("Unknown level ={} optname ={}", level, optname); + return 0; +} + +} // namespace Libraries::Net \ 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..e41671d88 --- /dev/null +++ b/src/core/libraries/network/sockets.h @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#ifdef _WIN32 +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#include +#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 Socket { + explicit Socket(int domain, int type, int protocol) {} + virtual ~Socket() = default; + virtual int Close() = 0; + virtual int SetSocketOptions(int level, int optname, const void* optval, u32 optlen) = 0; + virtual int GetSocketOptions(int level, int optname, void* optval, u32* optlen) = 0; + virtual int Bind(const OrbisNetSockaddr* addr, u32 addrlen) = 0; + virtual int Listen(int backlog) = 0; + virtual int SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to, + u32 tolen) = 0; + virtual SocketPtr Accept(OrbisNetSockaddr* addr, u32* addrlen) = 0; + virtual int ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, + u32* fromlen) = 0; + virtual int Connect(const OrbisNetSockaddr* addr, u32 namelen) = 0; + virtual int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) = 0; +}; + +struct PosixSocket : public Socket { + net_socket sock; + int sockopt_so_reuseport = 0; + int sockopt_so_onesbcast = 0; + int sockopt_so_usecrypto = 0; + int sockopt_so_usesignature = 0; + int sockopt_so_nbio = 0; + int sockopt_ip_ttlchk = 0; + int sockopt_ip_maxttl = 0; + int sockopt_tcp_mss_to_advertise = 0; + explicit PosixSocket(int domain, int type, int protocol) + : Socket(domain, type, protocol), sock(socket(domain, type, protocol)) {} + explicit PosixSocket(net_socket sock) : Socket(0, 0, 0), sock(sock) {} + int Close() override; + int SetSocketOptions(int level, int optname, const void* optval, u32 optlen) override; + int GetSocketOptions(int level, int optname, void* optval, u32* optlen) override; + int Bind(const OrbisNetSockaddr* addr, u32 addrlen) override; + int Listen(int backlog) override; + int SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to, + u32 tolen) override; + int ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, u32* fromlen) override; + SocketPtr Accept(OrbisNetSockaddr* addr, u32* addrlen) override; + int Connect(const OrbisNetSockaddr* addr, u32 namelen) override; + int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override; +}; + +struct P2PSocket : public Socket { + explicit P2PSocket(int domain, int type, int protocol) : Socket(domain, type, protocol) {} + int Close() override; + int SetSocketOptions(int level, int optname, const void* optval, u32 optlen) override; + int GetSocketOptions(int level, int optname, void* optval, u32* optlen) override; + int Bind(const OrbisNetSockaddr* addr, u32 addrlen) override; + int Listen(int backlog) override; + int SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to, + u32 tolen) override; + int ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, u32* fromlen) override; + SocketPtr Accept(OrbisNetSockaddr* addr, u32* addrlen) override; + int Connect(const OrbisNetSockaddr* addr, u32 namelen) override; + int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override; +}; + +class NetInternal { +public: + explicit NetInternal() = default; + ~NetInternal() = default; + SocketPtr FindSocket(int sockid) { + std::scoped_lock lock{m_mutex}; + const auto it = socks.find(sockid); + if (it != socks.end()) { + return it->second; + } + return 0; + } + +public: + std::mutex m_mutex; + typedef std::map 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..fbf2a2456 --- /dev/null +++ b/src/core/libraries/network/sys_net.cpp @@ -0,0 +1,229 @@ +#include "sys_net.h" +// 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/emulator.cpp b/src/emulator.cpp index 1a71b99cb..5c20353df 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -16,6 +16,9 @@ #ifdef ENABLE_DISCORD_RPC #include "common/discord_rpc_handler.h" #endif +#ifdef _WIN32 +#include +#endif #include "common/elf_info.h" #include "common/ntapi.h" #include "common/path_util.h" @@ -46,6 +49,10 @@ Emulator::Emulator() { #ifdef _WIN32 Common::NtApi::Initialize(); SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS); + // need to init this in order for winsock2 to work + WORD versionWanted = MAKEWORD(2, 2); + WSADATA wsaData; + WSAStartup(versionWanted, &wsaData); #endif // Create stdin/stdout/stderr diff --git a/src/qt_gui/cheats_patches.cpp b/src/qt_gui/cheats_patches.cpp index 7239affd5..67f616f87 100644 --- a/src/qt_gui/cheats_patches.cpp +++ b/src/qt_gui/cheats_patches.cpp @@ -1387,7 +1387,7 @@ void CheatsPatches::applyPatch(const QString& patchName, bool enabled) { if (type == "mask_jump32") patchMask = MemoryPatcher::PatchMask::Mask_Jump32; - if (type == "mask" || type == "mask_jump32" && !maskOffsetStr.toStdString().empty()) { + if ((type == "mask" || type == "mask_jump32") && !maskOffsetStr.toStdString().empty()) { maskOffsetValue = std::stoi(maskOffsetStr.toStdString(), 0, 10); } 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..cfc01c58f 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: @@ -466,6 +470,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..784f8b4d2 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -196,6 +196,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{}; 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/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index 778da149f..1de255e4d 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: 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/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/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 56829ffc6..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, info.format); + 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/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 072807124..99f225d79 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< @@ -272,6 +273,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; @@ -401,6 +409,10 @@ bool Instance::CreateDevice() { vk::PhysicalDeviceLegacyVertexAttributesFeaturesEXT{ .legacyVertexAttributes = true, }, + vk::PhysicalDeviceShaderAtomicFloat2FeaturesEXT{ + .shaderImageFloat32AtomicMinMax = + shader_atomic_float2_features.shaderImageFloat32AtomicMinMax, + }, #ifdef __APPLE__ portability_features, #endif @@ -430,6 +442,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..0a0c81d4c 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -206,6 +206,7 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, .supports_native_cube_calc = instance_.IsAmdGcnShaderSupported(), .supports_trinary_minmax = instance_.IsAmdShaderTrinaryMinMaxSupported(), .supports_robust_buffer_access = 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_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index b04b4a07e..4caa781b9 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -696,14 +696,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 +727,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) {