From 906c0b8fa69050fab05984878bb1ab23ff5d9432 Mon Sep 17 00:00:00 2001 From: Gliniak Date: Tue, 28 Oct 2025 17:43:21 +0100 Subject: [PATCH 1/4] [CPU] Fixed issue with const path in AVX512 vector left rotate. - Added logging in case of similar issues --- src/xenia/cpu/backend/x64/x64_op.h | 4 ++++ src/xenia/cpu/backend/x64/x64_seq_vector.cc | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/xenia/cpu/backend/x64/x64_op.h b/src/xenia/cpu/backend/x64/x64_op.h index 68e67567c..b923bc31b 100644 --- a/src/xenia/cpu/backend/x64/x64_op.h +++ b/src/xenia/cpu/backend/x64/x64_op.h @@ -175,6 +175,10 @@ struct ValueOp : Op, KEY_TYPE> { virtual bool ConstantFitsIn32Reg() const { return true; } const REG_TYPE& reg() const { assert_true(!is_constant); + if (is_constant) { + XELOGE("{} - Invalid handling of constant! Report this to developers!", + __FUNCTION__); + } return reg_; } operator const REG_TYPE&() const { return reg(); } diff --git a/src/xenia/cpu/backend/x64/x64_seq_vector.cc b/src/xenia/cpu/backend/x64/x64_seq_vector.cc index f1996a36a..40da0ac9a 100644 --- a/src/xenia/cpu/backend/x64/x64_seq_vector.cc +++ b/src/xenia/cpu/backend/x64/x64_seq_vector.cc @@ -1873,7 +1873,12 @@ struct VECTOR_ROTATE_LEFT_V128 } break; case INT32_TYPE: { if (e.IsFeatureEnabled(kX64EmitAVX512Ortho)) { - e.vprolvd(i.dest, i.src1, i.src2); + if (i.src2.is_constant) { + e.LoadConstantXmm(e.xmm0, i.src2.constant()); + e.vprolvd(i.dest, i.src1, e.xmm0); + } else { + e.vprolvd(i.dest, i.src1, i.src2); + } } else if (e.IsFeatureEnabled(kX64EmitAVX2)) { Xmm temp = i.dest; if (i.dest == i.src1 || i.dest == i.src2) { From a25bbed1c385c98ddde0d21acec37a1ce5f5bb23 Mon Sep 17 00:00:00 2001 From: Gliniak Date: Tue, 28 Oct 2025 19:38:51 +0100 Subject: [PATCH 2/4] [XMP] Fixed issue with xmp_default_volume not being correctly applied - Added menu to modify XMP volume and state --- src/xenia/app/emulator_window.cc | 70 ++++++++++++++++++++++++++++++ src/xenia/app/emulator_window.h | 21 +++++++++ src/xenia/apu/audio_media_player.h | 2 +- 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/src/xenia/app/emulator_window.cc b/src/xenia/app/emulator_window.cc index 3816b2cd7..f0968c8b8 100644 --- a/src/xenia/app/emulator_window.cc +++ b/src/xenia/app/emulator_window.cc @@ -645,6 +645,62 @@ void EmulatorWindow::ContentInstallDialog::OnDraw(ImGuiIO& io) { ImGui::End(); } +void EmulatorWindow::XMPConfigDialog::OnDraw(ImGuiIO& io) { + ImGui::SetNextWindowPos(ImVec2(20, 20), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSize(ImVec2(20, 20), ImGuiCond_FirstUseEver); + + bool dialog_open = true; + if (!ImGui::Begin("Audio Player Menu", &dialog_open, + ImGuiWindowFlags_NoCollapse | + ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_HorizontalScrollbar)) { + Close(); + ImGui::End(); + return; + } + + auto audio_player = emulator_window_.emulator_->audio_media_player(); + using xmp_state = kernel::xam::apps::XmpApp::State; + if (audio_player) { + ImGui::Text("Audio player status:"); + ImGui::SameLine(); + switch (audio_player->GetState()) { + case xmp_state::kIdle: + ImGui::Text("Idle"); + break; + case xmp_state::kPaused: + ImGui::Text("Paused"); + break; + case xmp_state::kPlaying: + ImGui::Text("Playing"); + break; + default: + break; + } + + if (audio_player->IsPlaying()) { + if (ImGui::Button("Pause")) { + audio_player->Pause(); + } + } else if (audio_player->IsPaused()) { + if (ImGui::Button("Resume")) { + audio_player->Continue(); + } + } + + if (ImGui::SliderFloat("Audio player volume", &volume_, 0.0f, 1.0f)) { + audio_player->SetVolume(volume_); + } + } + + ImGui::End(); + + if (!dialog_open) { + emulator_window_.ToggleXMPConfigDialog(); + return; + } +} + bool EmulatorWindow::Initialize() { window_->AddListener(&window_listener_); window_->AddInputListener(&window_listener_, kZOrderEmulatorWindowInput); @@ -777,6 +833,11 @@ bool EmulatorWindow::Initialize() { } main_menu->AddChild(std::move(hid_menu)); + // XMP menu + main_menu->AddChild(MenuItem::Create( + MenuItem::Type::kString, "&XMP", "", + std::bind(&EmulatorWindow::ToggleXMPConfigDialog, this))); + // Help menu. auto help_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Help"); { @@ -1468,6 +1529,15 @@ void EmulatorWindow::ToggleProfilesConfigDialog() { } } +void EmulatorWindow::ToggleXMPConfigDialog() { + if (!xmp_config_dialog_) { + xmp_config_dialog_ = std::unique_ptr( + new XMPConfigDialog(imgui_drawer_.get(), *this)); + } else { + xmp_config_dialog_.reset(); + } +} + void EmulatorWindow::ToggleControllerVibration() { auto input_sys = emulator()->input_system(); if (input_sys) { diff --git a/src/xenia/app/emulator_window.h b/src/xenia/app/emulator_window.h index 5b25b92a9..bfc7278a1 100644 --- a/src/xenia/app/emulator_window.h +++ b/src/xenia/app/emulator_window.h @@ -93,6 +93,7 @@ class EmulatorWindow { const xe::ui::RawImage& image); void ToggleProfilesConfigDialog(); + void ToggleXMPConfigDialog(); void SetHotkeysState(bool enabled) { disable_hotkeys_ = !enabled; } // Types of button functions for hotkeys. @@ -208,6 +209,24 @@ class EmulatorWindow { EmulatorWindow& emulator_window_; }; + class XMPConfigDialog final : public ui::ImGuiDialog { + public: + XMPConfigDialog(ui::ImGuiDrawer* imgui_drawer, + EmulatorWindow& emulator_window) + : ui::ImGuiDialog(imgui_drawer), emulator_window_(emulator_window) { + if (emulator_window_.emulator_->audio_media_player()) { + volume_ = emulator_window_.emulator_->audio_media_player()->GetVolume(); + } + } + + protected: + void OnDraw(ImGuiIO& io) override; + + private: + EmulatorWindow& emulator_window_; + float volume_ = 0.0f; + }; + explicit EmulatorWindow(Emulator* emulator, ui::WindowedAppContext& app_context, uint32_t width, uint32_t height); @@ -294,6 +313,8 @@ class EmulatorWindow { // messages back to guest. std::unique_ptr profile_config_dialog_; + std::unique_ptr xmp_config_dialog_; + std::vector recently_launched_titles_; }; diff --git a/src/xenia/apu/audio_media_player.h b/src/xenia/apu/audio_media_player.h index 4343a8b9d..b2733d915 100644 --- a/src/xenia/apu/audio_media_player.h +++ b/src/xenia/apu/audio_media_player.h @@ -106,7 +106,7 @@ class AudioMediaPlayer { XmpApp::PlaybackMode playback_mode_ = XmpApp::PlaybackMode::kInOrder; XmpApp::RepeatMode repeat_mode_ = XmpApp::RepeatMode::kPlaylist; XmpApp::PlaybackFlags playback_flags_ = XmpApp::PlaybackFlags::kDefault; - float volume_ = 1.0f; + float volume_ = 0.0f; uint32_t dash_init_state = 0; std::unordered_map> playlists_; From 344e8538e042f466b7c1396b0741227319af0f5b Mon Sep 17 00:00:00 2001 From: Gliniak Date: Tue, 28 Oct 2025 20:11:47 +0100 Subject: [PATCH 3/4] [XMP] Make volume atomic, fixed issue with invalid SetVolume received from guest - Replaced invalid log --- src/xenia/app/emulator_window.cc | 3 +++ src/xenia/app/emulator_window.h | 4 +++- src/xenia/apu/audio_media_player.cc | 2 +- src/xenia/apu/audio_media_player.h | 4 ++-- src/xenia/kernel/xam/apps/xmp_app.cc | 4 ++-- src/xenia/kernel/xam/apps/xmp_app.h | 2 +- 6 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/xenia/app/emulator_window.cc b/src/xenia/app/emulator_window.cc index f0968c8b8..4c76474e1 100644 --- a/src/xenia/app/emulator_window.cc +++ b/src/xenia/app/emulator_window.cc @@ -688,6 +688,9 @@ void EmulatorWindow::XMPConfigDialog::OnDraw(ImGuiIO& io) { } } + volume_ = + emulator_window_.emulator_->audio_media_player()->GetVolume()->load(); + if (ImGui::SliderFloat("Audio player volume", &volume_, 0.0f, 1.0f)) { audio_player->SetVolume(volume_); } diff --git a/src/xenia/app/emulator_window.h b/src/xenia/app/emulator_window.h index bfc7278a1..de0958902 100644 --- a/src/xenia/app/emulator_window.h +++ b/src/xenia/app/emulator_window.h @@ -215,7 +215,9 @@ class EmulatorWindow { EmulatorWindow& emulator_window) : ui::ImGuiDialog(imgui_drawer), emulator_window_(emulator_window) { if (emulator_window_.emulator_->audio_media_player()) { - volume_ = emulator_window_.emulator_->audio_media_player()->GetVolume(); + volume_ = emulator_window_.emulator_->audio_media_player() + ->GetVolume() + ->load(); } } diff --git a/src/xenia/apu/audio_media_player.cc b/src/xenia/apu/audio_media_player.cc index b88e5aab2..76e460c9e 100644 --- a/src/xenia/apu/audio_media_player.cc +++ b/src/xenia/apu/audio_media_player.cc @@ -448,7 +448,7 @@ void AudioMediaPlayer::RemovePlaylist(uint32_t handle) { } X_STATUS AudioMediaPlayer::SetVolume(float volume) { - volume_ = std::min(volume, 1.0f); + volume_.store(std::min(volume, 1.0f)); std::unique_lock guard(driver_mutex_); if (!driver_) { diff --git a/src/xenia/apu/audio_media_player.h b/src/xenia/apu/audio_media_player.h index b2733d915..6d0745dd5 100644 --- a/src/xenia/apu/audio_media_player.h +++ b/src/xenia/apu/audio_media_player.h @@ -51,7 +51,7 @@ class AudioMediaPlayer { bool IsLastSongInPlaylist() const; X_STATUS SetVolume(float volume); - float GetVolume() const { return volume_; } + const std::atomic* GetVolume() const { return &volume_; } void SetPlaybackMode(XmpApp::PlaybackMode playback_mode) { playback_mode_ = playback_mode; @@ -106,7 +106,7 @@ class AudioMediaPlayer { XmpApp::PlaybackMode playback_mode_ = XmpApp::PlaybackMode::kInOrder; XmpApp::RepeatMode repeat_mode_ = XmpApp::RepeatMode::kPlaylist; XmpApp::PlaybackFlags playback_flags_ = XmpApp::PlaybackFlags::kDefault; - float volume_ = 0.0f; + std::atomic volume_ = 0.0f; uint32_t dash_init_state = 0; std::unordered_map> playlists_; diff --git a/src/xenia/kernel/xam/apps/xmp_app.cc b/src/xenia/kernel/xam/apps/xmp_app.cc index 95176156e..283e17d34 100644 --- a/src/xenia/kernel/xam/apps/xmp_app.cc +++ b/src/xenia/kernel/xam/apps/xmp_app.cc @@ -262,7 +262,7 @@ X_HRESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, xe::store_and_swap( memory_->TranslateVirtual(args->volume_ptr), - kernel_state_->emulator()->audio_media_player()->GetVolume()); + kernel_state_->emulator()->audio_media_player()->GetVolume()->load()); return X_E_SUCCESS; } case 0x0007000C: { @@ -401,7 +401,7 @@ X_HRESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, XMP_CREATE_USER_PLAYLIST_ENUMERATOR* args = reinterpret_cast(buffer); - XELOGD("XMPSetPlaybackController({:08X}, {:08X}, {:08X})", + XELOGD("XMPCreateUserPlaylistEnumerator({:08X}, {:08X}, {:08X})", uint32_t(args->xmp_client), uint32_t(args->flags), uint32_t(args->unk_ptr)); return X_E_SUCCESS; diff --git a/src/xenia/kernel/xam/apps/xmp_app.h b/src/xenia/kernel/xam/apps/xmp_app.h index b0324fd81..92f228102 100644 --- a/src/xenia/kernel/xam/apps/xmp_app.h +++ b/src/xenia/kernel/xam/apps/xmp_app.h @@ -86,7 +86,7 @@ static_assert_size(XMP_GET_VOLUME, 0x8); struct XMP_SET_VOLUME { xe::be xmp_client; - xe::be value; + xe::be value; }; static_assert_size(XMP_SET_VOLUME, 0x8); From 4af2fd6513a69f731f58ac18682ed5eff47805a3 Mon Sep 17 00:00:00 2001 From: "Herman S." <429230+has207@users.noreply.github.com> Date: Fri, 3 Oct 2025 10:15:03 +0900 Subject: [PATCH 4/4] [Kernel] Add Linux socket support and ensure binding low ports is remapped. Remapping ports on both Linux and Windows to ensure games boot under wine as well. --- src/xenia/kernel/xam/xam_net.cc | 51 +++++++++------ src/xenia/kernel/xsocket.cc | 112 ++++++++++++++++++++++++++++++-- 2 files changed, 138 insertions(+), 25 deletions(-) diff --git a/src/xenia/kernel/xam/xam_net.cc b/src/xenia/kernel/xam/xam_net.cc index b7333075b..315596046 100644 --- a/src/xenia/kernel/xam/xam_net.cc +++ b/src/xenia/kernel/xam/xam_net.cc @@ -248,38 +248,42 @@ DECLARE_XAM_EXPORT1(NetDll_XNetRandom, kNetworking, kStub); dword_result_t NetDll_WSAStartup_entry(dword_t caller, word_t version, pointer_t data_ptr) { -// TODO(benvanik): abstraction layer needed. + // TODO(benvanik): abstraction layer needed. + int ret = 0; + #ifdef XE_PLATFORM_WIN32 WSADATA wsaData; ZeroMemory(&wsaData, sizeof(WSADATA)); - int ret = WSAStartup(version, &wsaData); - - auto data_out = kernel_state()->memory()->TranslateVirtual(data_ptr); + ret = WSAStartup(version, &wsaData); +#endif if (data_ptr) { + auto data_out = kernel_state()->memory()->TranslateVirtual(data_ptr); + +#ifdef XE_PLATFORM_WIN32 data_ptr->version = wsaData.wVersion; data_ptr->version_high = wsaData.wHighVersion; - std::memcpy(&data_ptr->description, wsaData.szDescription, 0x100); - std::memcpy(&data_ptr->system_status, wsaData.szSystemStatus, 0x80); data_ptr->max_sockets = wsaData.iMaxSockets; data_ptr->max_udpdg = wsaData.iMaxUdpDg; + std::memcpy(&data_ptr->description, wsaData.szDescription, 0x100); + std::memcpy(&data_ptr->system_status, wsaData.szSystemStatus, 0x80); +#else + // Match Windows behavior with reasonable values + data_ptr->version = version.value(); + data_ptr->version_high = version.value(); + data_ptr->max_sockets = 100; + data_ptr->max_udpdg = 1024; + // WinSock 2.2 typically returns empty strings for these fields + std::memset(&data_ptr->description, 0, 0x100); + std::memset(&data_ptr->system_status, 0, 0x80); +#endif // Some games (5841099F) want this value round-tripped - they'll compare if // it changes and bugcheck if it does. - uint32_t vendor_ptr = xe::load_and_swap(data_out + 0x190); - xe::store_and_swap(data_out + 0x190, vendor_ptr); + // vendor_info_ptr is at offset 0x18A (after max_udpdg at 0x188) + uint32_t vendor_ptr = xe::load_and_swap(data_out + 0x18A); + xe::store_and_swap(data_out + 0x18A, vendor_ptr); } -#else - int ret = 0; - if (data_ptr) { - // Guess these values! - data_ptr->version = version.value(); - data_ptr->description[0] = '\0'; - data_ptr->system_status[0] = '\0'; - data_ptr->max_sockets = 100; - data_ptr->max_udpdg = 1024; - } -#endif // DEBUG /* @@ -659,6 +663,8 @@ dword_result_t NetDll_socket_entry(dword_t caller, dword_t af, dword_t type, socket->Release(); XThread::SetLastError(socket->GetLastWSAError()); + XELOGE("NetDll_socket: failed with error {:08X}", + socket->GetLastWSAError()); return -1; } @@ -742,6 +748,8 @@ dword_result_t NetDll_ioctlsocket_entry(dword_t caller, dword_t socket_handle, X_STATUS status = socket->IOControl(cmd, arg_ptr); if (XFAILED(status)) { XThread::SetLastError(socket->GetLastWSAError()); + XELOGE("NetDll_ioctlsocket: failed with error {:08X}", + socket->GetLastWSAError()); return -1; } @@ -757,6 +765,7 @@ dword_result_t NetDll_bind_entry(dword_t caller, dword_t socket_handle, kernel_state()->object_table()->LookupObject(socket_handle); if (!socket) { XThread::SetLastError(uint32_t(X_WSAError::X_WSAENOTSOCK)); + XELOGE("NetDll_bind: failed - invalid socket"); return -1; } @@ -764,6 +773,7 @@ dword_result_t NetDll_bind_entry(dword_t caller, dword_t socket_handle, X_STATUS status = socket->Bind(&native_name, namelen); if (XFAILED(status)) { XThread::SetLastError(socket->GetLastWSAError()); + XELOGE("NetDll_bind: failed with status {:08X}", status); return -1; } @@ -1027,7 +1037,8 @@ dword_result_t NetDll_sendto_entry(dword_t caller, dword_t socket_handle, } N_XSOCKADDR_IN native_to(to_ptr); - return socket->SendTo(buf_ptr, buf_len, flags, &native_to, to_len); + int ret = socket->SendTo(buf_ptr, buf_len, flags, &native_to, to_len); + return ret; } DECLARE_XAM_EXPORT1(NetDll_sendto, kNetworking, kImplemented); diff --git a/src/xenia/kernel/xsocket.cc b/src/xenia/kernel/xsocket.cc index 0082f34fb..3f9bbaada 100644 --- a/src/xenia/kernel/xsocket.cc +++ b/src/xenia/kernel/xsocket.cc @@ -11,6 +11,7 @@ #include +#include "xenia/base/logging.h" #include "xenia/base/platform.h" #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/xam/xam_module.h" @@ -25,6 +26,7 @@ #include #include #include +#include #include #include #endif @@ -91,10 +93,73 @@ X_STATUS XSocket::SetOption(uint32_t level, uint32_t optname, void* optval_ptr, return X_STATUS_SUCCESS; } - int ret = - setsockopt(native_handle_, level, optname, (char*)optval_ptr, optlen); + // Translate Xbox socket level to native + int native_level = level; + if (level == 0xFFFF) { // Xbox SOL_SOCKET + native_level = SOL_SOCKET; + } + + // Translate Xbox socket options to native + // Xbox uses Winsock constants which mostly match standard values + int native_optname = optname; + if (level == 0xFFFF) { + switch (optname) { + case 0x0001: // SO_DEBUG + native_optname = SO_DEBUG; + break; + case 0x0002: // SO_ACCEPTCONN + native_optname = SO_ACCEPTCONN; + break; + case 0x0004: // SO_REUSEADDR + native_optname = SO_REUSEADDR; + break; + case 0x0008: // SO_KEEPALIVE + native_optname = SO_KEEPALIVE; + break; + case 0x0010: // SO_DONTROUTE + native_optname = SO_DONTROUTE; + break; + case 0x0020: // SO_BROADCAST + native_optname = SO_BROADCAST; + break; + case 0x0080: // SO_LINGER + native_optname = SO_LINGER; + break; + case 0x0100: // SO_OOBINLINE + native_optname = SO_OOBINLINE; + break; + case 0x1001: // SO_SNDBUF + native_optname = SO_SNDBUF; + break; + case 0x1002: // SO_RCVBUF + native_optname = SO_RCVBUF; + break; + case 0x1003: // SO_SNDLOWAT + native_optname = SO_SNDLOWAT; + break; + case 0x1004: // SO_RCVLOWAT + native_optname = SO_RCVLOWAT; + break; + case 0x1005: // SO_SNDTIMEO + native_optname = SO_SNDTIMEO; + break; + case 0x1006: // SO_RCVTIMEO + native_optname = SO_RCVTIMEO; + break; + case 0x1007: // SO_ERROR + native_optname = SO_ERROR; + break; + case 0x1008: // SO_TYPE + native_optname = SO_TYPE; + break; + // Add more translations as needed + } + } + + int ret = setsockopt(native_handle_, native_level, native_optname, + static_cast(optval_ptr), optlen); if (ret < 0) { - // TODO: WSAGetLastError() + XELOGE("XSocket::SetOption: setsockopt failed, errno={}", errno); return X_STATUS_UNSUCCESSFUL; } @@ -113,10 +178,29 @@ X_STATUS XSocket::IOControl(uint32_t cmd, uint8_t* arg_ptr) { // TODO: Get last error return X_STATUS_UNSUCCESSFUL; } - return X_STATUS_SUCCESS; #elif XE_PLATFORM_LINUX - return X_STATUS_UNSUCCESSFUL; + // Translate Xbox/Windows ioctl commands to Linux equivalents + int native_cmd = cmd; + switch (cmd) { + case 0x8004667E: // Windows FIONBIO - set non-blocking mode + native_cmd = FIONBIO; + break; + case 0x4004667F: // Windows FIONREAD - get bytes available + native_cmd = FIONREAD; + break; + default: + XELOGW("XSocket::IOControl: unknown cmd={:08X}, passing through", cmd); + break; + } + + int ret = ioctl(native_handle_, native_cmd, arg_ptr); + if (ret < 0) { + XELOGE("XSocket::IOControl: ioctl failed, cmd={:08X} -> {:08X}, errno={}", + cmd, native_cmd, errno); + return X_STATUS_UNSUCCESSFUL; + } + return X_STATUS_SUCCESS; #endif } @@ -130,8 +214,26 @@ X_STATUS XSocket::Connect(N_XSOCKADDR* name, int name_len) { } X_STATUS XSocket::Bind(N_XSOCKADDR_IN* name, int name_len) { + // On Linux and Windows (when running under Wine), ports < 1024 require root + // privileges. Remap to port + 10000 to avoid privilege issues. + // Note: sin_port is xe::be which automatically handles endianness, + // so we use it directly without ntohs/htons. + const uint16_t original_port = uint16_t(name->sin_port); + if (original_port < 1024) { + uint16_t new_port = original_port + 10000; + name->sin_port = new_port; + XELOGW("XSocket::Bind: port {} requires privileges, remapping to port {}", + original_port, new_port); + } + int ret = bind(native_handle_, (sockaddr*)name, name_len); + if (ret < 0) { +#ifdef XE_PLATFORM_WIN32 + XELOGE("XSocket::Bind: bind() failed with WSA error {}", WSAGetLastError()); +#else + XELOGE("XSocket::Bind: bind() failed with errno={}", errno); +#endif return X_STATUS_UNSUCCESSFUL; }