diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp index b4fa204bc..e7a786396 100644 --- a/src/common/logging/text_formatter.cpp +++ b/src/common/logging/text_formatter.cpp @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2014 Citra Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include @@ -13,6 +14,7 @@ #include "common/logging/log.h" #include "common/logging/log_entry.h" #include "common/logging/text_formatter.h" +#include "common/thread.h" namespace Common::Log { @@ -23,8 +25,9 @@ std::string FormatLogMessage(const Entry& entry) { const char* class_name = GetLogClassName(entry.log_class); const char* level_name = GetLevelName(entry.log_level); - return fmt::format("[{}] <{}> {}:{} {}: {}", class_name, level_name, entry.filename, - entry.line_num, entry.function, entry.message); + return fmt::format("[{}] <{}> ({}) {}:{} {}: {}", class_name, level_name, + Common::GetCurrentThreadName(), entry.filename, entry.line_num, + entry.function, entry.message); } void PrintMessage(const Entry& entry) { diff --git a/src/common/thread.cpp b/src/common/thread.cpp index 982041ebb..54194186c 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp @@ -1,11 +1,14 @@ // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project // SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include #include #include +#include "core/libraries/kernel/threads/pthread.h" + #include "common/error.h" #include "common/logging/log.h" #include "common/thread.h" @@ -237,4 +240,22 @@ void AccurateTimer::End() { target_interval - std::chrono::duration_cast(now - start_time); } +std::string GetCurrentThreadName() { + using namespace Libraries::Kernel; + if (g_curthread && !g_curthread->name.empty()) { + return g_curthread->name; + } +#ifdef _WIN32 + PWSTR name; + GetThreadDescription(GetCurrentThread(), &name); + return Common::UTF16ToUTF8(name); +#else + char name[256]; + if (pthread_getname_np(pthread_self(), name, sizeof(name)) != 0) { + return ""; + } + return std::string{name}; +#endif +} + } // namespace Common diff --git a/src/common/thread.h b/src/common/thread.h index 5bd83d35c..a300d10c3 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -1,5 +1,6 @@ // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project // SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -46,4 +47,6 @@ public: } }; +std::string GetCurrentThreadName(); + } // namespace Common diff --git a/src/core/libraries/ime/ime_dialog_ui.cpp b/src/core/libraries/ime/ime_dialog_ui.cpp index 1d1638bd0..4a95c60c9 100644 --- a/src/core/libraries/ime/ime_dialog_ui.cpp +++ b/src/core/libraries/ime/ime_dialog_ui.cpp @@ -48,7 +48,7 @@ ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param, title.resize(title_len * 4 + 1); title[title_len * 4] = '\0'; - if (!ConvertOrbisToUTF8(param->title, title_len, &title[0], title_len * 4)) { + if (!ConvertOrbisToUTF8(param->title, title_len, &title[0], title_len * 4 + 1)) { LOG_ERROR(Lib_ImeDialog, "Failed to convert title to utf8 encoding"); } } @@ -59,14 +59,14 @@ ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param, placeholder[placeholder_len * 4] = '\0'; if (!ConvertOrbisToUTF8(param->placeholder, placeholder_len, &placeholder[0], - placeholder_len * 4)) { + placeholder_len * 4 + 1)) { LOG_ERROR(Lib_ImeDialog, "Failed to convert placeholder to utf8 encoding"); } } std::size_t text_len = std::char_traits::length(text_buffer); if (!ConvertOrbisToUTF8(text_buffer, text_len, current_text.begin(), - ORBIS_IME_DIALOG_MAX_TEXT_LENGTH * 4)) { + ORBIS_IME_DIALOG_MAX_TEXT_LENGTH * 4 + 1)) { LOG_ERROR(Lib_ImeDialog, "Failed to convert text to utf8 encoding"); } } @@ -110,7 +110,7 @@ bool ImeDialogState::CopyTextToOrbisBuffer() { } return ConvertUTF8ToOrbis(current_text.begin(), current_text.capacity(), text_buffer, - max_text_length); + static_cast(max_text_length) + 1); } bool ImeDialogState::CallTextFilter() { @@ -380,10 +380,12 @@ int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) { .timestamp = {0}, }; - if (!ui->state->ConvertUTF8ToOrbis(event_char, 4, &src_keycode.character, 1)) { + char16_t tmp_char[2] = {0}; + if (!ui->state->ConvertUTF8ToOrbis(event_char, 4, tmp_char, 2)) { LOG_ERROR(Lib_ImeDialog, "InputTextCallback: ConvertUTF8ToOrbis failed"); return 0; } + src_keycode.character = tmp_char[0]; LOG_DEBUG(Lib_ImeDialog, "InputTextCallback: converted to Orbis char={:#X}", static_cast(src_keycode.character)); src_keycode.keycode = src_keycode.character; // TODO set this to the correct value diff --git a/src/core/libraries/network/net.h b/src/core/libraries/network/net.h index 2f1339d0a..4cb7afdda 100644 --- a/src/core/libraries/network/net.h +++ b/src/core/libraries/network/net.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -110,6 +110,15 @@ enum OrbisNetSocketSoOption : u32 { ORBIS_NET_SO_PRIORITY = 0x1203 }; +enum OrbisNetFlags : u32 { + ORBIS_NET_MSG_PEEK = 0x00000002, + ORBIS_NET_MSG_WAITALL = 0x00000040, + ORBIS_NET_MSG_DONTWAIT = 0x00000080, + ORBIS_NET_MSG_USECRYPTO = 0x00100000, + ORBIS_NET_MSG_USESIGNATURE = 0x00200000, + ORBIS_NET_MSG_PEEKLEN = (0x00400000 | ORBIS_NET_MSG_PEEK) +}; + constexpr std::string_view NameOf(OrbisNetSocketSoOption o) { switch (o) { case ORBIS_NET_SO_REUSEADDR: diff --git a/src/core/libraries/network/posix_sockets.cpp b/src/core/libraries/network/posix_sockets.cpp index 3ffb42528..7992fa217 100644 --- a/src/core/libraries/network/posix_sockets.cpp +++ b/src/core/libraries/network/posix_sockets.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include @@ -184,28 +184,103 @@ int PosixSocket::Listen(int backlog) { return ConvertReturnErrorCode(::listen(sock, backlog)); } +static int convertOrbisFlagsToPosix(int sock_type, int sce_flags) { + int posix_flags = 0; + + if (sce_flags & ORBIS_NET_MSG_PEEK) + posix_flags |= MSG_PEEK; +#ifndef _WIN32 + if (sce_flags & ORBIS_NET_MSG_DONTWAIT) + posix_flags |= MSG_DONTWAIT; +#endif + // MSG_WAITALL is only valid for stream sockets + if ((sce_flags & ORBIS_NET_MSG_WAITALL) && + ((sock_type == ORBIS_NET_SOCK_STREAM) || (sock_type == ORBIS_NET_SOCK_STREAM_P2P))) + posix_flags |= MSG_WAITALL; + + return posix_flags; +} + +// On Windows, MSG_DONTWAIT is not handled natively by recv/send. +// This function uses select() with zero timeout to simulate non-blocking behavior. +static int socket_is_ready(int sock, bool is_read = true) { + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + timeval timeout{0, 0}; + int res = + select(sock + 1, is_read ? &fds : nullptr, is_read ? nullptr : &fds, nullptr, &timeout); + if (res == 0) + return ORBIS_NET_ERROR_EWOULDBLOCK; + else if (res < 0) + return ConvertReturnErrorCode(res); + + return res; +} + int PosixSocket::SendMessage(const OrbisNetMsghdr* msg, int flags) { std::scoped_lock lock{m_mutex}; + #ifdef _WIN32 - DWORD bytesSent = 0; - LPFN_WSASENDMSG wsasendmsg = nullptr; - GUID guid = WSAID_WSASENDMSG; - DWORD bytes = 0; + int totalSent = 0; + bool waitAll = (flags & ORBIS_NET_MSG_WAITALL) != 0; + bool dontWait = (flags & ORBIS_NET_MSG_DONTWAIT) != 0; - if (WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &wsasendmsg, - sizeof(wsasendmsg), &bytes, nullptr, nullptr) != 0) { - return ConvertReturnErrorCode(-1); + // stream socket with multiple buffers + bool use_wsamsg = + (socket_type == ORBIS_NET_SOCK_STREAM || socket_type == ORBIS_NET_SOCK_STREAM_P2P) && + msg->msg_iovlen > 1; + + for (int i = 0; i < msg->msg_iovlen; ++i) { + char* buf = (char*)msg->msg_iov[i].iov_base; + size_t remaining = msg->msg_iov[i].iov_len; + + while (remaining > 0) { + if (dontWait) { + int ready = socket_is_ready(sock, false); + if (ready <= 0) + return ready; + } + + int sent = 0; + if (use_wsamsg) { + // only call WSASendMsg if we have multiple buffers + LPFN_WSASENDMSG wsasendmsg = nullptr; + GUID guid = WSAID_WSASENDMSG; + DWORD bytes = 0; + if (WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), + &wsasendmsg, sizeof(wsasendmsg), &bytes, nullptr, nullptr) != 0) { + // fallback to send() + sent = ::send(sock, buf, remaining, 0); + } else { + DWORD bytesSent = 0; + int res = wsasendmsg( + sock, reinterpret_cast(const_cast(msg)), 0, + &bytesSent, nullptr, nullptr); + if (res == SOCKET_ERROR) + return ConvertReturnErrorCode(WSAGetLastError()); + sent = bytesSent; + } + } else { + sent = ::send(sock, buf, remaining, 0); + if (sent == SOCKET_ERROR) + return ConvertReturnErrorCode(WSAGetLastError()); + } + + totalSent += sent; + remaining -= sent; + buf += sent; + + if (!waitAll) + break; + } } - int res = wsasendmsg(sock, reinterpret_cast(const_cast(msg)), flags, - &bytesSent, nullptr, nullptr); + return totalSent; - if (res == SOCKET_ERROR) { - return ConvertReturnErrorCode(-1); - } - return static_cast(bytesSent); #else - int res = sendmsg(sock, reinterpret_cast(msg), flags); + int native_flags = convertOrbisFlagsToPosix(socket_type, flags); + int res = sendmsg(sock, reinterpret_cast(msg), native_flags); return ConvertReturnErrorCode(res); #endif } @@ -213,37 +288,92 @@ int PosixSocket::SendMessage(const OrbisNetMsghdr* msg, int flags) { int PosixSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to, u32 tolen) { std::scoped_lock lock{m_mutex}; - if (to != nullptr) { - sockaddr addr; - convertOrbisNetSockaddrToPosix(to, &addr); - return ConvertReturnErrorCode( - sendto(sock, (const char*)msg, len, flags, &addr, sizeof(sockaddr_in))); - } else { - return ConvertReturnErrorCode(send(sock, (const char*)msg, len, flags)); + int res = 0; +#ifdef _WIN32 + if (flags & ORBIS_NET_MSG_DONTWAIT) { + res = socket_is_ready(sock, false); + if (res <= 0) + return res; } +#endif + const auto posix_flags = convertOrbisFlagsToPosix(socket_type, flags); + if (to == nullptr) { + res = send(sock, (const char*)msg, len, posix_flags); + } else { + sockaddr addr{}; + convertOrbisNetSockaddrToPosix(to, &addr); + res = sendto(sock, (const char*)msg, len, posix_flags, &addr, tolen); + } + return ConvertReturnErrorCode(res); } int PosixSocket::ReceiveMessage(OrbisNetMsghdr* msg, int flags) { std::scoped_lock lock{receive_mutex}; + #ifdef _WIN32 - LPFN_WSARECVMSG wsarecvmsg = nullptr; - GUID guid = WSAID_WSARECVMSG; - DWORD bytes = 0; + int totalReceived = 0; + bool waitAll = (flags & ORBIS_NET_MSG_WAITALL) != 0; + bool dontWait = (flags & ORBIS_NET_MSG_DONTWAIT) != 0; - if (WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &wsarecvmsg, - sizeof(wsarecvmsg), &bytes, nullptr, nullptr) != 0) { - return ConvertReturnErrorCode(-1); + // stream socket with multiple buffers + bool use_wsarecvmsg = + (socket_type == ORBIS_NET_SOCK_STREAM || socket_type == ORBIS_NET_SOCK_STREAM_P2P) && + msg->msg_iovlen > 1; + + for (int i = 0; i < msg->msg_iovlen; ++i) { + char* buf = (char*)msg->msg_iov[i].iov_base; + size_t remaining = msg->msg_iov[i].iov_len; + + while (remaining > 0) { + // emulate DONTWAIT + if (dontWait) { + int ready = socket_is_ready(sock, true); + if (ready <= 0) + return ready; // returns ORBIS_NET_ERROR_EWOULDBLOCK or error + } + + int received = 0; + if (use_wsarecvmsg) { + // only call WSARecvMsg if multiple buffers + stream + LPFN_WSARECVMSG wsarecvmsg = nullptr; + GUID guid = WSAID_WSARECVMSG; + DWORD bytes = 0; + if (WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), + &wsarecvmsg, sizeof(wsarecvmsg), &bytes, nullptr, nullptr) != 0) { + // fallback to recv() + received = ::recv(sock, buf, remaining, 0); + if (received == SOCKET_ERROR) + return ConvertReturnErrorCode(WSAGetLastError()); + } else { + DWORD bytesReceived = 0; + int res = wsarecvmsg(sock, reinterpret_cast(msg), &bytesReceived, + nullptr, nullptr); + if (res == SOCKET_ERROR) + return ConvertReturnErrorCode(WSAGetLastError()); + received = bytesReceived; + } + } else { + // fallback to recv() for UDP or single-buffer + received = ::recv(sock, buf, remaining, 0); + if (received == SOCKET_ERROR) + return ConvertReturnErrorCode(WSAGetLastError()); + } + + totalReceived += received; + remaining -= received; + buf += received; + + // stop after first receive if WAITALL is not set + if (!waitAll) + break; + } } - DWORD bytesReceived = 0; - int res = wsarecvmsg(sock, reinterpret_cast(msg), &bytesReceived, nullptr, nullptr); + return totalReceived; - if (res == SOCKET_ERROR) { - return ConvertReturnErrorCode(-1); - } - return static_cast(bytesReceived); #else - int res = recvmsg(sock, reinterpret_cast(msg), flags); + int native_flags = convertOrbisFlagsToPosix(socket_type, flags); + int res = recvmsg(sock, reinterpret_cast(msg), native_flags); return ConvertReturnErrorCode(res); #endif } @@ -251,15 +381,27 @@ int PosixSocket::ReceiveMessage(OrbisNetMsghdr* msg, int flags) { int PosixSocket::ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, u32* fromlen) { std::scoped_lock lock{receive_mutex}; - if (from != nullptr) { - sockaddr addr; - int res = recvfrom(sock, (char*)buf, len, flags, &addr, (socklen_t*)fromlen); - convertPosixSockaddrToOrbis(&addr, from); - *fromlen = sizeof(OrbisNetSockaddrIn); - return ConvertReturnErrorCode(res); - } else { - return ConvertReturnErrorCode(recv(sock, (char*)buf, len, flags)); + int res = 0; +#ifdef _WIN32 + if (flags & ORBIS_NET_MSG_DONTWAIT) { + res = socket_is_ready(sock); + if (res <= 0) + return res; } +#endif + const auto posix_flags = convertOrbisFlagsToPosix(socket_type, flags); + if (from == nullptr) { + res = recv(sock, (char*)buf, len, posix_flags); + } else { + sockaddr addr{}; + socklen_t addrlen = sizeof(addr); + res = recvfrom(sock, (char*)buf, len, posix_flags, &addr, + (fromlen && *fromlen <= sizeof(addr) ? (socklen_t*)fromlen : &addrlen)); + if (res > 0) + convertPosixSockaddrToOrbis(&addr, from); + } + + return ConvertReturnErrorCode(res); } SocketPtr PosixSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) { diff --git a/src/core/libraries/network/sockets.h b/src/core/libraries/network/sockets.h index 281c83ab4..86661c71c 100644 --- a/src/core/libraries/network/sockets.h +++ b/src/core/libraries/network/sockets.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -62,7 +62,7 @@ struct OrbisNetLinger { s32 l_linger; }; struct Socket { - explicit Socket(int domain, int type, int protocol) {} + explicit Socket(int domain, int type, int protocol) : socket_type(type) {} virtual ~Socket() = default; virtual bool IsValid() const = 0; virtual int Close() = 0; @@ -84,6 +84,7 @@ struct Socket { virtual std::optional Native() = 0; std::mutex m_mutex; std::mutex receive_mutex; + int socket_type; }; struct PosixSocket : public Socket { diff --git a/src/core/signals.cpp b/src/core/signals.cpp index 4099ac237..db6e4b6cc 100644 --- a/src/core/signals.cpp +++ b/src/core/signals.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/arch.h" @@ -42,14 +42,6 @@ static LONG WINAPI SignalHandler(EXCEPTION_POINTERS* pExp) noexcept { #else -static std::string GetThreadName() { - char name[256]; - if (pthread_getname_np(pthread_self(), name, sizeof(name)) != 0) { - return ""; - } - return std::string{name}; -} - static std::string DisassembleInstruction(void* code_address) { char buffer[256] = ""; @@ -80,18 +72,16 @@ static void SignalHandler(int sig, siginfo_t* info, void* raw_context) { case SIGBUS: { const bool is_write = Common::IsWriteError(raw_context); if (!signals->DispatchAccessViolation(raw_context, info->si_addr)) { - UNREACHABLE_MSG( - "Unhandled access violation in thread '{}' at code address {}: {} address {}", - GetThreadName(), fmt::ptr(code_address), is_write ? "Write to" : "Read from", - fmt::ptr(info->si_addr)); + UNREACHABLE_MSG("Unhandled access violation at code address {}: {} address {}", + fmt::ptr(code_address), is_write ? "Write to" : "Read from", + fmt::ptr(info->si_addr)); } break; } case SIGILL: if (!signals->DispatchIllegalInstruction(raw_context)) { - UNREACHABLE_MSG("Unhandled illegal instruction in thread '{}' at code address {}: {}", - GetThreadName(), fmt::ptr(code_address), - DisassembleInstruction(code_address)); + UNREACHABLE_MSG("Unhandled illegal instruction at code address {}: {}", + fmt::ptr(code_address), DisassembleInstruction(code_address)); } break; case SIGUSR1: { // Sleep thread until signal is received diff --git a/src/emulator.cpp b/src/emulator.cpp index ba22b8fc8..978c276b4 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -14,6 +14,7 @@ #include "common/debug.h" #include "common/logging/backend.h" #include "common/logging/log.h" +#include "common/thread.h" #include "core/ipc/ipc.h" #ifdef ENABLE_DISCORD_RPC #include "common/discord_rpc_handler.h" @@ -76,6 +77,7 @@ Emulator::~Emulator() {} void Emulator::Run(std::filesystem::path file, std::vector args, std::optional p_game_folder) { + Common::SetCurrentThreadName("Main Thread"); if (waitForDebuggerBeforeRun) { Debugger::WaitForDebuggerAttach(); }