diff --git a/.ci/build-mac-arm64.sh b/.ci/build-mac-arm64.sh
index 49b82737c7..043e421d80 100755
--- a/.ci/build-mac-arm64.sh
+++ b/.ci/build-mac-arm64.sh
@@ -6,7 +6,7 @@ export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
export HOMEBREW_NO_ENV_HINTS=1
export HOMEBREW_NO_INSTALL_CLEANUP=1
-brew install -f --overwrite --quiet googletest opencv@4 ffmpeg@5 "llvm@$LLVM_COMPILER_VER" glew sdl3 vulkan-headers vulkan-loader
+brew install -f --overwrite --quiet googletest opencv@4 ffmpeg@5 "llvm@$LLVM_COMPILER_VER" sdl3 vulkan-headers vulkan-loader
brew unlink --quiet ffmpeg qtbase qtsvg qtdeclarative
brew link -f --quiet "llvm@$LLVM_COMPILER_VER" ffmpeg@5
diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh
index 6328ce05d3..e391e3e575 100755
--- a/.ci/build-mac.sh
+++ b/.ci/build-mac.sh
@@ -10,7 +10,7 @@ brew install -f --overwrite --quiet ccache "llvm@$LLVM_COMPILER_VER"
brew link -f --overwrite --quiet "llvm@$LLVM_COMPILER_VER"
# shellcheck disable=SC3009
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
-arch -x86_64 /usr/local/bin/brew install -f --overwrite --quiet python@3.14 opencv@4 ffmpeg@5 "llvm@$LLVM_COMPILER_VER" glew sdl3 vulkan-headers vulkan-loader
+arch -x86_64 /usr/local/bin/brew install -f --overwrite --quiet python@3.14 opencv@4 ffmpeg@5 "llvm@$LLVM_COMPILER_VER" sdl3 vulkan-headers vulkan-loader
arch -x86_64 /usr/local/bin/brew unlink --quiet ffmpeg qtbase qtsvg qtdeclarative
arch -x86_64 /usr/local/bin/brew link -f --overwrite --quiet "llvm@$LLVM_COMPILER_VER" ffmpeg@5
diff --git a/.ci/deploy-mac-arm64.sh b/.ci/deploy-mac-arm64.sh
index 8a27d04676..a4661dc0dd 100755
--- a/.ci/deploy-mac-arm64.sh
+++ b/.ci/deploy-mac-arm64.sh
@@ -57,6 +57,10 @@ else
rm -f translations.zip
fi
+# Copy Qt translations manually
+QT_TRANS="$WORKDIR/qt-downloader/$QT_VER/clang_64/translations"
+cp $QT_TRANS/qt*.qm rpcs3.app/Contents/translations
+
# Hack
install_name_tool -delete_rpath /opt/homebrew/lib RPCS3.app/Contents/MacOS/rpcs3 || echo "Hack for deleting rpath /opt/homebrew/lib not needed"
install_name_tool -delete_rpath /opt/homebrew/opt/llvm@$LLVM_COMPILER_VER/lib RPCS3.app/Contents/MacOS/rpcs3 || echo "Hack for deleting rpath /opt/homebrew/opt/llvm@$LLVM_COMPILER_VER/lib not needed"
diff --git a/.ci/deploy-mac.sh b/.ci/deploy-mac.sh
index ec71fe0262..b0bfb4b455 100755
--- a/.ci/deploy-mac.sh
+++ b/.ci/deploy-mac.sh
@@ -58,6 +58,10 @@ else
rm -f translations.zip
fi
+# Copy Qt translations manually
+QT_TRANS="$WORKDIR/qt-downloader/$QT_VER/clang_64/translations"
+cp $QT_TRANS/qt*.qm rpcs3.app/Contents/translations
+
# Need to do this rename hack due to case insensitive filesystem
mv rpcs3.app RPCS3_.app
mv RPCS3_.app RPCS3.app
diff --git a/.ci/deploy-windows-clang.sh b/.ci/deploy-windows-clang.sh
index c95f82e7b8..0bf731e7c8 100644
--- a/.ci/deploy-windows-clang.sh
+++ b/.ci/deploy-windows-clang.sh
@@ -38,7 +38,7 @@ else
echo "Failed to download translations.zip. Continuing without translations."
exit 0
}
- unzip -o translations.zip -d "./bin/share/qt6/translations" >/dev/null 2>&1 || \
+ 7z x translations.zip -o"./bin/share/qt6/translations" >/dev/null 2>&1 || \
echo "Failed to extract translations.zip. Continuing without translations."
rm -f translations.zip
fi
diff --git a/.ci/setup-windows.sh b/.ci/setup-windows.sh
index f637cec9ad..d874c7a7f0 100755
--- a/.ci/setup-windows.sh
+++ b/.ci/setup-windows.sh
@@ -14,6 +14,7 @@ QT_DECL_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtdeclarative${QT_SUFFIX}"
QT_TOOL_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qttools${QT_SUFFIX}"
QT_MM_URL="${QT_HOST}${QT_PREFIX}addons.qtmultimedia.${QT_PREFIX_2}qtmultimedia${QT_SUFFIX}"
QT_SVG_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtsvg${QT_SUFFIX}"
+QT_TRANSLATIONS_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qttranslations${QT_SUFFIX}"
LLVMLIBS_URL="https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-${LLVM_VER}/llvmlibs_mt.7z"
VULKAN_SDK_URL="https://www.dropbox.com/scl/fi/sjjh0fc4ld281pjbl2xzu/VulkanSDK-${VULKAN_VER}-Installer.exe?rlkey=f6wzc0lvms5vwkt2z3qabfv9d&dl=1"
CCACHE_URL="https://github.com/ccache/ccache/releases/download/v4.11.2/ccache-4.11.2-windows-x86_64.zip"
@@ -24,6 +25,7 @@ DEP_URLS=" \
$QT_TOOL_URL \
$QT_MM_URL \
$QT_SVG_URL \
+ $QT_TRANSLATIONS_URL \
$LLVMLIBS_URL \
$VULKAN_SDK_URL\
$CCACHE_URL"
diff --git a/.github/workflows/rpcs3.yml b/.github/workflows/rpcs3.yml
index 7bb68f1f03..e5c4e6ec61 100644
--- a/.github/workflows/rpcs3.yml
+++ b/.github/workflows/rpcs3.yml
@@ -528,7 +528,11 @@ jobs:
env:
CCACHE_DIR: ${{ github.workspace }}/ccache
QT_VER_MAIN: '6'
- LLVM_COMPILER_VER: '19'
+ LLVM_COMPILER_VER: '-devel'
+ CC: 'clang-devel'
+ CXX: 'clang++-devel'
+ LLVM_CONFIG: 'llvm-config-devel'
+
steps:
- name: Checkout repository
uses: actions/checkout@main
@@ -547,8 +551,10 @@ jobs:
id: root
uses: vmactions/freebsd-vm@v1
with:
- envs: 'QT_VER_MAIN LLVM_COMPILER_VER CCACHE_DIR'
+ envs: 'QT_VER_MAIN LLVM_COMPILER_VER CCACHE_DIR CC CXX LLVM_CONFIG'
usesh: true
+ copyback: false
+ release: "14.3"
run: .ci/install-freebsd.sh && .ci/build-freebsd.sh
- name: Save Build Ccache
diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt
index a800ba1dd5..3b2dc05f1f 100644
--- a/3rdparty/CMakeLists.txt
+++ b/3rdparty/CMakeLists.txt
@@ -107,7 +107,7 @@ add_subdirectory(yaml-cpp)
# OpenGL
-if (NOT ANDROID)
+if (NOT ANDROID AND NOT APPLE)
find_package(OpenGL REQUIRED OPTIONAL_COMPONENTS EGL)
add_library(3rdparty_opengl INTERFACE)
@@ -119,8 +119,6 @@ if (NOT ANDROID)
else()
target_link_libraries(3rdparty_opengl INTERFACE dxgi.lib d2d1.lib dwrite.lib)
endif()
- elseif(APPLE)
- target_link_libraries(3rdparty_opengl INTERFACE OpenGL::GL OpenGL::GLU)
else()
target_link_libraries(3rdparty_opengl INTERFACE OpenGL::GL OpenGL::GLU OpenGL::GLX)
endif()
@@ -335,7 +333,7 @@ endif()
# GLEW
add_library(3rdparty_glew INTERFACE)
-if(NOT MSVC AND NOT ANDROID)
+if(NOT MSVC AND NOT ANDROID AND NOT APPLE)
find_package(GLEW REQUIRED)
target_link_libraries(3rdparty_glew INTERFACE GLEW::GLEW)
endif()
diff --git a/3rdparty/OpenAL/openal-soft b/3rdparty/OpenAL/openal-soft
index 0e5e98e4ac..75c0059630 160000
--- a/3rdparty/OpenAL/openal-soft
+++ b/3rdparty/OpenAL/openal-soft
@@ -1 +1 @@
-Subproject commit 0e5e98e4ac8adae92e4f7653dd6eee17aa9c8791
+Subproject commit 75c00596307bf05ba7bbc8c7022836bf52f17477
diff --git a/3rdparty/libpng/libpng b/3rdparty/libpng/libpng
index 49363adcfa..4e3f57d50f 160000
--- a/3rdparty/libpng/libpng
+++ b/3rdparty/libpng/libpng
@@ -1 +1 @@
-Subproject commit 49363adcfaf098748d7a4c8c624ad8c45a8c3a86
+Subproject commit 4e3f57d50f552841550a36eabbb3fbcecacb7750
diff --git a/3rdparty/libsdl-org/SDL b/3rdparty/libsdl-org/SDL
index 7f3ae3d574..a962f40bbb 160000
--- a/3rdparty/libsdl-org/SDL
+++ b/3rdparty/libsdl-org/SDL
@@ -1 +1 @@
-Subproject commit 7f3ae3d57459e59943a4ecfefc8f6277ec6bf540
+Subproject commit a962f40bbba175e9716557a25d5d7965f134a3d3
diff --git a/3rdparty/libsdl-org/SDL.vcxproj b/3rdparty/libsdl-org/SDL.vcxproj
index f0b38ca09f..fd2bcf2f03 100644
--- a/3rdparty/libsdl-org/SDL.vcxproj
+++ b/3rdparty/libsdl-org/SDL.vcxproj
@@ -23,6 +23,7 @@
+
@@ -102,6 +103,7 @@
+
@@ -130,6 +132,8 @@
+
+
@@ -140,7 +144,11 @@
+
+
+
+
@@ -156,6 +164,7 @@
+
@@ -175,7 +184,6 @@
-
@@ -184,20 +192,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -241,6 +264,7 @@
+
@@ -256,13 +280,14 @@
+
+
-
@@ -303,7 +328,6 @@
-
@@ -333,7 +357,6 @@
-
@@ -393,7 +416,6 @@
-
@@ -464,6 +486,7 @@
+
@@ -471,12 +494,11 @@
-
+
-
diff --git a/3rdparty/libsdl-org/SDL.vcxproj.filters b/3rdparty/libsdl-org/SDL.vcxproj.filters
index 5839899c0d..8b7f293ef3 100644
--- a/3rdparty/libsdl-org/SDL.vcxproj.filters
+++ b/3rdparty/libsdl-org/SDL.vcxproj.filters
@@ -214,6 +214,9 @@
{000028b2ea36d7190d13777a4dc70000}
+
+ {695ffc61-5497-4227-b415-15e9bdd5b6bf}
+
@@ -699,9 +702,6 @@
video\yuv2rgb
-
- video\windows
-
video\windows
@@ -831,9 +831,6 @@
render\software
-
- render\software
-
render\software
@@ -911,12 +908,6 @@
-
-
-
-
-
-
render\vulkan
@@ -950,6 +941,60 @@
+
+ video\yuv2rgb
+
+
+ video\yuv2rgb
+
+
+ video\yuv2rgb
+
+
+ video\yuv2rgb
+
+
+ video\yuv2rgb
+
+
+ video\yuv2rgb
+
+
+ video
+
+
+ video
+
+
+ video
+
+
+ misc
+
+
+ haptic\hidapi
+
+
+ haptic\hidapi
+
+
+ core
+
+
+ joystick\hidapi
+
+
+ joystick\hidapi
+
+
+ joystick\hidapi
+
+
+ joystick\hidapi
+
+
+ API Headers
+
@@ -1037,9 +1082,6 @@
core
-
- core\windows
-
core\windows
@@ -1166,9 +1208,6 @@
joystick\dummy
-
- joystick\gdk
-
joystick\hidapi
@@ -1328,9 +1367,6 @@
video\dummy
-
- video\windows
-
video\windows
@@ -1343,9 +1379,6 @@
video\windows
-
- video\windows
-
video\windows
@@ -1508,9 +1541,6 @@
render\software
-
- render\software
-
render\software
@@ -1535,9 +1565,6 @@
-
-
-
render\vulkan
@@ -1579,6 +1606,66 @@
+
+ video\windows
+
+
+ video\yuv2rgb
+
+
+ video\yuv2rgb
+
+
+ video\yuv2rgb
+
+
+ video
+
+
+ misc
+
+
+ haptic\hidapi
+
+
+ haptic\hidapi
+
+
+ core\windows
+
+
+ core\windows
+
+
+ joystick\gdk
+
+
+ joystick\hidapi
+
+
+ joystick\hidapi
+
+
+ joystick\hidapi
+
+
+ joystick\hidapi
+
+
+ joystick\hidapi
+
+
+ joystick\hidapi
+
+
+ joystick\hidapi
+
+
+ joystick\hidapi
+
+
+ joystick\hidapi
+
diff --git a/BUILDING.md b/BUILDING.md
index 60b7046cb2..32cdc6cc03 100644
--- a/BUILDING.md
+++ b/BUILDING.md
@@ -108,9 +108,10 @@ Clone and initialize the repository
```bash
git clone --recurse-submodules https://github.com/RPCS3/rpcs3.git
cd rpcs3
+git submodule sync
# This is automatically done by `git clone --recurse-submodules`,
# but in case you forgot it, you can manually fetch submodules this way:
-git submodule update --init
+git submodule update --init --recursive
```
### Windows
diff --git a/Utilities/cheat_info.cpp b/Utilities/cheat_info.cpp
index cc8934f15a..7745d26732 100644
--- a/Utilities/cheat_info.cpp
+++ b/Utilities/cheat_info.cpp
@@ -34,7 +34,7 @@ bool cheat_info::from_str(std::string_view cheat_line)
s64 val64 = 0;
if (cheat_vec.size() != 5 || !try_to_int64(&val64, cheat_vec[2], 0, cheat_type_max - 1))
{
- log_cheat.fatal("Failed to parse cheat line");
+ log_cheat.error("Failed to parse cheat line: '%s'", cheat_line);
return false;
}
diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt
index 249aca9910..6e5c2cd635 100644
--- a/rpcs3/CMakeLists.txt
+++ b/rpcs3/CMakeLists.txt
@@ -151,8 +151,9 @@ if (NOT ANDROID)
get_target_property(WINDEPLOYQT_EXECUTABLE Qt6::windeployqt IMPORTED_LOCATION)
add_custom_command(TARGET rpcs3 POST_BUILD
COMMAND ${WINDEPLOYQT_EXECUTABLE} --no-compiler-runtime --no-opengl-sw --no-patchqt
- --no-translations --no-system-d3d-compiler --no-system-dxc-compiler --no-quick-import
+ --no-system-d3d-compiler --no-system-dxc-compiler --no-quick-import
--plugindir "$,$/qt6/plugins,$/share/qt6/plugins>"
+ --translationdir "$,$/qt6/translations,$/share/qt6/translations>"
--verbose 0
$
)
diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt
index 8bcd0f5215..8775144360 100644
--- a/rpcs3/Emu/CMakeLists.txt
+++ b/rpcs3/Emu/CMakeLists.txt
@@ -546,7 +546,7 @@ target_sources(rpcs3_emu PRIVATE
RSX/rsx_vertex_data.cpp
)
-if(NOT ANDROID)
+if(NOT ANDROID AND NOT APPLE)
target_sources(rpcs3_emu PRIVATE
RSX/GL/GLCommonDecompiler.cpp
RSX/GL/GLCompute.cpp
@@ -667,6 +667,7 @@ target_link_libraries(rpcs3_emu
3rdparty::yaml-cpp
3rdparty::zlib
3rdparty::zstd
+ 3rdparty::libcurl
)
if(APPLE)
diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp
index 2de2d2cd3c..3c7b299af7 100644
--- a/rpcs3/Emu/Cell/Modules/cellGem.cpp
+++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp
@@ -1501,8 +1501,15 @@ void gem_config_data::operator()()
vc = vc_attribute;
}
- if (g_cfg.io.camera != camera_handler::qt)
+ switch (g_cfg.io.camera)
{
+#ifdef HAVE_SDL3
+ case camera_handler::sdl:
+#endif
+ case camera_handler::qt:
+ break;
+ case camera_handler::fake:
+ case camera_handler::null:
video_conversion_in_progress = false;
done();
continue;
diff --git a/rpcs3/Emu/Cell/Modules/cellMusic.cpp b/rpcs3/Emu/Cell/Modules/cellMusic.cpp
index 83937f7d16..c23bf274b0 100644
--- a/rpcs3/Emu/Cell/Modules/cellMusic.cpp
+++ b/rpcs3/Emu/Cell/Modules/cellMusic.cpp
@@ -215,7 +215,7 @@ error_code cell_music_select_contents()
const std::string vfs_dir_path = vfs::get("/dev_hdd0/music");
const std::string title = get_localized_string(localized_string_id::RSX_OVERLAYS_MEDIA_DIALOG_TITLE);
- error_code error = rsx::overlays::show_media_list_dialog(rsx::overlays::media_list_dialog::media_type::audio, vfs_dir_path, title,
+ error_code error = rsx::overlays::show_media_list_dialog(rsx::overlays::media_list_dialog::media_type::audio, music_selection_context::max_depth, vfs_dir_path, title,
[&music](s32 status, utils::media_info info)
{
sysutil_register_cb([&music, info = std::move(info), status](ppu_thread& ppu) -> s32
diff --git a/rpcs3/Emu/Cell/Modules/cellMusicDecode.cpp b/rpcs3/Emu/Cell/Modules/cellMusicDecode.cpp
index c938f723b1..b7f21b90ad 100644
--- a/rpcs3/Emu/Cell/Modules/cellMusicDecode.cpp
+++ b/rpcs3/Emu/Cell/Modules/cellMusicDecode.cpp
@@ -134,7 +134,7 @@ error_code cell_music_decode_select_contents()
const std::string vfs_dir_path = vfs::get("/dev_hdd0/music");
const std::string title = get_localized_string(localized_string_id::RSX_OVERLAYS_MEDIA_DIALOG_TITLE);
- error_code error = rsx::overlays::show_media_list_dialog(rsx::overlays::media_list_dialog::media_type::audio, vfs_dir_path, title,
+ error_code error = rsx::overlays::show_media_list_dialog(rsx::overlays::media_list_dialog::media_type::audio, music_selection_context::max_depth, vfs_dir_path, title,
[&dec](s32 status, utils::media_info info)
{
sysutil_register_cb([&dec, info = std::move(info), status](ppu_thread& ppu) -> s32
diff --git a/rpcs3/Emu/Cell/Modules/cellPhotoExport.cpp b/rpcs3/Emu/Cell/Modules/cellPhotoExport.cpp
index 8a264bc721..473bd435e7 100644
--- a/rpcs3/Emu/Cell/Modules/cellPhotoExport.cpp
+++ b/rpcs3/Emu/Cell/Modules/cellPhotoExport.cpp
@@ -1,5 +1,6 @@
#include "stdafx.h"
#include "Emu/Cell/PPUModule.h"
+#include "Emu/System.h"
#include "Emu/IdManager.h"
#include "Emu/VFS.h"
#include "cellSysutil.h"
@@ -107,30 +108,16 @@ bool check_photo_path(const std::string& file_path)
return true;
}
-std::string get_available_photo_path(const std::string& filename)
+std::string get_available_photo_path(std::string_view filename)
{
- const std::string photo_dir = "/dev_hdd0/photo/";
- std::string dst_path = vfs::get(photo_dir + filename);
-
- // Do not overwrite existing files. Add a suffix instead.
- for (u32 i = 0; fs::exists(dst_path); i++)
+ std::string_view extension = ".png";
+ if (const auto extension_start = filename.find_last_of('.');
+ extension_start != umax)
{
- const std::string suffix = fmt::format("_%d", i);
- std::string new_filename = filename;
-
- if (const usz pos = new_filename.find_last_of('.'); pos != std::string::npos)
- {
- new_filename.insert(pos, suffix);
- }
- else
- {
- new_filename.append(suffix);
- }
-
- dst_path = vfs::get(photo_dir + new_filename);
+ extension = filename.substr(extension_start);
}
- return dst_path;
+ return Emu.GetCallbacks().get_photo_path(fmt::format("%s%s", Emu.GetTitle(), extension));
}
diff --git a/rpcs3/Emu/Cell/Modules/cellPhotoImport.cpp b/rpcs3/Emu/Cell/Modules/cellPhotoImport.cpp
index d56db1f060..03f6e147ee 100644
--- a/rpcs3/Emu/Cell/Modules/cellPhotoImport.cpp
+++ b/rpcs3/Emu/Cell/Modules/cellPhotoImport.cpp
@@ -142,7 +142,7 @@ error_code select_photo(std::string dst_dir)
const std::string vfs_dir_path = vfs::get("/dev_hdd0/photo");
const std::string title = get_localized_string(localized_string_id::RSX_OVERLAYS_MEDIA_DIALOG_TITLE_PHOTO_IMPORT);
- error_code error = rsx::overlays::show_media_list_dialog(rsx::overlays::media_list_dialog::media_type::photo, vfs_dir_path, title,
+ error_code error = rsx::overlays::show_media_list_dialog(rsx::overlays::media_list_dialog::media_type::photo, umax, vfs_dir_path, title,
[&pi_manager, dst_dir](s32 status, utils::media_info info)
{
sysutil_register_cb([&pi_manager, dst_dir, info, status](ppu_thread& ppu) -> s32
@@ -176,10 +176,29 @@ error_code select_photo(std::string dst_dir)
const std::string filename = info.path.substr(info.path.find_last_of(fs::delim) + 1);
const std::string title = info.get_metadata("title", filename);
- const std::string dst_path = dst_dir + "/" + filename;
+ std::string dst_path = dst_dir + "/";
std::string sub_type = info.sub_type;
- strcpy_trunc(g_filedata->dstFileName, filename);
+ // Try to find a unique filename (TODO: how does the PS3 copy the files exactly?)
+ std::string extension;
+ std::string dst_filename = filename;
+ if (const auto extension_start = filename.find_last_of('.');
+ extension_start != umax)
+ {
+ extension = filename.substr(extension_start);
+ dst_filename = filename.substr(0, extension_start);
+ }
+
+ std::string suffix = extension;
+ u32 counter = 0;
+ while (!Emu.IsStopped() && fs::is_file(dst_path + dst_filename + suffix))
+ {
+ suffix = fmt::format(" %d%s", ++counter, extension);
+ }
+ dst_filename += std::move(suffix);
+ dst_path += dst_filename;
+
+ strcpy_trunc(g_filedata->dstFileName, dst_filename);
strcpy_trunc(g_filedata->photo_title, title);
strcpy_trunc(g_filedata->game_title, Emu.GetTitle());
strcpy_trunc(g_filedata->game_comment, ""); // TODO
diff --git a/rpcs3/Emu/Cell/Modules/cellScreenshot.cpp b/rpcs3/Emu/Cell/Modules/cellScreenshot.cpp
index b899155dde..060683bdea 100644
--- a/rpcs3/Emu/Cell/Modules/cellScreenshot.cpp
+++ b/rpcs3/Emu/Cell/Modules/cellScreenshot.cpp
@@ -33,18 +33,12 @@ std::string screenshot_info::get_overlay_path() const
std::string screenshot_info::get_photo_title() const
{
- std::string photo = photo_title;
- if (photo.empty())
- photo = Emu.GetTitle();
- return photo;
+ return photo_title.empty() ? Emu.GetTitle() : photo_title;
}
std::string screenshot_info::get_game_title() const
{
- std::string game = game_title;
- if (game.empty())
- game = Emu.GetTitle();
- return game;
+ return game_title.empty() ? Emu.GetTitle() : game_title;
}
std::string screenshot_info::get_game_comment() const
@@ -52,20 +46,6 @@ std::string screenshot_info::get_game_comment() const
return game_comment;
}
-std::string screenshot_info::get_screenshot_path(const std::string& date_path) const
-{
- u32 counter = 0;
- std::string path = vfs::get("/dev_hdd0/photo/" + date_path + "/" + get_photo_title());
- std::string suffix = ".png";
-
- while (!Emu.IsStopped() && fs::is_file(path + suffix))
- {
- suffix = fmt::format("_%d.png", ++counter);
- }
-
- return path + suffix;
-}
-
error_code cellScreenShotSetParameter(vm::cptr param)
{
diff --git a/rpcs3/Emu/Cell/Modules/cellScreenshot.h b/rpcs3/Emu/Cell/Modules/cellScreenshot.h
index 20a8d41cc5..e581400dcd 100644
--- a/rpcs3/Emu/Cell/Modules/cellScreenshot.h
+++ b/rpcs3/Emu/Cell/Modules/cellScreenshot.h
@@ -44,7 +44,6 @@ struct screenshot_info
std::string get_photo_title() const;
std::string get_game_title() const;
std::string get_game_comment() const;
- std::string get_screenshot_path(const std::string& date_path) const;
};
struct screenshot_manager : public screenshot_info
diff --git a/rpcs3/Emu/Cell/Modules/sceNp.h b/rpcs3/Emu/Cell/Modules/sceNp.h
index 6f29a2f8a9..88dd2d816b 100644
--- a/rpcs3/Emu/Cell/Modules/sceNp.h
+++ b/rpcs3/Emu/Cell/Modules/sceNp.h
@@ -1265,7 +1265,7 @@ struct SceNpCommunicationId
// OnlineId structure
struct SceNpOnlineId
{
- char data[16 + 1]; // char term;
+ char data[SCE_NET_NP_ONLINEID_MAX_LENGTH + 1]; // char term;
char dummy[3];
};
diff --git a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp
index 64ac967d8e..1a9491ae6e 100644
--- a/rpcs3/Emu/Cell/Modules/sceNpClans.cpp
+++ b/rpcs3/Emu/Cell/Modules/sceNpClans.cpp
@@ -144,9 +144,9 @@ error_code sceNpClansCreateRequest(vm::ptr handle, u64
}
auto& clans_manager = g_fxo->get();
-
+
s32 reqId = 0;
- SceNpClansError res = clans_manager.client->create_request(&reqId);
+ const SceNpClansError res = clans_manager.client->create_request(reqId);
if (res != SCE_NP_CLANS_SUCCESS)
{
return res;
@@ -167,8 +167,8 @@ error_code sceNpClansDestroyRequest(SceNpClansRequestHandle handle)
}
auto& clans_manager = g_fxo->get();
-
- SceNpClansError res = clans_manager.client->destroy_request(handle);
+
+ const SceNpClansError res = clans_manager.client->destroy_request(handle);
if (res != SCE_NP_CLANS_SUCCESS)
{
return res;
@@ -220,7 +220,7 @@ error_code sceNpClansCreateClan(SceNpClansRequestHandle handle, vm::cptr n
std::string tag_str;
vm::read_string(tag.addr(), SCE_NP_CLANS_CLAN_TAG_MAX_LENGTH, tag_str);
- SceNpClansError res = clans_manager.client->create_clan(nph, handle, name_str, tag_str, clanId);
+ const SceNpClansError res = clans_manager.client->create_clan(nph, handle, name_str, tag_str, clanId);
if (res != SCE_NP_CLANS_SUCCESS)
{
return res;
@@ -246,7 +246,7 @@ error_code sceNpClansDisbandClan(SceNpClansRequestHandle handle, SceNpClanId cla
auto& nph = g_fxo->get>();
auto& clans_manager = g_fxo->get();
- SceNpClansError res = clans_manager.client->disband_dlan(nph, handle, clanId);
+ const SceNpClansError res = clans_manager.client->disband_dlan(nph, handle, clanId);
if (res != SCE_NP_CLANS_SUCCESS)
{
return res;
@@ -283,13 +283,13 @@ error_code sceNpClansGetClanList(SceNpClansRequestHandle handle, vm::cptr host_clanList(SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX);
SceNpClansPagingResult host_pageResult = {};
- SceNpClansError ret = clans_manager.client->get_clan_list(nph, handle, &host_paging, host_clanList, &host_pageResult);
+ const SceNpClansError ret = clans_manager.client->get_clan_list(nph, handle, host_paging, host_clanList, host_pageResult);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
@@ -297,9 +297,9 @@ error_code sceNpClansGetClanList(SceNpClansRequestHandle handle, vm::cptr 0)
{
- std::memcpy(clanList.get_ptr(), host_clanList, sizeof(SceNpClansEntry) * host_pageResult.count);
+ std::memcpy(clanList.get_ptr(), host_clanList.data(), sizeof(SceNpClansEntry) * host_pageResult.count);
}
- std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult));
+ *pageResult = host_pageResult;
return CELL_OK;
}
@@ -383,16 +383,15 @@ error_code sceNpClansSearchByName(SceNpClansRequestHandle handle, vm::cptr host_results(SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX);
SceNpClansPagingResult host_pageResult = {};
- SceNpClansError ret = clans_manager.client->clan_search(handle, &host_paging, &host_search, host_results, &host_pageResult);
+ const SceNpClansError ret = clans_manager.client->clan_search(handle, host_paging, host_search, host_results, host_pageResult);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
@@ -400,9 +399,9 @@ error_code sceNpClansSearchByName(SceNpClansRequestHandle handle, vm::cptr 0)
{
- std::memcpy(results.get_ptr(), host_results, sizeof(SceNpClansClanBasicInfo) * host_pageResult.count);
+ std::memcpy(results.get_ptr(), host_results.data(), sizeof(SceNpClansClanBasicInfo) * host_pageResult.count);
}
- std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult));
+ *pageResult = host_pageResult;
return CELL_OK;
}
@@ -425,14 +424,14 @@ error_code sceNpClansGetClanInfo(SceNpClansRequestHandle handle, SceNpClanId cla
auto& clans_manager = g_fxo->get();
SceNpClansClanInfo host_info = {};
-
- SceNpClansError ret = clans_manager.client->get_clan_info(handle, clanId, &host_info);
+
+ const SceNpClansError ret = clans_manager.client->get_clan_info(handle, clanId, host_info);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
}
-
- std::memcpy(info.get_ptr(), &host_info, sizeof(SceNpClansClanInfo));
+
+ *info = host_info;
return CELL_OK;
}
@@ -455,10 +454,9 @@ error_code sceNpClansUpdateClanInfo(SceNpClansRequestHandle handle, SceNpClanId
auto& nph = g_fxo->get>();
auto& clans_manager = g_fxo->get();
- SceNpClansUpdatableClanInfo host_info = {};
- std::memcpy(&host_info, info.get_ptr(), sizeof(SceNpClansUpdatableClanInfo));
+ const SceNpClansUpdatableClanInfo host_info = *info;
- SceNpClansError ret = clans_manager.client->update_clan_info(nph, handle, clanId, &host_info);
+ const SceNpClansError ret = clans_manager.client->update_clan_info(nph, handle, clanId, host_info);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
@@ -495,13 +493,13 @@ error_code sceNpClansGetMemberList(SceNpClansRequestHandle handle, SceNpClanId c
SceNpClansPagingRequest host_paging = {};
if (paging)
{
- std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest));
+ host_paging = *paging;
}
- SceNpClansMemberEntry host_memList_addr[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {};
+ std::vector host_memList_addr(SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX);
SceNpClansPagingResult host_pageResult = {};
- SceNpClansError ret = clans_manager.client->get_member_list(nph, handle, clanId, &host_paging, status, host_memList_addr, &host_pageResult);
+ const SceNpClansError ret = clans_manager.client->get_member_list(nph, handle, clanId, host_paging, status, host_memList_addr, host_pageResult);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
@@ -509,9 +507,9 @@ error_code sceNpClansGetMemberList(SceNpClansRequestHandle handle, SceNpClanId c
if (memList && host_pageResult.count > 0)
{
- std::memcpy(memList.get_ptr(), host_memList_addr, sizeof(SceNpClansMemberEntry) * host_pageResult.count);
+ std::memcpy(memList.get_ptr(), host_memList_addr.data(), sizeof(SceNpClansMemberEntry) * host_pageResult.count);
}
- std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult));
+ *pageResult = host_pageResult;
return CELL_OK;
}
@@ -533,18 +531,17 @@ error_code sceNpClansGetMemberInfo(SceNpClansRequestHandle handle, SceNpClanId c
auto& nph = g_fxo->get>();
auto& clans_manager = g_fxo->get();
- SceNpId host_npid = {};
- std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId));
+ const SceNpId host_npid = *npid;
SceNpClansMemberEntry host_memInfo = {};
- SceNpClansError ret = clans_manager.client->get_member_info(nph, handle, clanId, host_npid, &host_memInfo);
+ const SceNpClansError ret = clans_manager.client->get_member_info(nph, handle, clanId, host_npid, host_memInfo);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
}
- std::memcpy(memInfo.get_ptr(), &host_memInfo, sizeof(SceNpClansMemberEntry));
+ *memInfo = host_memInfo;
return CELL_OK;
}
@@ -566,10 +563,9 @@ error_code sceNpClansUpdateMemberInfo(SceNpClansRequestHandle handle, SceNpClanI
auto& nph = g_fxo->get>();
auto& clans_manager = g_fxo->get();
- SceNpClansUpdatableMemberInfo host_info = {};
- std::memcpy(&host_info, info.get_ptr(), sizeof(SceNpClansUpdatableMemberInfo));
+ const SceNpClansUpdatableMemberInfo host_info = *info;
- SceNpClansError ret = clans_manager.client->update_member_info(nph, handle, clanId, &host_info);
+ const SceNpClansError ret = clans_manager.client->update_member_info(nph, handle, clanId, host_info);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
@@ -595,10 +591,9 @@ error_code sceNpClansChangeMemberRole(SceNpClansRequestHandle handle, SceNpClanI
auto& nph = g_fxo->get>();
auto& clans_manager = g_fxo->get();
- SceNpId host_npid = {};
- std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId));
+ const SceNpId host_npid = *npid;
- SceNpClansError ret = clans_manager.client->change_member_role(nph, handle, clanId, host_npid, static_cast(role));
+ const SceNpClansError ret = clans_manager.client->change_member_role(nph, handle, clanId, host_npid, static_cast(role));
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
@@ -650,7 +645,7 @@ error_code sceNpClansJoinClan(SceNpClansRequestHandle handle, SceNpClanId clanId
auto& nph = g_fxo->get>();
auto& clans_manager = g_fxo->get();
- SceNpClansError ret = clans_manager.client->join_clan(nph, handle, clanId);
+ const SceNpClansError ret = clans_manager.client->join_clan(nph, handle, clanId);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
@@ -671,7 +666,7 @@ error_code sceNpClansLeaveClan(SceNpClansRequestHandle handle, SceNpClanId clanI
auto& nph = g_fxo->get>();
auto& clans_manager = g_fxo->get();
- SceNpClansError ret = clans_manager.client->leave_clan(nph, handle, clanId);
+ const SceNpClansError ret = clans_manager.client->leave_clan(nph, handle, clanId);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
@@ -705,16 +700,15 @@ error_code sceNpClansKickMember(SceNpClansRequestHandle handle, SceNpClanId clan
auto& nph = g_fxo->get>();
auto& clans_manager = g_fxo->get();
- SceNpId host_npid = {};
- std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId));
+ const SceNpId host_npid = *npid;
SceNpClansMessage host_message = {};
if (message)
{
- std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage));
+ host_message = *message;
}
- SceNpClansError ret = clans_manager.client->kick_member(nph, handle, clanId, host_npid, &host_message);
+ const SceNpClansError ret = clans_manager.client->kick_member(nph, handle, clanId, host_npid, host_message);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
@@ -748,16 +742,15 @@ error_code sceNpClansSendInvitation(SceNpClansRequestHandle handle, SceNpClanId
auto& nph = g_fxo->get>();
auto& clans_manager = g_fxo->get();
- SceNpId host_npid = {};
- std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId));
+ const SceNpId host_npid = *npid;
SceNpClansMessage host_message = {};
if (message)
{
- std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage));
+ host_message = *message;
}
- SceNpClansError ret = clans_manager.client->send_invitation(nph, handle, clanId, host_npid, &host_message);
+ const SceNpClansError ret = clans_manager.client->send_invitation(nph, handle, clanId, host_npid, host_message);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
@@ -783,10 +776,9 @@ error_code sceNpClansCancelInvitation(SceNpClansRequestHandle handle, SceNpClanI
auto& nph = g_fxo->get>();
auto& clans_manager = g_fxo->get();
- SceNpId host_npid = {};
- std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId));
+ const SceNpId host_npid = *npid;
- SceNpClansError ret = clans_manager.client->cancel_invitation(nph, handle, clanId, host_npid);
+ const SceNpClansError ret = clans_manager.client->cancel_invitation(nph, handle, clanId, host_npid);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
@@ -818,15 +810,10 @@ error_code sceNpClansSendInvitationResponse(SceNpClansRequestHandle handle, SceN
SceNpClansMessage host_message = {};
if (message)
{
- std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage));
+ host_message = *message;
}
- if (message)
- {
- std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage));
- }
-
- SceNpClansError ret = clans_manager.client->send_invitation_response(nph, handle, clanId, &host_message, accept);
+ const SceNpClansError ret = clans_manager.client->send_invitation_response(nph, handle, clanId, host_message, accept);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
@@ -858,10 +845,10 @@ error_code sceNpClansSendMembershipRequest(SceNpClansRequestHandle handle, u32 c
SceNpClansMessage host_message = {};
if (message)
{
- std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage));
+ host_message = *message;
}
- SceNpClansError ret = clans_manager.client->request_membership(nph, handle, clanId, &host_message);
+ const SceNpClansError ret = clans_manager.client->request_membership(nph, handle, clanId, host_message);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
@@ -882,8 +869,7 @@ error_code sceNpClansCancelMembershipRequest(SceNpClansRequestHandle handle, Sce
auto& nph = g_fxo->get>();
auto& clans_manager = g_fxo->get();
- SceNpClansError ret = clans_manager.client->cancel_request_membership(nph, handle, clanId);
-
+ const SceNpClansError ret = clans_manager.client->cancel_request_membership(nph, handle, clanId);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
@@ -917,16 +903,15 @@ error_code sceNpClansSendMembershipResponse(SceNpClansRequestHandle handle, SceN
auto& nph = g_fxo->get>();
auto& clans_manager = g_fxo->get();
- SceNpId host_npid = {};
- std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId));
+ const SceNpId host_npid = *npid;
SceNpClansMessage host_message = {};
if (message)
{
- std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage));
+ host_message = *message;
}
- SceNpClansError ret = clans_manager.client->send_membership_response(nph, handle, clanId, host_npid, &host_message, allow);
+ const SceNpClansError ret = clans_manager.client->send_membership_response(nph, handle, clanId, host_npid, host_message, allow);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
@@ -963,13 +948,13 @@ error_code sceNpClansGetBlacklist(SceNpClansRequestHandle handle, SceNpClanId cl
SceNpClansPagingRequest host_paging = {};
if (paging)
{
- std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest));
+ host_paging = *paging;
}
- SceNpClansBlacklistEntry host_blacklist[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {};
+ std::vector host_blacklist(SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX);
SceNpClansPagingResult host_pageResult = {};
- SceNpClansError ret = clans_manager.client->get_blacklist(nph, handle, clanId, &host_paging, host_blacklist, &host_pageResult);
+ const SceNpClansError ret = clans_manager.client->get_blacklist(nph, handle, clanId, host_paging, host_blacklist, host_pageResult);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
@@ -977,9 +962,9 @@ error_code sceNpClansGetBlacklist(SceNpClansRequestHandle handle, SceNpClanId cl
if (bl && host_pageResult.count > 0)
{
- std::memcpy(bl.get_ptr(), host_blacklist, sizeof(SceNpClansBlacklistEntry) * host_pageResult.count);
+ std::memcpy(bl.get_ptr(), host_blacklist.data(), sizeof(SceNpClansBlacklistEntry) * host_pageResult.count);
}
- std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult));
+ *pageResult = host_pageResult;
return CELL_OK;
}
@@ -1001,10 +986,9 @@ error_code sceNpClansAddBlacklistEntry(SceNpClansRequestHandle handle, SceNpClan
auto& nph = g_fxo->get>();
auto& clans_manager = g_fxo->get();
- SceNpId host_member = {};
- std::memcpy(&host_member, member.get_ptr(), sizeof(SceNpId));
+ const SceNpId host_member = *member;
- SceNpClansError ret = clans_manager.client->add_blacklist_entry(nph, handle, clanId, host_member);
+ const SceNpClansError ret = clans_manager.client->add_blacklist_entry(nph, handle, clanId, host_member);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
@@ -1030,10 +1014,9 @@ error_code sceNpClansRemoveBlacklistEntry(SceNpClansRequestHandle handle, SceNpC
auto& nph = g_fxo->get>();
auto& clans_manager = g_fxo->get();
- SceNpId host_member = {};
- std::memcpy(&host_member, member.get_ptr(), sizeof(SceNpId));
+ const SceNpId host_member = *member;
- SceNpClansError ret = clans_manager.client->remove_blacklist_entry(nph, handle, clanId, host_member);
+ const SceNpClansError ret = clans_manager.client->remove_blacklist_entry(nph, handle, clanId, host_member);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
@@ -1070,13 +1053,13 @@ error_code sceNpClansRetrieveAnnouncements(SceNpClansRequestHandle handle, SceNp
SceNpClansPagingRequest host_paging = {};
if (paging)
{
- std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest));
+ host_paging = *paging;
}
- SceNpClansMessageEntry host_announcements[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {};
+ std::vector host_announcements(SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX);
SceNpClansPagingResult host_pageResult = {};
- SceNpClansError ret = clans_manager.client->retrieve_announcements(nph, handle, clanId, &host_paging, host_announcements, &host_pageResult);
+ const SceNpClansError ret = clans_manager.client->retrieve_announcements(nph, handle, clanId, host_paging, host_announcements, host_pageResult);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
@@ -1084,9 +1067,9 @@ error_code sceNpClansRetrieveAnnouncements(SceNpClansRequestHandle handle, SceNp
if (mlist && host_pageResult.count > 0)
{
- std::memcpy(mlist.get_ptr(), host_announcements, sizeof(SceNpClansMessageEntry) * host_pageResult.count);
+ std::memcpy(mlist.get_ptr(), host_announcements.data(), sizeof(SceNpClansMessageEntry) * host_pageResult.count);
}
- std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult));
+ *pageResult = host_pageResult;
return CELL_OK;
}
@@ -1113,17 +1096,16 @@ error_code sceNpClansPostAnnouncement(SceNpClansRequestHandle handle, SceNpClanI
auto& clans_manager = g_fxo->get();
auto& nph = g_fxo->get>();
- SceNpClansMessage host_announcement = {};
- std::memcpy(&host_announcement, message.get_ptr(), sizeof(SceNpClansMessage));
+ const SceNpClansMessage host_announcement = *message;
SceNpClansMessageData host_data = {};
if (data)
{
- std::memcpy(&host_data, data.get_ptr(), sizeof(SceNpClansMessageData));
+ host_data = *data;
}
SceNpClansMessageId host_announcementId = 0;
- SceNpClansError ret = clans_manager.client->post_announcement(nph, handle, clanId, &host_announcement, &host_data, duration, &host_announcementId);
+ const SceNpClansError ret = clans_manager.client->post_announcement(nph, handle, clanId, host_announcement, host_data, duration, host_announcementId);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
@@ -1146,7 +1128,7 @@ error_code sceNpClansRemoveAnnouncement(SceNpClansRequestHandle handle, SceNpCla
auto& clans_manager = g_fxo->get();
auto& nph = g_fxo->get>();
- SceNpClansError ret = clans_manager.client->delete_announcement(nph, handle, clanId, mId);
+ const SceNpClansError ret = clans_manager.client->delete_announcement(nph, handle, clanId, mId);
if (ret != SCE_NP_CLANS_SUCCESS)
{
return ret;
diff --git a/rpcs3/Emu/Cell/lv2/sys_usbd.cpp b/rpcs3/Emu/Cell/lv2/sys_usbd.cpp
index 78183293e7..463ec7a68d 100644
--- a/rpcs3/Emu/Cell/lv2/sys_usbd.cpp
+++ b/rpcs3/Emu/Cell/lv2/sys_usbd.cpp
@@ -246,7 +246,7 @@ private:
{0x054C, 0x01C8, 0x01C8, "PSP Type A", nullptr, nullptr},
{0x054C, 0x01C9, 0x01C9, "PSP Type B", nullptr, nullptr},
{0x054C, 0x01CA, 0x01CA, "PSP Type C", nullptr, nullptr},
- {0x054C, 0x01CB, 0x01CB, "PSP Type D", nullptr, nullptr},
+ {0x054C, 0x01CB, 0x01CB, "PSP Type D", nullptr, nullptr}, // UsbPspCm
{0x054C, 0x02D2, 0x02D2, "PSP Slim", nullptr, nullptr},
// 0x0900: "H050 USJ(C) PCB rev00", 0x0910: "USIO PCB rev00"
@@ -261,9 +261,6 @@ private:
// Tony Hawk RIDE Skateboard
{0x12BA, 0x0400, 0x0400, "Tony Hawk RIDE Skateboard Controller", nullptr, nullptr},
- // PSP in UsbPspCm mode
- {0x054C, 0x01CB, 0x01CB, "UsbPspcm", nullptr, nullptr},
-
// Sony Stereo Headsets
{0x12BA, 0x0032, 0x0032, "Wireless Stereo Headset", nullptr, nullptr},
{0x12BA, 0x0042, 0x0042, "Wireless Stereo Headset", nullptr, nullptr},
@@ -636,6 +633,8 @@ void usb_handler_thread::operator()()
// Process asynchronous requests that are pending
libusb_handle_events_timeout_completed(ctx, &lusb_tv, nullptr);
+ u64 delay = 1'000;
+
// Process fake transfers
if (!fake_transfers.empty())
{
@@ -650,6 +649,13 @@ void usb_handler_thread::operator()()
if (transfer->expected_time > timestamp)
{
+ const u64 diff_time = transfer->expected_time - timestamp;
+
+ if (diff_time < delay)
+ {
+ delay = diff_time;
+ }
+
++it;
continue;
}
@@ -668,7 +674,7 @@ void usb_handler_thread::operator()()
if (handled_devices.empty())
thread_ctrl::wait_for(500'000);
else
- thread_ctrl::wait_for(1'000);
+ thread_ctrl::wait_for(delay);
}
}
@@ -878,7 +884,9 @@ std::pair usb_handler_thread::get_free_transfer()
u32 transfer_id = get_free_transfer_id();
auto& transfer = get_transfer(transfer_id);
- transfer.busy = true;
+
+ libusb_transfer* const transfer_buf = transfer.transfer;
+ transfer = {.transfer_id = transfer_id, .transfer = transfer_buf, .busy = true};
return {transfer_id, transfer};
}
@@ -1046,6 +1054,27 @@ void connect_usb_controller(u8 index, input::product_type type)
}
}
+void reconnect_usb(u32 assigned_number)
+{
+ auto usbh = g_fxo->try_get>();
+ if (!usbh)
+ {
+ return;
+ }
+
+ std::lock_guard lock(usbh->mutex);
+ for (auto& [nr, pair] : usbh->handled_devices)
+ {
+ auto& [internal_dev, dev] = pair;
+ if (nr == assigned_number)
+ {
+ usbh->disconnect_usb_device(dev, false);
+ usbh->connect_usb_device(dev, false);
+ break;
+ }
+ }
+}
+
void handle_hotplug_event(bool connected)
{
if (auto usbh = g_fxo->try_get>())
diff --git a/rpcs3/Emu/Cell/lv2/sys_usbd.h b/rpcs3/Emu/Cell/lv2/sys_usbd.h
index a2fd911e35..76f5f0b061 100644
--- a/rpcs3/Emu/Cell/lv2/sys_usbd.h
+++ b/rpcs3/Emu/Cell/lv2/sys_usbd.h
@@ -89,4 +89,5 @@ error_code sys_usbd_register_extra_ldd(ppu_thread& ppu, u32 handle, vm::cptr s_product, u16 slen_product);
void connect_usb_controller(u8 index, input::product_type);
+void reconnect_usb(u32 assigned_number);
void handle_hotplug_event(bool connected);
diff --git a/rpcs3/Emu/Io/Dimensions.cpp b/rpcs3/Emu/Io/Dimensions.cpp
index e80a64c0c9..84a604a86e 100644
--- a/rpcs3/Emu/Io/Dimensions.cpp
+++ b/rpcs3/Emu/Io/Dimensions.cpp
@@ -700,8 +700,3 @@ void usb_device_dimensions::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoi
break;
}
}
-
-void usb_device_dimensions::isochronous_transfer(UsbTransfer* transfer)
-{
- usb_device_emulated::isochronous_transfer(transfer);
-}
diff --git a/rpcs3/Emu/Io/Dimensions.h b/rpcs3/Emu/Io/Dimensions.h
index e2bfbd1e7f..d25fb8ed2e 100644
--- a/rpcs3/Emu/Io/Dimensions.h
+++ b/rpcs3/Emu/Io/Dimensions.h
@@ -76,7 +76,6 @@ public:
void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override;
void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override;
- void isochronous_transfer(UsbTransfer* transfer) override;
protected:
std::queue> m_queries;
diff --git a/rpcs3/Emu/Io/KamenRider.cpp b/rpcs3/Emu/Io/KamenRider.cpp
index aaa4836f08..df7b86a07f 100644
--- a/rpcs3/Emu/Io/KamenRider.cpp
+++ b/rpcs3/Emu/Io/KamenRider.cpp
@@ -39,7 +39,7 @@ kamen_rider_figure& rider_gate::get_figure_by_uid(const std::array uid)
return figures[7];
}
-void rider_gate::get_blank_response(u8 command, u8 sequence, std::array& reply_buf)
+void rider_gate::get_blank_response(std::array& reply_buf, u8 command, u8 sequence)
{
reply_buf = {0x55, 0x02, command, sequence};
reply_buf[4] = generate_checksum(reply_buf, 4);
@@ -93,7 +93,7 @@ void rider_gate::query_block(std::array& reply_buf, u8 command, u8 seque
reply_buf[21] = generate_checksum(reply_buf, 21);
}
-void rider_gate::write_block(std::array& replyBuf, u8 command, u8 sequence, const u8* uid, u8 sector, u8 block, const u8* to_write_buf)
+void rider_gate::write_block(std::array& reply_buf, u8 command, u8 sequence, const u8* uid, u8 sector, u8 block, const u8* to_write_buf)
{
std::lock_guard lock(kamen_mutex);
@@ -108,7 +108,7 @@ void rider_gate::write_block(std::array& replyBuf, u8 command, u8 sequen
}
}
- get_blank_response(command, sequence, replyBuf);
+ get_blank_response(reply_buf, command, sequence);
}
std::optional> rider_gate::pop_added_removed_response()
@@ -190,11 +190,50 @@ u8 rider_gate::load_figure(const std::array& buf, fs::file in_f
usb_device_kamen_rider::usb_device_kamen_rider(const std::array& location)
: usb_device_emulated(location)
{
- device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, UsbDeviceDescriptor{0x200, 0x0, 0x0, 0x0, 0x40, 0x0E6F, 0x200A, 0x100, 0x1, 0x2, 0x3, 0x1});
- auto& config0 = device.add_node(UsbDescriptorNode(USB_DESCRIPTOR_CONFIG, UsbDeviceConfiguration{0x29, 0x1, 0x1, 0x0, 0x80, 0xFA}));
- config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, UsbDeviceInterface{0x0, 0x0, 0x2, 0x3, 0x0, 0x0, 0x0}));
- config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{0x81, 0x3, 0x40, 0x1}));
- config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{0x1, 0x3, 0x40, 0x1}));
+ device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, UsbDeviceDescriptor{
+ .bcdUSB = 0x0200,
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize0 = 0x40,
+ .idVendor = 0x0E6F,
+ .idProduct = 0x200A,
+ .bcdDevice = 0x0100,
+ .iManufacturer = 0x01,
+ .iProduct = 0x02,
+ .iSerialNumber = 0x03,
+ .bNumConfigurations = 0x01});
+ auto& config0 = device.add_node(UsbDescriptorNode(USB_DESCRIPTOR_CONFIG, UsbDeviceConfiguration{
+ .wTotalLength = 0x0029,
+ .bNumInterfaces = 0x01,
+ .bConfigurationValue = 0x01,
+ .iConfiguration = 0x00,
+ .bmAttributes = 0x80,
+ .bMaxPower = 0xFA}));
+ config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, UsbDeviceInterface{
+ .bInterfaceNumber = 0x00,
+ .bAlternateSetting = 0x00,
+ .bNumEndpoints = 0x02,
+ .bInterfaceClass = 0x03,
+ .bInterfaceSubClass = 0x00,
+ .bInterfaceProtocol = 0x00,
+ .iInterface = 0x00}));
+ config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_HID, UsbDeviceHID{
+ .bcdHID = 0x0100,
+ .bCountryCode = 0x00,
+ .bNumDescriptors = 0x01,
+ .bDescriptorType = 0x22,
+ .wDescriptorLength = 0x001d}));
+ config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{
+ .bEndpointAddress = 0x81,
+ .bmAttributes = 0x03,
+ .wMaxPacketSize = 0x0040,
+ .bInterval = 0x1}));
+ config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{
+ .bEndpointAddress = 0x01,
+ .bmAttributes = 0x03,
+ .wMaxPacketSize = 0x0040,
+ .bInterval = 0x1}));
}
usb_device_kamen_rider::~usb_device_kamen_rider()
@@ -227,7 +266,7 @@ void usb_device_kamen_rider::interrupt_transfer(u32 buf_size, u8* buf, u32 endpo
if (endpoint == 0x81)
{
// Respond after FF command
- transfer->expected_time = get_timestamp() + 1000;
+ transfer->expected_time = get_timestamp() + 22000;
std::optional> response = g_ridergate.pop_added_removed_response();
if (response)
{
@@ -246,6 +285,7 @@ void usb_device_kamen_rider::interrupt_transfer(u32 buf_size, u8* buf, u32 endpo
}
else if (endpoint == 0x01)
{
+ transfer->expected_time = get_timestamp() + 10;
const u8 command = buf[2];
const u8 sequence = buf[3];
@@ -261,7 +301,7 @@ void usb_device_kamen_rider::interrupt_transfer(u32 buf_size, u8* buf, u32 endpo
case 0xC0:
case 0xC3: // Color Commands
{
- g_ridergate.get_blank_response(command, sequence, q_result);
+ g_ridergate.get_blank_response(q_result, command, sequence);
break;
}
case 0xD0: // Tag List
diff --git a/rpcs3/Emu/Io/KamenRider.h b/rpcs3/Emu/Io/KamenRider.h
index 0e30024b06..6c4bea29ee 100644
--- a/rpcs3/Emu/Io/KamenRider.h
+++ b/rpcs3/Emu/Io/KamenRider.h
@@ -18,11 +18,11 @@ struct kamen_rider_figure
class rider_gate
{
public:
- void get_blank_response(u8 command, u8 sequence, std::array& reply_buf);
- void wake_rider_gate(std::array& replyBuf, u8 command, u8 sequence);
- void get_list_tags(std::array& replyBuf, u8 command, u8 sequence);
- void query_block(std::array& replyBuf, u8 command, u8 sequence, const u8* uid, u8 sector, u8 block);
- void write_block(std::array& replyBuf, u8 command, u8 sequence, const u8* uid, u8 sector, u8 block, const u8* to_write_buf);
+ void get_blank_response(std::array& reply_buf, u8 command, u8 sequence);
+ void wake_rider_gate(std::array& reply_buf, u8 command, u8 sequence);
+ void get_list_tags(std::array& reply_buf, u8 command, u8 sequence);
+ void query_block(std::array& reply_buf, u8 command, u8 sequence, const u8* uid, u8 sector, u8 block);
+ void write_block(std::array& reply_buf, u8 command, u8 sequence, const u8* uid, u8 sector, u8 block, const u8* to_write_buf);
std::optional> pop_added_removed_response();
bool remove_figure(u8 position);
diff --git a/rpcs3/Emu/Io/LogitechG27.cpp b/rpcs3/Emu/Io/LogitechG27.cpp
index 5503c606fc..c8aa453592 100644
--- a/rpcs3/Emu/Io/LogitechG27.cpp
+++ b/rpcs3/Emu/Io/LogitechG27.cpp
@@ -17,7 +17,254 @@
#include "Input/pad_thread.h"
#include "Input/sdl_instance.h"
-LOG_CHANNEL(logitech_g27_log, "LOGIG27");
+LOG_CHANNEL(logitech_g27_log, "logitech_g27");
+
+#pragma pack(push, 1)
+struct DFEX_data
+{
+ u8 square : 1;
+ u8 cross : 1;
+ u8 circle : 1;
+ u8 triangle : 1;
+ u8 l1 : 1; // Left_paddle
+ u8 r1 : 1; // Right_paddle
+ u8 l2 : 1;
+ u8 r2 : 1;
+
+ u8 select : 1; // Share
+ u8 start : 1; // Options
+ u8 l3 : 1;
+ u8 r3 : 1;
+ u8 ps : 1;
+ u8 : 3;
+
+ u8 dpad; // 00=N, 01=NE, 02=E, 03=SW, 04=S, 05=SW, 06=W, 07=NW, 08=IDLE
+ u8 steering; // 00=Left, ff=Right
+ u8 brake_throttle; // 00=ThrottlePressed, 7f=BothReleased/BothPressed, ff=BrakePressed
+ u8 const1;
+ u8 const2;
+ u32 : 32;
+ u32 : 32;
+ u16 : 16;
+ u8 brake; // 00=Released, ff=Pressed
+ u8 throttle; // 00=Released, ff=Pressed
+ u32 : 32;
+ u32 : 32;
+};
+
+struct DFP_data
+{
+ u16 steering : 14; // 0000=Left, 1fff=Mid, 3fff=Right
+ u16 cross: 1;
+ u16 square : 1;
+
+ u8 circle : 1;
+ u8 triangle : 1;
+ u8 r1 : 1; // Right_paddle
+ u8 l1 : 1; // Left_paddle
+ u8 r2 : 1;
+ u8 l2 : 1;
+ u8 select : 1; // Share
+ u8 start : 1; // Options
+
+ u8 r3 : 1;
+ u8 l3 : 1;
+ u8 r3_2 : 1;
+ u8 l3_2 : 1;
+ u8 dpad : 4; // 0=N, 1=NE, 2=E, 3=SW, 4=S, 5=SW, 6=W, 7=NW, 8=IDLE
+
+ u8 brake_throttle; // 00=ThrottlePressed, 7f=BothReleased/BothPressed, ff=BrakePressed
+ u8 throttle; // 00=Pressed, ff=Released
+ u8 brake; // 00=Pressed, ff=Released
+
+ u8 pedals_attached : 1;
+ u8 powered : 1;
+ u8 : 1;
+ u8 self_check_done : 1;
+ u8 set1 : 1; // always set
+ u8 : 3;
+};
+
+struct DFGT_data
+{
+ u8 dpad : 4; // 0=N, 1=NE, 2=E, 3=SW, 4=S, 5=SW, 6=W, 7=NW, 8=IDLE
+ u8 cross: 1;
+ u8 square : 1;
+ u8 circle : 1;
+ u8 triangle : 1;
+
+ u8 r1 : 1; // Right_paddle
+ u8 l1 : 1; // Left_paddle
+ u8 r2 : 1;
+ u8 l2 : 1;
+ u8 select : 1; // Share
+ u8 start : 1; // Options
+ u8 r3 : 1;
+ u8 l3 : 1;
+
+ u8 : 2;
+ u8 dial_center : 1;
+ u8 plus : 1;
+ u8 dial_cw : 1;
+ u8 dial_ccw : 1;
+ u8 minus : 1;
+ u8 : 1;
+
+ u8 ps : 1;
+ u8 pedals_attached : 1;
+ u8 powered : 1;
+ u8 self_check_done : 1;
+ u8 set1 : 1;
+ u8 : 1;
+ u8 set2 : 1;
+ u8 : 1;
+
+ u16 steering : 14; // 0000=Left, 1fff=Mid, 3fff=Right
+ u16 : 2;
+ u8 throttle; // 00=Pressed, ff=Released
+ u8 brake; // 00=Pressed, ff=Released
+};
+
+struct G25_data
+{
+ u8 dpad : 4; // 0=N, 1=NE, 2=E, 3=SW, 4=S, 5=SW, 6=W, 7=NW, 8=IDLE
+ u8 cross: 1;
+ u8 square : 1;
+ u8 circle : 1;
+ u8 triangle : 1;
+
+ u8 r1 : 1; // Right_paddle
+ u8 l1 : 1; // Left_paddle
+ u8 r2 : 1; // + dial_center
+ u8 l2 : 1;
+ u8 select : 1; // Share
+ u8 start : 1; // Options
+ u8 r3 : 1; // + dial_cw + plus
+ u8 l3 : 1; // + dial_ccw + minus
+
+ u8 gear1 : 1;
+ u8 gear2 : 1;
+ u8 gear3 : 1;
+ u8 gear4 : 1;
+ u8 gear5 : 1;
+ u8 gear6 : 1;
+ u8 gearR : 1;
+ u8 : 1;
+
+ u16 pedals_detached : 1;
+ u16 powered : 1;
+ u16 steering : 14; // 0000=Left, 1fff=Mid, 3fff=Right
+
+ u8 throttle; // 00=Pressed, ff=Released
+ u8 brake; // 00=Pressed, ff=Released
+ u8 clutch; // 00=Pressed, ff=Released
+
+ u8 shifter_x; // 30=left(1,2), 7a=middle(3,4), b2=right(5,6)
+ u8 shifter_y; // 32=bottom(2,4,6), b7=top(1,3,5)
+
+ u8 shifter_attached : 1;
+ u8 set1 : 1;
+ u8 : 1;
+ u8 shifter_pressed : 1;
+ u8 : 4;
+};
+
+struct G27_data
+{
+ u8 dpad : 4; // 0=N, 1=NE, 2=E, 3=SW, 4=S, 5=SW, 6=W, 7=NW, 8=IDLE
+ u8 cross: 1;
+ u8 square : 1;
+ u8 circle : 1;
+ u8 triangle : 1;
+
+ u8 r1 : 1; // Right_paddle
+ u8 l1 : 1; // Left_paddle
+ u8 r2 : 1;
+ u8 l2 : 1;
+ u8 select : 1; // Share
+ u8 start : 1; // Options
+ u8 r3 : 1; // + dial_center
+ u8 l3 : 1;
+
+ u8 gear1 : 1;
+ u8 gear2 : 1;
+ u8 gear3 : 1;
+ u8 gear4 : 1;
+ u8 gear5 : 1;
+ u8 gear6 : 1;
+ u8 dial_cw : 1;
+ u8 dial_ccw : 1;
+
+ u16 plus : 1;
+ u16 minus : 1;
+ u16 steering : 14; // 0000=Left, 1fff=Mid, 3fff=Right
+
+ u8 throttle; // 00=Pressed, ff=Released
+ u8 brake; // 00=Pressed, ff=Released
+ u8 clutch; // 00=Pressed, ff=Released
+
+ u8 shifter_x; // 30=left(1,2), 7a=middle(3,4), b2=right(5,6)
+ u8 shifter_y; // 32=bottom(2,4,6), b7=top(1,3,5)
+
+ u8 gearR : 1;
+ u8 pedals_detached : 1;
+ u8 powered : 1;
+ u8 shifter_attached : 1;
+ u8 set1 : 1;
+ u8 : 1;
+ u8 shifter_pressed : 1;
+ u8 range : 1;
+};
+#pragma pack(pop)
+
+static const std::map>> s_logitech_personality = {
+{
+ logitech_personality::driving_force_ex,
+ {
+ UsbDeviceDescriptor{0x0200, 0x00, 0x00, 0x00, 0x10, 0x046D, 0xC294, 0x1350, 0x01, 0x02, 0x00, 0x01},
+ {0x09, 0x02, 0x29, 0x00, 0x01, 0x01, 0x04, 0x80, 0x31, 0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00,
+ 0x00, 0x00, 0x09, 0x21, 0x00, 0x01, 0x21, 0x01, 0x22, 0x9D, 0x00, 0x07, 0x05, 0x81, 0x03, 0x40,
+ 0x00, 0x0A, 0x07, 0x05, 0x01, 0x03, 0x10, 0x00, 0x0A}
+ }
+},
+{
+ logitech_personality::driving_force_pro,
+ {
+ UsbDeviceDescriptor{0x0200, 0x00, 0x00, 0x00, 0x10, 0x046D, 0xC298, 0x1350, 0x01, 0x02, 0x00, 0x01},
+ {0x09, 0x02, 0x29, 0x00, 0x01, 0x01, 0x04, 0x80, 0x31, 0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00,
+ 0x00, 0x00, 0x09, 0x21, 0x00, 0x01, 0x21, 0x01, 0x22, 0x61, 0x00, 0x07, 0x05, 0x81, 0x03, 0x08,
+ 0x00, 0x0A, 0x07, 0x05, 0x01, 0x03, 0x08, 0x00, 0x0A}
+ }
+},
+{
+ logitech_personality::g25,
+ {
+ UsbDeviceDescriptor{0x0200, 0x00, 0x00, 0x00, 0x10, 0x046D, 0xC299, 0x1350, 0x01, 0x02, 0x00, 0x01},
+ {0x09, 0x02, 0x29, 0x00, 0x01, 0x01, 0x04, 0x80, 0x31, 0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00,
+ 0x00, 0x00, 0x09, 0x21, 0x11, 0x01, 0x21, 0x01, 0x22, 0x6F, 0x00, 0x07, 0x05, 0x81, 0x03, 0x10,
+ 0x00, 0x02, 0x07, 0x05, 0x01, 0x03, 0x10, 0x00, 0x02}
+ }
+},
+{
+ logitech_personality::driving_force_gt,
+ {
+ UsbDeviceDescriptor{0x0200, 0x00, 0x00, 0x00, 0x10, 0x046D, 0xC29A, 0x1350, 0x00, 0x02, 0x00, 0x01},
+ {0x09, 0x02, 0x29, 0x00, 0x01, 0x01, 0x00, 0x80, 0x31, 0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00,
+ 0x00, 0xFE, 0x09, 0x21, 0x11, 0x01, 0x21, 0x01, 0x22, 0x73, 0x00, 0x07, 0x05, 0x81, 0x03, 0x10,
+ 0x00, 0x02, 0x07, 0x05, 0x01, 0x03, 0x10, 0x00, 0x02}
+ }
+},
+{
+ logitech_personality::g27,
+ {
+ UsbDeviceDescriptor{0x0200, 0x00, 0x00, 0x00, 0x10, 0x046D, 0xC29B, 0x1350, 0x01, 0x02, 0x00, 0x01},
+ {0x09, 0x02, 0x29, 0x00, 0x01, 0x01, 0x04, 0x80, 0x31, 0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00,
+ 0x00, 0x00, 0x09, 0x21, 0x11, 0x01, 0x21, 0x01, 0x22, 0x85, 0x00, 0x07, 0x05, 0x81, 0x03, 0x10,
+ 0x00, 0x02, 0x07, 0x05, 0x01, 0x03, 0x10, 0x00, 0x02}
+ }
+}
+};
// ref: https://github.com/libsdl-org/SDL/issues/7941, need to use SDL_HAPTIC_STEERING_AXIS for some windows drivers
static const SDL_HapticDirection STEERING_DIRECTION =
@@ -26,20 +273,31 @@ static const SDL_HapticDirection STEERING_DIRECTION =
.dir = {0, 0, 0}
};
-usb_device_logitech_g27::usb_device_logitech_g27(u32 controller_index, const std::array& location)
- : usb_device_emulated(location), m_controller_index(controller_index)
+void usb_device_logitech_g27::set_personality(logitech_personality personality, bool reconnect)
{
- device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, UsbDeviceDescriptor{0x0200, 0, 0, 0, 16, 0x046d, 0xc29b, 0x1350, 1, 2, 0, 1});
+ m_personality = personality;
+ device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, ::at32(s_logitech_personality, personality).first);
// parse the raw response like with passthrough device
- static constexpr u8 raw_config[] = {0x9, 0x2, 0x29, 0x0, 0x1, 0x1, 0x4, 0x80, 0x31, 0x9, 0x4, 0x0, 0x0, 0x2, 0x3, 0x0, 0x0, 0x0, 0x9, 0x21, 0x11, 0x1, 0x21, 0x1, 0x22, 0x85, 0x0, 0x7, 0x5, 0x81, 0x3, 0x10, 0x0, 0x2, 0x7, 0x5, 0x1, 0x3, 0x10, 0x0, 0x2};
+ const u8* raw_config = ::at32(s_logitech_personality, personality).second.data();
auto& conf = device.add_node(UsbDescriptorNode(raw_config[0], raw_config[1], &raw_config[2]));
- for (unsigned int index = raw_config[0]; index < sizeof(raw_config);)
+ for (unsigned int index = raw_config[0]; index < raw_config[2];)
{
conf.add_node(UsbDescriptorNode(raw_config[index], raw_config[index + 1], &raw_config[index + 2]));
index += raw_config[index];
}
+ if (reconnect)
+ {
+ reconnect_usb(assigned_number);
+ }
+}
+
+usb_device_logitech_g27::usb_device_logitech_g27(u32 controller_index, const std::array& location)
+ : usb_device_emulated(location), m_controller_index(controller_index)
+{
+ set_personality(logitech_personality::driving_force_ex);
+
m_default_spring_effect.type = SDL_HAPTIC_SPRING;
m_default_spring_effect.condition.direction = STEERING_DIRECTION;
m_default_spring_effect.condition.length = SDL_HAPTIC_INFINITY;
@@ -63,7 +321,14 @@ usb_device_logitech_g27::usb_device_logitech_g27(u32 controller_index, const std
while (thread_ctrl::state() != thread_state::aborting)
{
sdl_refresh();
- thread_ctrl::wait_for(5'000'000);
+ thread_ctrl::wait_for(1'000'000);
+
+ std::unique_lock lock(g_cfg_logitech_g27.m_mutex);
+ if (logitech_personality::invalid != m_next_personality && m_personality != m_next_personality)
+ {
+ set_personality(m_next_personality, true);
+ m_next_personality = logitech_personality::invalid;
+ }
}
});
}
@@ -115,7 +380,7 @@ u16 usb_device_logitech_g27::get_num_emu_devices()
void usb_device_logitech_g27::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer)
{
- logitech_g27_log.todo("control transfer bmRequestType %02x, bRequest %02x, wValue %04x, wIndex %04x, wLength %04x, %s", bmRequestType, bRequest, wValue, wIndex, wLength, fmt::buf_to_hexstring(buf, buf_size));
+ logitech_g27_log.notice("control transfer bmRequestType %02x, bRequest %02x, wValue %04x, wIndex %04x, wLength %04x, %s", bmRequestType, bRequest, wValue, wIndex, wLength, fmt::buf_to_hexstring(buf, buf_size));
usb_device_emulated::control_transfer(bmRequestType, bRequest, wValue, wIndex, wLength, buf_size, buf, transfer);
}
@@ -194,9 +459,11 @@ static inline logitech_g27_sdl_mapping get_runtime_mapping()
convert_mapping(cfg.dial_clockwise, mapping.dial_clockwise);
convert_mapping(cfg.dial_anticlockwise, mapping.dial_anticlockwise);
+ convert_mapping(cfg.dial_center, mapping.dial_center);
convert_mapping(cfg.select, mapping.select);
- convert_mapping(cfg.pause, mapping.pause);
+ convert_mapping(cfg.start, mapping.start);
+ convert_mapping(cfg.ps, mapping.ps);
convert_mapping(cfg.shifter_1, mapping.shifter_1);
convert_mapping(cfg.shifter_2, mapping.shifter_2);
@@ -205,7 +472,6 @@ static inline logitech_g27_sdl_mapping get_runtime_mapping()
convert_mapping(cfg.shifter_5, mapping.shifter_5);
convert_mapping(cfg.shifter_6, mapping.shifter_6);
convert_mapping(cfg.shifter_r, mapping.shifter_r);
- convert_mapping(cfg.shifter_press, mapping.shifter_press);
return mapping;
}
@@ -467,6 +733,50 @@ static u8 hat_components_to_logitech_g27_hat(bool up, bool down, bool left, bool
return sdl_hat_to_logitech_g27_hat(sdl_hat);
}
+static std::pair shifter_to_coord_xy(bool shifter_1, bool shifter_2, bool shifter_3, bool shifter_4,
+ bool shifter_5, bool shifter_6, bool shifter_r)
+{
+ // rough analog values recorded in https://github.com/RPCS3/rpcs3/pull/17199#issuecomment-2883934412
+ constexpr u8 coord_center = 0x80;
+ constexpr u8 coord_top = 0xb7;
+ constexpr u8 coord_bottom = 0x32;
+ constexpr u8 coord_left = 0x30;
+ constexpr u8 coord_right = 0xb3;
+ constexpr u8 coord_right_reverse = 0xaa;
+ if (shifter_1)
+ {
+ return {coord_left, coord_top};
+ }
+ else if (shifter_2)
+ {
+ return {coord_left, coord_bottom};
+ }
+ else if (shifter_3)
+ {
+ return {coord_center, coord_top};
+ }
+ else if (shifter_4)
+ {
+ return {coord_center, coord_bottom};
+ }
+ else if (shifter_5)
+ {
+ return {coord_right, coord_top};
+ }
+ else if (shifter_6)
+ {
+ return {coord_right, coord_bottom};
+ }
+ else if (shifter_r)
+ {
+ return {coord_right_reverse, coord_bottom};
+ }
+ else
+ {
+ return {coord_center, coord_center};
+ }
+}
+
static bool fetch_sdl_as_button(SDL_Joystick* joystick, const sdl_mapping& mapping)
{
switch (mapping.type)
@@ -646,6 +956,220 @@ static inline void set_bit(u8* buf, int bit_num, bool set)
buf[byte_num] = buf[byte_num] & (~mask);
}
+void usb_device_logitech_g27::transfer_dfex(u32 buf_size, u8* buf, UsbTransfer* transfer)
+{
+ DFEX_data data{};
+ ensure(buf_size >= sizeof(data));
+ transfer->expected_count = sizeof(data);
+
+ const std::lock_guard lock(m_sdl_handles_mutex);
+ data.square = sdl_to_logitech_g27_button(m_joysticks, m_mapping.square);
+ data.cross = sdl_to_logitech_g27_button(m_joysticks, m_mapping.cross);
+ data.circle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.circle);
+ data.triangle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.triangle);
+ data.l1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_down);
+ data.r1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_up);
+ data.l2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l2);
+ data.r2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r2);
+ data.select = sdl_to_logitech_g27_button(m_joysticks, m_mapping.select);
+ data.start = sdl_to_logitech_g27_button(m_joysticks, m_mapping.start);
+ data.l3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l3);
+ data.r3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r3);
+ data.dpad = hat_components_to_logitech_g27_hat(
+ sdl_to_logitech_g27_button(m_joysticks, m_mapping.up),
+ sdl_to_logitech_g27_button(m_joysticks, m_mapping.down),
+ sdl_to_logitech_g27_button(m_joysticks, m_mapping.left),
+ sdl_to_logitech_g27_button(m_joysticks, m_mapping.right)
+ );
+ data.steering = sdl_to_logitech_g27_steering(m_joysticks, m_mapping.steering) >> 6;
+ data.brake_throttle = 0x7f;
+ data.const1 = 0x7f;
+ data.const2 = 0x7f;
+ data.brake = 0xff - sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.brake);
+ data.throttle = 0xff - sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.throttle);
+ std::memcpy(buf, &data, sizeof(data));
+}
+
+void usb_device_logitech_g27::transfer_dfp(u32 buf_size, u8* buf, UsbTransfer* transfer)
+{
+ DFP_data data{};
+ ensure(buf_size >= sizeof(data));
+ transfer->expected_count = sizeof(data);
+
+ const std::lock_guard lock(m_sdl_handles_mutex);
+ data.steering = sdl_to_logitech_g27_steering(m_joysticks, m_mapping.steering);
+ data.cross = sdl_to_logitech_g27_button(m_joysticks, m_mapping.cross);
+ data.square = sdl_to_logitech_g27_button(m_joysticks, m_mapping.square);
+ data.circle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.circle);
+ data.triangle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.triangle);
+ data.r1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_up);
+ data.l1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_down);
+ data.r2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r2);
+ data.l2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l2);
+ data.select = sdl_to_logitech_g27_button(m_joysticks, m_mapping.select);
+ data.start = sdl_to_logitech_g27_button(m_joysticks, m_mapping.start);
+ data.r3 = data.r3_2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r3);
+ data.l3 = data.l3_2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l3);
+ data.dpad = hat_components_to_logitech_g27_hat(
+ sdl_to_logitech_g27_button(m_joysticks, m_mapping.up),
+ sdl_to_logitech_g27_button(m_joysticks, m_mapping.down),
+ sdl_to_logitech_g27_button(m_joysticks, m_mapping.left),
+ sdl_to_logitech_g27_button(m_joysticks, m_mapping.right)
+ );
+ data.brake_throttle = 0x7f;
+ data.throttle = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.throttle);
+ data.brake = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.brake);
+ data.pedals_attached = 1;
+ data.powered = 1;
+ data.self_check_done = 1;
+ data.set1 = 1;
+ std::memcpy(buf, &data, sizeof(data));
+}
+
+void usb_device_logitech_g27::transfer_dfgt(u32 buf_size, u8* buf, UsbTransfer* transfer)
+{
+ DFGT_data data{};
+ ensure(buf_size >= sizeof(data));
+ transfer->expected_count = sizeof(data);
+
+ const std::lock_guard lock(m_sdl_handles_mutex);
+ data.dpad = hat_components_to_logitech_g27_hat(
+ sdl_to_logitech_g27_button(m_joysticks, m_mapping.up),
+ sdl_to_logitech_g27_button(m_joysticks, m_mapping.down),
+ sdl_to_logitech_g27_button(m_joysticks, m_mapping.left),
+ sdl_to_logitech_g27_button(m_joysticks, m_mapping.right)
+ );
+ data.cross = sdl_to_logitech_g27_button(m_joysticks, m_mapping.cross);
+ data.square = sdl_to_logitech_g27_button(m_joysticks, m_mapping.square);
+ data.circle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.circle);
+ data.triangle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.triangle);
+ data.r1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_up);
+ data.l1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_down);
+ data.r2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r2);
+ data.l2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l2);
+ data.select = sdl_to_logitech_g27_button(m_joysticks, m_mapping.select);
+ data.start = sdl_to_logitech_g27_button(m_joysticks, m_mapping.start);
+ data.r3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r3);
+ data.l3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l3);
+ data.dial_center = sdl_to_logitech_g27_button(m_joysticks, m_mapping.dial_center);
+ data.plus = sdl_to_logitech_g27_button(m_joysticks, m_mapping.plus);
+ data.dial_cw = sdl_to_logitech_g27_button(m_joysticks, m_mapping.dial_clockwise);
+ data.dial_ccw = sdl_to_logitech_g27_button(m_joysticks, m_mapping.dial_anticlockwise);
+ data.minus = sdl_to_logitech_g27_button(m_joysticks, m_mapping.minus);
+ data.ps = sdl_to_logitech_g27_button(m_joysticks, m_mapping.ps);
+ data.pedals_attached = 1;
+ data.powered = 1;
+ data.self_check_done = 1;
+ data.set1 = 1;
+ data.set2 = 1;
+ data.steering = sdl_to_logitech_g27_steering(m_joysticks, m_mapping.steering);
+ data.throttle = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.throttle);
+ data.brake = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.brake);
+ std::memcpy(buf, &data, sizeof(data));
+}
+
+void usb_device_logitech_g27::transfer_g25(u32 buf_size, u8* buf, UsbTransfer* transfer)
+{
+ G25_data data{};
+ ensure(buf_size >= sizeof(data));
+ transfer->expected_count = sizeof(data);
+
+ const std::lock_guard lock(m_sdl_handles_mutex);
+ data.dpad = hat_components_to_logitech_g27_hat(
+ sdl_to_logitech_g27_button(m_joysticks, m_mapping.up),
+ sdl_to_logitech_g27_button(m_joysticks, m_mapping.down),
+ sdl_to_logitech_g27_button(m_joysticks, m_mapping.left),
+ sdl_to_logitech_g27_button(m_joysticks, m_mapping.right)
+ );
+ data.cross = sdl_to_logitech_g27_button(m_joysticks, m_mapping.cross);
+ data.square = sdl_to_logitech_g27_button(m_joysticks, m_mapping.square);
+ data.circle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.circle);
+ data.triangle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.triangle);
+ data.r1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_up);
+ data.l1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_down);
+ data.r2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r2);
+ data.l2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l2);
+ data.select = sdl_to_logitech_g27_button(m_joysticks, m_mapping.select);
+ data.start = sdl_to_logitech_g27_button(m_joysticks, m_mapping.start);
+ data.r3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r3);
+ data.l3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l3);
+ data.gear1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_1);
+ data.gear2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_2);
+ data.gear3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_3);
+ data.gear4 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_4);
+ data.gear5 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_5);
+ data.gear6 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_6);
+ data.gearR = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_r);
+ data.pedals_detached = 0;
+ data.powered = 1;
+ data.steering = sdl_to_logitech_g27_steering(m_joysticks, m_mapping.steering);
+ data.throttle = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.throttle);
+ data.brake = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.brake);
+ data.clutch = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.clutch);
+ auto [shifter_x, shifter_y] = shifter_to_coord_xy(data.gear1, data.gear2,
+ data.gear3, data.gear4, data.gear5, data.gear6, data.gearR);
+ data.shifter_x = shifter_x;
+ data.shifter_y = shifter_y;
+ data.shifter_attached = 1;
+ data.set1 = 1;
+ data.shifter_pressed = data.gearR;
+ std::memcpy(buf, &data, sizeof(data));
+}
+
+void usb_device_logitech_g27::transfer_g27(u32 buf_size, u8* buf, UsbTransfer* transfer)
+{
+ G27_data data{};
+ ensure(buf_size >= sizeof(data));
+ transfer->expected_count = sizeof(data);
+
+ const std::lock_guard lock(m_sdl_handles_mutex);
+ data.dpad = hat_components_to_logitech_g27_hat(
+ sdl_to_logitech_g27_button(m_joysticks, m_mapping.up),
+ sdl_to_logitech_g27_button(m_joysticks, m_mapping.down),
+ sdl_to_logitech_g27_button(m_joysticks, m_mapping.left),
+ sdl_to_logitech_g27_button(m_joysticks, m_mapping.right)
+ );
+ data.cross = sdl_to_logitech_g27_button(m_joysticks, m_mapping.cross);
+ data.square = sdl_to_logitech_g27_button(m_joysticks, m_mapping.square);
+ data.circle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.circle);
+ data.triangle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.triangle);
+ data.r1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_up);
+ data.l1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_down);
+ data.r2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r2);
+ data.l2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l2);
+ data.select = sdl_to_logitech_g27_button(m_joysticks, m_mapping.select);
+ data.start = sdl_to_logitech_g27_button(m_joysticks, m_mapping.start);
+ data.r3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r3);
+ data.l3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l3);
+ data.gear1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_1);
+ data.gear2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_2);
+ data.gear3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_3);
+ data.gear4 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_4);
+ data.gear5 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_5);
+ data.gear6 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_6);
+ const bool shifter_r = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_r);
+ data.dial_cw = sdl_to_logitech_g27_button(m_joysticks, m_mapping.dial_clockwise);
+ data.dial_ccw = sdl_to_logitech_g27_button(m_joysticks, m_mapping.dial_anticlockwise);
+ data.plus = sdl_to_logitech_g27_button(m_joysticks, m_mapping.plus);
+ data.minus = sdl_to_logitech_g27_button(m_joysticks, m_mapping.minus);
+ data.steering = sdl_to_logitech_g27_steering(m_joysticks, m_mapping.steering);
+ data.throttle = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.throttle);
+ data.brake = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.brake);
+ data.clutch = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.clutch);
+ auto [shifter_x, shifter_y] = shifter_to_coord_xy(data.gear1, data.gear2,
+ data.gear3, data.gear4, data.gear5, data.gear6, shifter_r);
+ data.shifter_x = shifter_x;
+ data.shifter_y = shifter_y;
+ data.gearR = shifter_r;
+ data.pedals_detached = 0;
+ data.powered = 1;
+ data.shifter_attached = 1;
+ data.set1 = 1;
+ data.shifter_pressed = shifter_r;
+ data.range = (m_wheel_range > 360);
+ std::memcpy(buf, &data, sizeof(data));
+}
+
void usb_device_logitech_g27::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer)
{
transfer->fake = true;
@@ -655,168 +1179,31 @@ void usb_device_logitech_g27::interrupt_transfer(u32 buf_size, u8* buf, u32 endp
if (endpoint & (1 << 7))
{
- if (buf_size < 11)
- {
- logitech_g27_log.error("Not populating input buffer with a buffer of the size of %u", buf_size);
- return;
- }
-
- ensure(buf_size >= 11);
memset(buf, 0, buf_size);
-
- transfer->expected_count = 11;
-
sdl_instance::get_instance().pump_events();
- // Fetch input states from SDL
- m_sdl_handles_mutex.lock();
- const u16 steering = sdl_to_logitech_g27_steering(m_joysticks, m_mapping.steering);
- const u8 throttle = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.throttle);
- const u8 brake = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.brake);
- const u8 clutch = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.clutch);
- const bool shift_up = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_up);
- const bool shift_down = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_down);
-
- const bool up = sdl_to_logitech_g27_button(m_joysticks, m_mapping.up);
- const bool down = sdl_to_logitech_g27_button(m_joysticks, m_mapping.down);
- const bool left = sdl_to_logitech_g27_button(m_joysticks, m_mapping.left);
- const bool right = sdl_to_logitech_g27_button(m_joysticks, m_mapping.right);
-
- const bool triangle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.triangle);
- const bool cross = sdl_to_logitech_g27_button(m_joysticks, m_mapping.cross);
- const bool square = sdl_to_logitech_g27_button(m_joysticks, m_mapping.square);
- const bool circle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.circle);
-
- const bool l2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l2);
- const bool l3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l3);
- const bool r2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r2);
- const bool r3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r3);
-
- const bool plus = sdl_to_logitech_g27_button(m_joysticks, m_mapping.plus);
- const bool minus = sdl_to_logitech_g27_button(m_joysticks, m_mapping.minus);
-
- const bool dial_clockwise = sdl_to_logitech_g27_button(m_joysticks, m_mapping.dial_clockwise);
- const bool dial_anticlockwise = sdl_to_logitech_g27_button(m_joysticks, m_mapping.dial_anticlockwise);
-
- const bool select = sdl_to_logitech_g27_button(m_joysticks, m_mapping.select);
- const bool pause = sdl_to_logitech_g27_button(m_joysticks, m_mapping.pause);
-
- const bool shifter_1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_1);
- const bool shifter_2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_2);
- const bool shifter_3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_3);
- const bool shifter_4 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_4);
- const bool shifter_5 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_5);
- const bool shifter_6 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_6);
- const bool shifter_r = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_r);
- const bool shifter_press = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_press);
- m_sdl_handles_mutex.unlock();
-
- // populate buffer
- buf[0] = hat_components_to_logitech_g27_hat(up, down, left, right);
- set_bit(buf, 8, shift_up);
- set_bit(buf, 9, shift_down);
-
- set_bit(buf, 7, triangle);
- set_bit(buf, 4, cross);
- set_bit(buf, 5, square);
- set_bit(buf, 6, circle);
-
- set_bit(buf, 11, l2);
- set_bit(buf, 15, l3);
- set_bit(buf, 10, r2);
- set_bit(buf, 14, r3);
-
- set_bit(buf, 22, dial_clockwise);
- set_bit(buf, 23, dial_anticlockwise);
-
- set_bit(buf, 24, plus);
- set_bit(buf, 25, minus);
-
- set_bit(buf, 12, select);
- set_bit(buf, 13, pause);
-
- set_bit(buf, 16, shifter_1);
- set_bit(buf, 17, shifter_2);
- set_bit(buf, 18, shifter_3);
- set_bit(buf, 19, shifter_4);
- set_bit(buf, 20, shifter_5);
- set_bit(buf, 21, shifter_6);
- set_bit(buf, 80, shifter_r);
-
- // calibrated, unsure
- set_bit(buf, 82, true);
- // shifter connected
- set_bit(buf, 83, true);
- /*
- * shifter pressed/down bit
- * mechanical references:
- * - G29 shifter mechanical explanation https://youtu.be/d7qCn3o8K98?t=1124
- * - same mechanism on the G27 https://youtu.be/rdjejtIfkVA?t=760
- * - same mechanism on the G25 https://youtu.be/eCyt_4luwF0?t=130
- * on healthy G29/G27/G25 shifters, shifter is mechnically kept pressed in reverse, the bit should be set
- * the shifter_press mapping alone captures instead a shifter press without going into reverse, ie. neutral press, just in case there are games using it for input
- */
- set_bit(buf, 86, shifter_press | shifter_r);
-
- buf[3] = (steering << 2) | buf[3];
- buf[4] = steering >> 6;
- buf[5] = throttle;
- buf[6] = brake;
- buf[7] = clutch;
-
- // rough analog values recorded in https://github.com/RPCS3/rpcs3/pull/17199#issuecomment-2883934412
- // buf[8] shifter x
- // buf[9] shifter y
- constexpr u8 shifter_coord_center = 0x80;
- constexpr u8 shifter_coord_top = 0xb7;
- constexpr u8 shifter_coord_bottom = 0x32;
- constexpr u8 shifter_coord_left = 0x30;
- constexpr u8 shifter_coord_right = 0xb3;
- constexpr u8 shifter_coord_right_reverse = 0xaa;
- if (shifter_1)
+ switch (m_personality)
{
- buf[8] = shifter_coord_left;
- buf[9] = shifter_coord_top;
- }
- else if (shifter_2)
- {
- buf[8] = shifter_coord_left;
- buf[9] = shifter_coord_bottom;
- }
- else if (shifter_3)
- {
- buf[8] = shifter_coord_center;
- buf[9] = shifter_coord_top;
- }
- else if (shifter_4)
- {
- buf[8] = shifter_coord_center;
- buf[9] = shifter_coord_bottom;
- }
- else if (shifter_5)
- {
- buf[8] = shifter_coord_right;
- buf[9] = shifter_coord_top;
- }
- else if (shifter_6)
- {
- buf[8] = shifter_coord_right;
- buf[9] = shifter_coord_bottom;
- }
- else if (shifter_r)
- {
- buf[8] = shifter_coord_right_reverse;
- buf[9] = shifter_coord_bottom;
- }
- else
- {
- buf[8] = shifter_coord_center;
- buf[9] = shifter_coord_center;
+ case logitech_personality::driving_force_ex:
+ transfer_dfex(buf_size, buf, transfer);
+ break;
+ case logitech_personality::driving_force_pro:
+ transfer_dfp(buf_size, buf, transfer);
+ break;
+ case logitech_personality::g25:
+ transfer_g25(buf_size, buf, transfer);
+ break;
+ case logitech_personality::driving_force_gt:
+ transfer_dfgt(buf_size, buf, transfer);
+ break;
+ case logitech_personality::g27:
+ transfer_g27(buf_size, buf, transfer);
+ break;
+ case logitech_personality::invalid:
+ fmt::throw_exception("unreachable");
}
- buf[10] = buf[10] | (m_wheel_range > 360 ? 0x90 : 0x10);
-
- // logitech_g27_log.error("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10]);
+ // logitech_g27_log.error("dev=%d, ep in : %s", static_cast(m_personality), fmt::buf_to_hexstring(buf, buf_size));
return;
}
@@ -831,8 +1218,7 @@ void usb_device_logitech_g27::interrupt_transfer(u32 buf_size, u8* buf, u32 endp
transfer->expected_count = buf_size;
- // logitech_g27_log.error("%02x %02x %02x %02x %02x %02x %02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
- // printf("%02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
+ // logitech_g27_log.error("ep out : %s", fmt::buf_to_hexstring(buf, buf_size));
// TODO maybe force clipping from cfg
@@ -843,9 +1229,44 @@ void usb_device_logitech_g27::interrupt_transfer(u32 buf_size, u8* buf, u32 endp
switch (buf[1])
{
case 0x01:
+ case 0x09:
+ case 0x10:
{
- // Change to DFP
- logitech_g27_log.error("Drive Force Pro mode switch command ignored");
+ // Change device mode
+ u8 cmd = buf[1];
+ u8 arg = buf[2];
+ if (buf[8] == 0xf8) // we have 2 commands back to back
+ {
+ cmd = buf[9];
+ arg = buf[10];
+ }
+
+ m_next_personality = logitech_personality::invalid;
+ if (cmd == 0x09 && arg == 0x04)
+ m_next_personality = logitech_personality::g27;
+ else if (cmd == 0x09 && arg == 0x03)
+ m_next_personality = logitech_personality::driving_force_gt;
+ else if ((cmd == 0x09 && arg == 0x02) || cmd == 0x10)
+ m_next_personality = logitech_personality::g25;
+ else if ((cmd == 0x09 && arg == 0x01) || cmd == 0x01)
+ m_next_personality = logitech_personality::driving_force_pro;
+ else if (cmd == 0x09 && arg == 0x00)
+ m_next_personality = logitech_personality::driving_force_ex;
+
+ if (logitech_personality limit = static_cast(g_cfg_logitech_g27.compatibility_limit.get());
+ limit < m_next_personality)
+ {
+ m_next_personality = limit;
+ }
+
+ logitech_g27_log.success("Change device mode : buf=[%s], cmd=0x%x, arg=0x%x, lim=%d -> pers=%d(%s)",
+ fmt::buf_to_hexstring(buf, buf_size), cmd, arg, g_cfg_logitech_g27.compatibility_limit.get(),
+ static_cast(m_next_personality),
+ m_next_personality == logitech_personality::g27 ? "G27"
+ : m_next_personality == logitech_personality::driving_force_gt ? "Driving Force GT"
+ : m_next_personality == logitech_personality::g25 ? "G25"
+ : m_next_personality == logitech_personality::driving_force_pro ? "Driving Force Pro"
+ : m_next_personality == logitech_personality::driving_force_ex ? "Driving Force EX" : "Invalid");
break;
}
case 0x02:
@@ -862,24 +1283,12 @@ void usb_device_logitech_g27::interrupt_transfer(u32 buf_size, u8* buf, u32 endp
m_wheel_range = 900;
break;
}
- case 0x09:
- {
- // Change device mode
- logitech_g27_log.error("Change device mode to %d %s detaching command ignored", buf[2], buf[3] ? "with" : "without");
- break;
- }
case 0x0a:
{
// Revert indentity
logitech_g27_log.error("Revert device identity after reset %s command ignored", buf[2] ? "enable" : "disable");
break;
}
- case 0x10:
- {
- // Switch to G25 with detach
- logitech_g27_log.error("Switch to G25 with detach command ignored");
- break;
- }
case 0x11:
{
// Switch to G25 without detach
diff --git a/rpcs3/Emu/Io/LogitechG27.h b/rpcs3/Emu/Io/LogitechG27.h
index 52735a253d..4ec0d35c0b 100644
--- a/rpcs3/Emu/Io/LogitechG27.h
+++ b/rpcs3/Emu/Io/LogitechG27.h
@@ -16,6 +16,16 @@
#include