From b1080c40deed03d087d52233e602ee2114c2cbdf Mon Sep 17 00:00:00 2001 From: DH Date: Sat, 4 Oct 2025 12:14:45 +0300 Subject: [PATCH] removed rpcs3-qt ui --- .ci/build-freebsd.sh | 27 - .ci/build-linux-aarch64.sh | 50 - .ci/build-linux.sh | 51 - .ci/build-mac-arm64.sh | 160 - .ci/build-mac.sh | 123 - .ci/deploy-linux.sh | 35 - .ci/deploy-mac-arm64.sh | 76 - .ci/deploy-mac.sh | 74 - .ci/deploy-windows.sh | 19 - .ci/docker.env | 15 - .ci/export-azure-vars.sh | 13 - .ci/export-cirrus-vars.sh | 13 - .ci/generate-qt-ts.sh | 13 - .ci/get_keys-windows.sh | 4 - .ci/github-upload.sh | 42 - .ci/install-freebsd.sh | 18 - .ci/optimize-mac.sh | 21 - .ci/setup-windows.sh | 134 - .github/workflows/rpcs3.yml | 222 - CMakeLists.txt | 22 +- android/CMakeLists.txt | 6 +- rpcs3qt-legacy/.clang-format | 32 - rpcs3qt-legacy/CMakeLists.txt | 281 - rpcs3qt-legacy/Icons/DualShock_3.svg | 36 - .../Icons/combo_config_bordered.png | Bin 30346 -> 0 bytes rpcs3qt-legacy/Icons/configure.png | Bin 9102 -> 0 bytes rpcs3qt-legacy/Icons/controllers.png | Bin 24754 -> 0 bytes rpcs3qt-legacy/Icons/custom_config.png | Bin 17016 -> 0 bytes rpcs3qt-legacy/Icons/exit_fullscreen.png | Bin 4303 -> 0 bytes rpcs3qt-legacy/Icons/fullscreen.png | Bin 3961 -> 0 bytes rpcs3qt-legacy/Icons/grid.png | Bin 9395 -> 0 bytes rpcs3qt-legacy/Icons/list.png | Bin 4445 -> 0 bytes rpcs3qt-legacy/Icons/open.png | Bin 11299 -> 0 bytes rpcs3qt-legacy/Icons/pause.png | Bin 7524 -> 0 bytes rpcs3qt-legacy/Icons/play.png | Bin 8029 -> 0 bytes rpcs3qt-legacy/Icons/refresh.png | Bin 13725 -> 0 bytes rpcs3qt-legacy/Icons/restart.png | Bin 11633 -> 0 bytes rpcs3qt-legacy/Icons/stop.png | Bin 4405 -> 0 bytes .../Input/basic_keyboard_handler.cpp | 347 -- rpcs3qt-legacy/Input/basic_keyboard_handler.h | 26 - rpcs3qt-legacy/Input/basic_mouse_handler.cpp | 280 - rpcs3qt-legacy/Input/basic_mouse_handler.h | 44 - rpcs3qt-legacy/Input/gui_pad_thread.cpp | 815 --- rpcs3qt-legacy/Input/gui_pad_thread.h | 101 - rpcs3qt-legacy/Input/keyboard_pad_handler.cpp | 1311 ----- rpcs3qt-legacy/Input/keyboard_pad_handler.h | 154 - rpcs3qt-legacy/_discord_utils.cpp | 37 - rpcs3qt-legacy/_discord_utils.h | 15 - rpcs3qt-legacy/about_dialog.cpp | 56 - rpcs3qt-legacy/about_dialog.h | 20 - rpcs3qt-legacy/about_dialog.ui | 468 -- rpcs3qt-legacy/auto_pause_settings_dialog.cpp | 299 -- rpcs3qt-legacy/auto_pause_settings_dialog.h | 56 - .../basic_mouse_settings_dialog.cpp | 298 -- rpcs3qt-legacy/basic_mouse_settings_dialog.h | 43 - rpcs3qt-legacy/breakpoint_handler.cpp | 54 - rpcs3qt-legacy/breakpoint_handler.h | 53 - rpcs3qt-legacy/breakpoint_list.cpp | 287 - rpcs3qt-legacy/breakpoint_list.h | 44 - rpcs3qt-legacy/call_stack_list.cpp | 70 - rpcs3qt-legacy/call_stack_list.h | 31 - rpcs3qt-legacy/camera_settings_dialog.cpp | 284 - rpcs3qt-legacy/camera_settings_dialog.h | 31 - rpcs3qt-legacy/camera_settings_dialog.ui | 130 - rpcs3qt-legacy/category.h | 56 - rpcs3qt-legacy/cg_disasm_window.cpp | 172 - rpcs3qt-legacy/cg_disasm_window.h | 40 - rpcs3qt-legacy/cheat_manager.cpp | 1063 ---- rpcs3qt-legacy/cheat_manager.h | 96 - rpcs3qt-legacy/config_adapter.cpp | 71 - rpcs3qt-legacy/config_adapter.h | 22 - rpcs3qt-legacy/config_checker.cpp | 214 - rpcs3qt-legacy/config_checker.h | 13 - rpcs3qt-legacy/curl_handle.cpp | 85 - rpcs3qt-legacy/curl_handle.h | 36 - rpcs3qt-legacy/custom_dialog.cpp | 36 - rpcs3qt-legacy/custom_dialog.h | 17 - rpcs3qt-legacy/custom_dock_widget.h | 67 - rpcs3qt-legacy/custom_table_widget_item.cpp | 72 - rpcs3qt-legacy/custom_table_widget_item.h | 20 - rpcs3qt-legacy/debugger_add_bp_window.cpp | 116 - rpcs3qt-legacy/debugger_add_bp_window.h | 13 - rpcs3qt-legacy/debugger_frame.cpp | 1740 ------- rpcs3qt-legacy/debugger_frame.h | 146 - rpcs3qt-legacy/debugger_list.cpp | 497 -- rpcs3qt-legacy/debugger_list.h | 67 - rpcs3qt-legacy/dimensions_dialog.cpp | 779 --- rpcs3qt-legacy/dimensions_dialog.h | 63 - rpcs3qt-legacy/display_sleep_control.cpp | 96 - rpcs3qt-legacy/display_sleep_control.h | 4 - rpcs3qt-legacy/downloader.cpp | 204 - rpcs3qt-legacy/downloader.h | 55 - rpcs3qt-legacy/elf_memory_dumping_dialog.cpp | 222 - rpcs3qt-legacy/elf_memory_dumping_dialog.h | 33 - rpcs3qt-legacy/emu_settings.cpp | 1468 ------ rpcs3qt-legacy/emu_settings.h | 130 - rpcs3qt-legacy/emu_settings_type.h | 414 -- .../emulated_pad_settings_dialog.cpp | 609 --- rpcs3qt-legacy/emulated_pad_settings_dialog.h | 41 - rpcs3qt-legacy/fatal_error_dialog.cpp | 54 - rpcs3qt-legacy/fatal_error_dialog.h | 13 - rpcs3qt-legacy/find_dialog.cpp | 117 - rpcs3qt-legacy/find_dialog.h | 35 - rpcs3qt-legacy/flow_layout.cpp | 288 -- rpcs3qt-legacy/flow_layout.h | 115 - rpcs3qt-legacy/flow_widget.cpp | 257 - rpcs3qt-legacy/flow_widget.h | 54 - rpcs3qt-legacy/flow_widget_item.cpp | 125 - rpcs3qt-legacy/flow_widget_item.h | 49 - rpcs3qt-legacy/frame_icon.xpm | 139 - rpcs3qt-legacy/game_compatibility.cpp | 347 -- rpcs3qt-legacy/game_compatibility.h | 177 - rpcs3qt-legacy/game_list.cpp | 194 - rpcs3qt-legacy/game_list.h | 49 - rpcs3qt-legacy/game_list_base.cpp | 216 - rpcs3qt-legacy/game_list_base.h | 49 - rpcs3qt-legacy/game_list_delegate.cpp | 48 - rpcs3qt-legacy/game_list_delegate.h | 11 - rpcs3qt-legacy/game_list_frame.cpp | 3070 ----------- rpcs3qt-legacy/game_list_frame.h | 225 - rpcs3qt-legacy/game_list_grid.cpp | 217 - rpcs3qt-legacy/game_list_grid.h | 37 - rpcs3qt-legacy/game_list_grid_item.cpp | 87 - rpcs3qt-legacy/game_list_grid_item.h | 36 - rpcs3qt-legacy/game_list_table.cpp | 398 -- rpcs3qt-legacy/game_list_table.h | 42 - rpcs3qt-legacy/gl_gs_frame.cpp | 149 - rpcs3qt-legacy/gl_gs_frame.h | 28 - rpcs3qt-legacy/gs_frame.cpp | 1210 ----- rpcs3qt-legacy/gs_frame.h | 125 - rpcs3qt-legacy/gui_application.cpp | 1477 ------ rpcs3qt-legacy/gui_application.h | 154 - rpcs3qt-legacy/gui_game_info.cpp | 14 - rpcs3qt-legacy/gui_game_info.h | 30 - rpcs3qt-legacy/gui_save.h | 25 - rpcs3qt-legacy/gui_settings.cpp | 391 -- rpcs3qt-legacy/gui_settings.h | 368 -- rpcs3qt-legacy/headless_application.cpp | 279 - rpcs3qt-legacy/headless_application.h | 37 - rpcs3qt-legacy/infinity_dialog.cpp | 834 --- rpcs3qt-legacy/infinity_dialog.h | 50 - rpcs3qt-legacy/input_dialog.cpp | 79 - rpcs3qt-legacy/input_dialog.h | 33 - rpcs3qt-legacy/instruction_editor_dialog.cpp | 163 - rpcs3qt-legacy/instruction_editor_dialog.h | 32 - rpcs3qt-legacy/ipc_settings_dialog.cpp | 78 - rpcs3qt-legacy/ipc_settings_dialog.h | 10 - rpcs3qt-legacy/kernel_explorer.cpp | 1018 ---- rpcs3qt-legacy/kernel_explorer.h | 41 - rpcs3qt-legacy/localized.cpp | 108 - rpcs3qt-legacy/localized.h | 132 - rpcs3qt-legacy/localized_emu.cpp | 65 - rpcs3qt-legacy/localized_emu.h | 339 -- rpcs3qt-legacy/log_frame.cpp | 943 ---- rpcs3qt-legacy/log_frame.h | 102 - rpcs3qt-legacy/log_viewer.cpp | 506 -- rpcs3qt-legacy/log_viewer.h | 48 - rpcs3qt-legacy/main.cpp | 1558 ------ rpcs3qt-legacy/main_application.cpp | 380 -- rpcs3qt-legacy/main_application.h | 30 - rpcs3qt-legacy/main_window.cpp | 4269 --------------- rpcs3qt-legacy/main_window.h | 202 - rpcs3qt-legacy/main_window.ui | 1433 ----- rpcs3qt-legacy/memory_string_searcher.cpp | 463 -- rpcs3qt-legacy/memory_viewer_panel.cpp | 1252 ----- rpcs3qt-legacy/memory_viewer_panel.h | 147 - rpcs3qt-legacy/microphone_creator.cpp | 82 - rpcs3qt-legacy/microphone_creator.h | 28 - rpcs3qt-legacy/midi_creator.cpp | 121 - rpcs3qt-legacy/midi_creator.h | 25 - rpcs3qt-legacy/movie_item.cpp | 14 - rpcs3qt-legacy/movie_item.h | 13 - rpcs3qt-legacy/movie_item_base.cpp | 101 - rpcs3qt-legacy/movie_item_base.h | 62 - rpcs3qt-legacy/msg_dialog_frame.cpp | 321 -- rpcs3qt-legacy/msg_dialog_frame.h | 40 - rpcs3qt-legacy/numbered_widget_item.h | 31 - rpcs3qt-legacy/osk_dialog_frame.cpp | 230 - rpcs3qt-legacy/osk_dialog_frame.h | 30 - rpcs3qt-legacy/pad_device_info.h | 13 - rpcs3qt-legacy/pad_led_settings_dialog.cpp | 135 - rpcs3qt-legacy/pad_led_settings_dialog.h | 44 - rpcs3qt-legacy/pad_led_settings_dialog.ui | 149 - rpcs3qt-legacy/pad_motion_settings_dialog.cpp | 284 - rpcs3qt-legacy/pad_motion_settings_dialog.h | 74 - rpcs3qt-legacy/pad_motion_settings_dialog.ui | 297 -- rpcs3qt-legacy/pad_settings_dialog.cpp | 2132 -------- rpcs3qt-legacy/pad_settings_dialog.h | 256 - rpcs3qt-legacy/pad_settings_dialog.ui | 2769 ---------- rpcs3qt-legacy/patch_creator_dialog.cpp | 605 --- rpcs3qt-legacy/patch_creator_dialog.h | 54 - rpcs3qt-legacy/patch_creator_dialog.ui | 204 - rpcs3qt-legacy/patch_manager_dialog.cpp | 1320 ----- rpcs3qt-legacy/patch_manager_dialog.h | 88 - rpcs3qt-legacy/patch_manager_dialog.ui | 359 -- rpcs3qt-legacy/permissions.cpp | 68 - rpcs3qt-legacy/permissions.h | 10 - rpcs3qt-legacy/persistent_settings.cpp | 59 - rpcs3qt-legacy/persistent_settings.h | 51 - rpcs3qt-legacy/pkg_install_dialog.cpp | 209 - rpcs3qt-legacy/pkg_install_dialog.h | 25 - rpcs3qt-legacy/progress_dialog.cpp | 53 - rpcs3qt-legacy/progress_dialog.h | 19 - rpcs3qt-legacy/progress_indicator.cpp | 81 - rpcs3qt-legacy/progress_indicator.h | 23 - rpcs3qt-legacy/ps_move_tracker_dialog.cpp | 611 --- rpcs3qt-legacy/ps_move_tracker_dialog.h | 88 - rpcs3qt-legacy/ps_move_tracker_dialog.ui | 393 -- rpcs3qt-legacy/qt/etc/qt.conf | 4 - rpcs3qt-legacy/qt6.cmake | 44 - rpcs3qt-legacy/qt_camera_handler.cpp | 404 -- rpcs3qt-legacy/qt_camera_handler.h | 44 - rpcs3qt-legacy/qt_camera_video_sink.cpp | 318 -- rpcs3qt-legacy/qt_camera_video_sink.h | 50 - rpcs3qt-legacy/qt_music_handler.cpp | 236 - rpcs3qt-legacy/qt_music_handler.h | 34 - rpcs3qt-legacy/qt_utils.cpp | 663 --- rpcs3qt-legacy/qt_utils.h | 193 - rpcs3qt-legacy/qt_video_source.cpp | 269 - rpcs3qt-legacy/qt_video_source.h | 87 - rpcs3qt-legacy/raw_mouse_settings_dialog.cpp | 583 --- rpcs3qt-legacy/raw_mouse_settings_dialog.h | 65 - rpcs3qt-legacy/recvmessage_dialog_frame.cpp | 163 - rpcs3qt-legacy/recvmessage_dialog_frame.h | 41 - rpcs3qt-legacy/register_editor_dialog.cpp | 504 -- rpcs3qt-legacy/register_editor_dialog.h | 28 - rpcs3qt-legacy/render_creator.cpp | 134 - rpcs3qt-legacy/render_creator.h | 41 - rpcs3qt-legacy/resource.h | 13 - rpcs3qt-legacy/resources.qrc | 21 - rpcs3qt-legacy/richtext_item_delegate.h | 74 - rpcs3qt-legacy/rpcn_settings_dialog.cpp | 1371 ----- rpcs3qt-legacy/rpcn_settings_dialog.h | 144 - rpcs3qt-legacy/rpcs3.desktop | 12 - rpcs3qt-legacy/rpcs3.icns | Bin 27960 -> 0 bytes rpcs3qt-legacy/rpcs3.ico | Bin 65265 -> 0 bytes rpcs3qt-legacy/rpcs3.metainfo.xml | 52 - rpcs3qt-legacy/rpcs3.plist.in | 42 - rpcs3qt-legacy/rpcs3.png | Bin 1596 -> 0 bytes rpcs3qt-legacy/rpcs3.rc | 3 - rpcs3qt-legacy/rpcs3.svg | 1 - rpcs3qt-legacy/rsx_debugger.cpp | 1199 ----- rpcs3qt-legacy/rsx_debugger.h | 90 - rpcs3qt-legacy/save_data_dialog.cpp | 71 - rpcs3qt-legacy/save_data_dialog.h | 10 - rpcs3qt-legacy/save_data_info_dialog.cpp | 80 - rpcs3qt-legacy/save_data_info_dialog.h | 22 - rpcs3qt-legacy/save_data_list_dialog.cpp | 249 - rpcs3qt-legacy/save_data_list_dialog.h | 50 - rpcs3qt-legacy/save_manager_dialog.cpp | 747 --- rpcs3qt-legacy/save_manager_dialog.h | 73 - rpcs3qt-legacy/savestate_manager_dialog.cpp | 817 --- rpcs3qt-legacy/savestate_manager_dialog.h | 85 - rpcs3qt-legacy/screenshot_item.cpp | 32 - rpcs3qt-legacy/screenshot_item.h | 24 - rpcs3qt-legacy/screenshot_manager_dialog.cpp | 139 - rpcs3qt-legacy/screenshot_manager_dialog.h | 46 - rpcs3qt-legacy/screenshot_preview.cpp | 78 - rpcs3qt-legacy/screenshot_preview.h | 26 - rpcs3qt-legacy/sendmessage_dialog_frame.cpp | 198 - rpcs3qt-legacy/sendmessage_dialog_frame.h | 36 - rpcs3qt-legacy/settings.cpp | 111 - rpcs3qt-legacy/settings.h | 56 - rpcs3qt-legacy/settings_dialog.cpp | 2728 ---------- rpcs3qt-legacy/settings_dialog.h | 75 - rpcs3qt-legacy/settings_dialog.ui | 4608 ----------------- rpcs3qt-legacy/shortcut_dialog.cpp | 104 - rpcs3qt-legacy/shortcut_dialog.h | 32 - rpcs3qt-legacy/shortcut_dialog.ui | 82 - rpcs3qt-legacy/shortcut_handler.cpp | 76 - rpcs3qt-legacy/shortcut_handler.h | 37 - rpcs3qt-legacy/shortcut_settings.cpp | 121 - rpcs3qt-legacy/shortcut_settings.h | 71 - rpcs3qt-legacy/shortcut_utils.cpp | 378 -- rpcs3qt-legacy/shortcut_utils.h | 21 - rpcs3qt-legacy/skylander_dialog.cpp | 841 --- rpcs3qt-legacy/skylander_dialog.h | 49 - rpcs3qt-legacy/stylesheets.h | 110 - rpcs3qt-legacy/syntax_highlighter.cpp | 185 - rpcs3qt-legacy/syntax_highlighter.h | 54 - rpcs3qt-legacy/system_cmd_dialog.cpp | 175 - rpcs3qt-legacy/system_cmd_dialog.h | 26 - rpcs3qt-legacy/table_item_delegate.cpp | 36 - rpcs3qt-legacy/table_item_delegate.h | 18 - rpcs3qt-legacy/tooltips.cpp | 5 - rpcs3qt-legacy/tooltips.h | 308 -- rpcs3qt-legacy/trophy_manager_dialog.cpp | 1399 ----- rpcs3qt-legacy/trophy_manager_dialog.h | 117 - rpcs3qt-legacy/trophy_notification_frame.cpp | 66 - rpcs3qt-legacy/trophy_notification_frame.h | 13 - rpcs3qt-legacy/trophy_notification_helper.cpp | 38 - rpcs3qt-legacy/trophy_notification_helper.h | 17 - rpcs3qt-legacy/update_manager.cpp | 795 --- rpcs3qt-legacy/update_manager.h | 48 - rpcs3qt-legacy/user_account.cpp | 66 - rpcs3qt-legacy/user_account.h | 36 - rpcs3qt-legacy/user_manager_dialog.cpp | 522 -- rpcs3qt-legacy/user_manager_dialog.h | 57 - rpcs3qt-legacy/uuid.cpp | 127 - rpcs3qt-legacy/uuid.h | 17 - rpcs3qt-legacy/vfs_dialog.cpp | 108 - rpcs3qt-legacy/vfs_dialog.h | 16 - rpcs3qt-legacy/vfs_dialog_path_widget.cpp | 107 - rpcs3qt-legacy/vfs_dialog_path_widget.h | 40 - rpcs3qt-legacy/vfs_dialog_tab.cpp | 13 - rpcs3qt-legacy/vfs_dialog_tab.h | 25 - rpcs3qt-legacy/vfs_dialog_usb_input.cpp | 96 - rpcs3qt-legacy/vfs_dialog_usb_input.h | 29 - rpcs3qt-legacy/vfs_dialog_usb_tab.cpp | 184 - rpcs3qt-legacy/vfs_dialog_usb_tab.h | 38 - rpcs3qt-legacy/vfs_tool_dialog.cpp | 49 - rpcs3qt-legacy/vfs_tool_dialog.h | 23 - rpcs3qt-legacy/vfs_tool_dialog.ui | 89 - rpcs3qt-legacy/video_label.cpp | 51 - rpcs3qt-legacy/video_label.h | 22 - rpcs3qt-legacy/welcome_dialog.cpp | 107 - rpcs3qt-legacy/welcome_dialog.h | 23 - rpcs3qt-legacy/welcome_dialog.ui | 313 -- rpcs3qt-legacy/windows.qrc | 5 - 319 files changed, 10 insertions(+), 76903 deletions(-) delete mode 100755 .ci/build-freebsd.sh delete mode 100755 .ci/build-linux-aarch64.sh delete mode 100755 .ci/build-linux.sh delete mode 100644 .ci/build-mac-arm64.sh delete mode 100644 .ci/build-mac.sh delete mode 100755 .ci/deploy-linux.sh delete mode 100644 .ci/deploy-mac-arm64.sh delete mode 100644 .ci/deploy-mac.sh delete mode 100755 .ci/deploy-windows.sh delete mode 100644 .ci/docker.env delete mode 100755 .ci/export-azure-vars.sh delete mode 100644 .ci/export-cirrus-vars.sh delete mode 100755 .ci/generate-qt-ts.sh delete mode 100644 .ci/get_keys-windows.sh delete mode 100755 .ci/github-upload.sh delete mode 100755 .ci/install-freebsd.sh delete mode 100644 .ci/optimize-mac.sh delete mode 100755 .ci/setup-windows.sh delete mode 100644 .github/workflows/rpcs3.yml delete mode 100644 rpcs3qt-legacy/.clang-format delete mode 100644 rpcs3qt-legacy/CMakeLists.txt delete mode 100644 rpcs3qt-legacy/Icons/DualShock_3.svg delete mode 100644 rpcs3qt-legacy/Icons/combo_config_bordered.png delete mode 100644 rpcs3qt-legacy/Icons/configure.png delete mode 100644 rpcs3qt-legacy/Icons/controllers.png delete mode 100644 rpcs3qt-legacy/Icons/custom_config.png delete mode 100644 rpcs3qt-legacy/Icons/exit_fullscreen.png delete mode 100644 rpcs3qt-legacy/Icons/fullscreen.png delete mode 100644 rpcs3qt-legacy/Icons/grid.png delete mode 100644 rpcs3qt-legacy/Icons/list.png delete mode 100644 rpcs3qt-legacy/Icons/open.png delete mode 100644 rpcs3qt-legacy/Icons/pause.png delete mode 100644 rpcs3qt-legacy/Icons/play.png delete mode 100644 rpcs3qt-legacy/Icons/refresh.png delete mode 100644 rpcs3qt-legacy/Icons/restart.png delete mode 100644 rpcs3qt-legacy/Icons/stop.png delete mode 100644 rpcs3qt-legacy/Input/basic_keyboard_handler.cpp delete mode 100644 rpcs3qt-legacy/Input/basic_keyboard_handler.h delete mode 100644 rpcs3qt-legacy/Input/basic_mouse_handler.cpp delete mode 100644 rpcs3qt-legacy/Input/basic_mouse_handler.h delete mode 100644 rpcs3qt-legacy/Input/gui_pad_thread.cpp delete mode 100644 rpcs3qt-legacy/Input/gui_pad_thread.h delete mode 100644 rpcs3qt-legacy/Input/keyboard_pad_handler.cpp delete mode 100644 rpcs3qt-legacy/Input/keyboard_pad_handler.h delete mode 100644 rpcs3qt-legacy/_discord_utils.cpp delete mode 100644 rpcs3qt-legacy/_discord_utils.h delete mode 100644 rpcs3qt-legacy/about_dialog.cpp delete mode 100644 rpcs3qt-legacy/about_dialog.h delete mode 100644 rpcs3qt-legacy/about_dialog.ui delete mode 100644 rpcs3qt-legacy/auto_pause_settings_dialog.cpp delete mode 100644 rpcs3qt-legacy/auto_pause_settings_dialog.h delete mode 100644 rpcs3qt-legacy/basic_mouse_settings_dialog.cpp delete mode 100644 rpcs3qt-legacy/basic_mouse_settings_dialog.h delete mode 100644 rpcs3qt-legacy/breakpoint_handler.cpp delete mode 100644 rpcs3qt-legacy/breakpoint_handler.h delete mode 100644 rpcs3qt-legacy/breakpoint_list.cpp delete mode 100644 rpcs3qt-legacy/breakpoint_list.h delete mode 100644 rpcs3qt-legacy/call_stack_list.cpp delete mode 100644 rpcs3qt-legacy/call_stack_list.h delete mode 100644 rpcs3qt-legacy/camera_settings_dialog.cpp delete mode 100644 rpcs3qt-legacy/camera_settings_dialog.h delete mode 100644 rpcs3qt-legacy/camera_settings_dialog.ui delete mode 100644 rpcs3qt-legacy/category.h delete mode 100644 rpcs3qt-legacy/cg_disasm_window.cpp delete mode 100644 rpcs3qt-legacy/cg_disasm_window.h delete mode 100644 rpcs3qt-legacy/cheat_manager.cpp delete mode 100644 rpcs3qt-legacy/cheat_manager.h delete mode 100644 rpcs3qt-legacy/config_adapter.cpp delete mode 100644 rpcs3qt-legacy/config_adapter.h delete mode 100644 rpcs3qt-legacy/config_checker.cpp delete mode 100644 rpcs3qt-legacy/config_checker.h delete mode 100644 rpcs3qt-legacy/curl_handle.cpp delete mode 100644 rpcs3qt-legacy/curl_handle.h delete mode 100644 rpcs3qt-legacy/custom_dialog.cpp delete mode 100644 rpcs3qt-legacy/custom_dialog.h delete mode 100644 rpcs3qt-legacy/custom_dock_widget.h delete mode 100644 rpcs3qt-legacy/custom_table_widget_item.cpp delete mode 100644 rpcs3qt-legacy/custom_table_widget_item.h delete mode 100644 rpcs3qt-legacy/debugger_add_bp_window.cpp delete mode 100644 rpcs3qt-legacy/debugger_add_bp_window.h delete mode 100644 rpcs3qt-legacy/debugger_frame.cpp delete mode 100644 rpcs3qt-legacy/debugger_frame.h delete mode 100644 rpcs3qt-legacy/debugger_list.cpp delete mode 100644 rpcs3qt-legacy/debugger_list.h delete mode 100644 rpcs3qt-legacy/dimensions_dialog.cpp delete mode 100644 rpcs3qt-legacy/dimensions_dialog.h delete mode 100644 rpcs3qt-legacy/display_sleep_control.cpp delete mode 100644 rpcs3qt-legacy/display_sleep_control.h delete mode 100644 rpcs3qt-legacy/downloader.cpp delete mode 100644 rpcs3qt-legacy/downloader.h delete mode 100644 rpcs3qt-legacy/elf_memory_dumping_dialog.cpp delete mode 100644 rpcs3qt-legacy/elf_memory_dumping_dialog.h delete mode 100644 rpcs3qt-legacy/emu_settings.cpp delete mode 100644 rpcs3qt-legacy/emu_settings.h delete mode 100644 rpcs3qt-legacy/emu_settings_type.h delete mode 100644 rpcs3qt-legacy/emulated_pad_settings_dialog.cpp delete mode 100644 rpcs3qt-legacy/emulated_pad_settings_dialog.h delete mode 100644 rpcs3qt-legacy/fatal_error_dialog.cpp delete mode 100644 rpcs3qt-legacy/fatal_error_dialog.h delete mode 100644 rpcs3qt-legacy/find_dialog.cpp delete mode 100644 rpcs3qt-legacy/find_dialog.h delete mode 100644 rpcs3qt-legacy/flow_layout.cpp delete mode 100644 rpcs3qt-legacy/flow_layout.h delete mode 100644 rpcs3qt-legacy/flow_widget.cpp delete mode 100644 rpcs3qt-legacy/flow_widget.h delete mode 100644 rpcs3qt-legacy/flow_widget_item.cpp delete mode 100644 rpcs3qt-legacy/flow_widget_item.h delete mode 100644 rpcs3qt-legacy/frame_icon.xpm delete mode 100644 rpcs3qt-legacy/game_compatibility.cpp delete mode 100644 rpcs3qt-legacy/game_compatibility.h delete mode 100644 rpcs3qt-legacy/game_list.cpp delete mode 100644 rpcs3qt-legacy/game_list.h delete mode 100644 rpcs3qt-legacy/game_list_base.cpp delete mode 100644 rpcs3qt-legacy/game_list_base.h delete mode 100644 rpcs3qt-legacy/game_list_delegate.cpp delete mode 100644 rpcs3qt-legacy/game_list_delegate.h delete mode 100644 rpcs3qt-legacy/game_list_frame.cpp delete mode 100644 rpcs3qt-legacy/game_list_frame.h delete mode 100644 rpcs3qt-legacy/game_list_grid.cpp delete mode 100644 rpcs3qt-legacy/game_list_grid.h delete mode 100644 rpcs3qt-legacy/game_list_grid_item.cpp delete mode 100644 rpcs3qt-legacy/game_list_grid_item.h delete mode 100644 rpcs3qt-legacy/game_list_table.cpp delete mode 100644 rpcs3qt-legacy/game_list_table.h delete mode 100644 rpcs3qt-legacy/gl_gs_frame.cpp delete mode 100644 rpcs3qt-legacy/gl_gs_frame.h delete mode 100644 rpcs3qt-legacy/gs_frame.cpp delete mode 100644 rpcs3qt-legacy/gs_frame.h delete mode 100644 rpcs3qt-legacy/gui_application.cpp delete mode 100644 rpcs3qt-legacy/gui_application.h delete mode 100644 rpcs3qt-legacy/gui_game_info.cpp delete mode 100644 rpcs3qt-legacy/gui_game_info.h delete mode 100644 rpcs3qt-legacy/gui_save.h delete mode 100644 rpcs3qt-legacy/gui_settings.cpp delete mode 100644 rpcs3qt-legacy/gui_settings.h delete mode 100644 rpcs3qt-legacy/headless_application.cpp delete mode 100644 rpcs3qt-legacy/headless_application.h delete mode 100644 rpcs3qt-legacy/infinity_dialog.cpp delete mode 100644 rpcs3qt-legacy/infinity_dialog.h delete mode 100644 rpcs3qt-legacy/input_dialog.cpp delete mode 100644 rpcs3qt-legacy/input_dialog.h delete mode 100644 rpcs3qt-legacy/instruction_editor_dialog.cpp delete mode 100644 rpcs3qt-legacy/instruction_editor_dialog.h delete mode 100644 rpcs3qt-legacy/ipc_settings_dialog.cpp delete mode 100644 rpcs3qt-legacy/ipc_settings_dialog.h delete mode 100644 rpcs3qt-legacy/kernel_explorer.cpp delete mode 100644 rpcs3qt-legacy/kernel_explorer.h delete mode 100644 rpcs3qt-legacy/localized.cpp delete mode 100644 rpcs3qt-legacy/localized.h delete mode 100644 rpcs3qt-legacy/localized_emu.cpp delete mode 100644 rpcs3qt-legacy/localized_emu.h delete mode 100644 rpcs3qt-legacy/log_frame.cpp delete mode 100644 rpcs3qt-legacy/log_frame.h delete mode 100644 rpcs3qt-legacy/log_viewer.cpp delete mode 100644 rpcs3qt-legacy/log_viewer.h delete mode 100644 rpcs3qt-legacy/main.cpp delete mode 100644 rpcs3qt-legacy/main_application.cpp delete mode 100644 rpcs3qt-legacy/main_application.h delete mode 100644 rpcs3qt-legacy/main_window.cpp delete mode 100644 rpcs3qt-legacy/main_window.h delete mode 100644 rpcs3qt-legacy/main_window.ui delete mode 100644 rpcs3qt-legacy/memory_string_searcher.cpp delete mode 100644 rpcs3qt-legacy/memory_viewer_panel.cpp delete mode 100644 rpcs3qt-legacy/memory_viewer_panel.h delete mode 100644 rpcs3qt-legacy/microphone_creator.cpp delete mode 100644 rpcs3qt-legacy/microphone_creator.h delete mode 100644 rpcs3qt-legacy/midi_creator.cpp delete mode 100644 rpcs3qt-legacy/midi_creator.h delete mode 100644 rpcs3qt-legacy/movie_item.cpp delete mode 100644 rpcs3qt-legacy/movie_item.h delete mode 100644 rpcs3qt-legacy/movie_item_base.cpp delete mode 100644 rpcs3qt-legacy/movie_item_base.h delete mode 100644 rpcs3qt-legacy/msg_dialog_frame.cpp delete mode 100644 rpcs3qt-legacy/msg_dialog_frame.h delete mode 100644 rpcs3qt-legacy/numbered_widget_item.h delete mode 100644 rpcs3qt-legacy/osk_dialog_frame.cpp delete mode 100644 rpcs3qt-legacy/osk_dialog_frame.h delete mode 100644 rpcs3qt-legacy/pad_device_info.h delete mode 100644 rpcs3qt-legacy/pad_led_settings_dialog.cpp delete mode 100644 rpcs3qt-legacy/pad_led_settings_dialog.h delete mode 100644 rpcs3qt-legacy/pad_led_settings_dialog.ui delete mode 100644 rpcs3qt-legacy/pad_motion_settings_dialog.cpp delete mode 100644 rpcs3qt-legacy/pad_motion_settings_dialog.h delete mode 100644 rpcs3qt-legacy/pad_motion_settings_dialog.ui delete mode 100644 rpcs3qt-legacy/pad_settings_dialog.cpp delete mode 100644 rpcs3qt-legacy/pad_settings_dialog.h delete mode 100644 rpcs3qt-legacy/pad_settings_dialog.ui delete mode 100644 rpcs3qt-legacy/patch_creator_dialog.cpp delete mode 100644 rpcs3qt-legacy/patch_creator_dialog.h delete mode 100644 rpcs3qt-legacy/patch_creator_dialog.ui delete mode 100644 rpcs3qt-legacy/patch_manager_dialog.cpp delete mode 100644 rpcs3qt-legacy/patch_manager_dialog.h delete mode 100644 rpcs3qt-legacy/patch_manager_dialog.ui delete mode 100644 rpcs3qt-legacy/permissions.cpp delete mode 100644 rpcs3qt-legacy/permissions.h delete mode 100644 rpcs3qt-legacy/persistent_settings.cpp delete mode 100644 rpcs3qt-legacy/persistent_settings.h delete mode 100644 rpcs3qt-legacy/pkg_install_dialog.cpp delete mode 100644 rpcs3qt-legacy/pkg_install_dialog.h delete mode 100644 rpcs3qt-legacy/progress_dialog.cpp delete mode 100644 rpcs3qt-legacy/progress_dialog.h delete mode 100644 rpcs3qt-legacy/progress_indicator.cpp delete mode 100644 rpcs3qt-legacy/progress_indicator.h delete mode 100644 rpcs3qt-legacy/ps_move_tracker_dialog.cpp delete mode 100644 rpcs3qt-legacy/ps_move_tracker_dialog.h delete mode 100644 rpcs3qt-legacy/ps_move_tracker_dialog.ui delete mode 100644 rpcs3qt-legacy/qt/etc/qt.conf delete mode 100644 rpcs3qt-legacy/qt6.cmake delete mode 100644 rpcs3qt-legacy/qt_camera_handler.cpp delete mode 100644 rpcs3qt-legacy/qt_camera_handler.h delete mode 100644 rpcs3qt-legacy/qt_camera_video_sink.cpp delete mode 100644 rpcs3qt-legacy/qt_camera_video_sink.h delete mode 100644 rpcs3qt-legacy/qt_music_handler.cpp delete mode 100644 rpcs3qt-legacy/qt_music_handler.h delete mode 100644 rpcs3qt-legacy/qt_utils.cpp delete mode 100644 rpcs3qt-legacy/qt_utils.h delete mode 100644 rpcs3qt-legacy/qt_video_source.cpp delete mode 100644 rpcs3qt-legacy/qt_video_source.h delete mode 100644 rpcs3qt-legacy/raw_mouse_settings_dialog.cpp delete mode 100644 rpcs3qt-legacy/raw_mouse_settings_dialog.h delete mode 100644 rpcs3qt-legacy/recvmessage_dialog_frame.cpp delete mode 100644 rpcs3qt-legacy/recvmessage_dialog_frame.h delete mode 100644 rpcs3qt-legacy/register_editor_dialog.cpp delete mode 100644 rpcs3qt-legacy/register_editor_dialog.h delete mode 100644 rpcs3qt-legacy/render_creator.cpp delete mode 100644 rpcs3qt-legacy/render_creator.h delete mode 100644 rpcs3qt-legacy/resource.h delete mode 100644 rpcs3qt-legacy/resources.qrc delete mode 100644 rpcs3qt-legacy/richtext_item_delegate.h delete mode 100644 rpcs3qt-legacy/rpcn_settings_dialog.cpp delete mode 100644 rpcs3qt-legacy/rpcn_settings_dialog.h delete mode 100644 rpcs3qt-legacy/rpcs3.desktop delete mode 100644 rpcs3qt-legacy/rpcs3.icns delete mode 100644 rpcs3qt-legacy/rpcs3.ico delete mode 100644 rpcs3qt-legacy/rpcs3.metainfo.xml delete mode 100644 rpcs3qt-legacy/rpcs3.plist.in delete mode 100644 rpcs3qt-legacy/rpcs3.png delete mode 100644 rpcs3qt-legacy/rpcs3.rc delete mode 100644 rpcs3qt-legacy/rpcs3.svg delete mode 100644 rpcs3qt-legacy/rsx_debugger.cpp delete mode 100644 rpcs3qt-legacy/rsx_debugger.h delete mode 100644 rpcs3qt-legacy/save_data_dialog.cpp delete mode 100644 rpcs3qt-legacy/save_data_dialog.h delete mode 100644 rpcs3qt-legacy/save_data_info_dialog.cpp delete mode 100644 rpcs3qt-legacy/save_data_info_dialog.h delete mode 100644 rpcs3qt-legacy/save_data_list_dialog.cpp delete mode 100644 rpcs3qt-legacy/save_data_list_dialog.h delete mode 100644 rpcs3qt-legacy/save_manager_dialog.cpp delete mode 100644 rpcs3qt-legacy/save_manager_dialog.h delete mode 100644 rpcs3qt-legacy/savestate_manager_dialog.cpp delete mode 100644 rpcs3qt-legacy/savestate_manager_dialog.h delete mode 100644 rpcs3qt-legacy/screenshot_item.cpp delete mode 100644 rpcs3qt-legacy/screenshot_item.h delete mode 100644 rpcs3qt-legacy/screenshot_manager_dialog.cpp delete mode 100644 rpcs3qt-legacy/screenshot_manager_dialog.h delete mode 100644 rpcs3qt-legacy/screenshot_preview.cpp delete mode 100644 rpcs3qt-legacy/screenshot_preview.h delete mode 100644 rpcs3qt-legacy/sendmessage_dialog_frame.cpp delete mode 100644 rpcs3qt-legacy/sendmessage_dialog_frame.h delete mode 100644 rpcs3qt-legacy/settings.cpp delete mode 100644 rpcs3qt-legacy/settings.h delete mode 100644 rpcs3qt-legacy/settings_dialog.cpp delete mode 100644 rpcs3qt-legacy/settings_dialog.h delete mode 100644 rpcs3qt-legacy/settings_dialog.ui delete mode 100644 rpcs3qt-legacy/shortcut_dialog.cpp delete mode 100644 rpcs3qt-legacy/shortcut_dialog.h delete mode 100644 rpcs3qt-legacy/shortcut_dialog.ui delete mode 100644 rpcs3qt-legacy/shortcut_handler.cpp delete mode 100644 rpcs3qt-legacy/shortcut_handler.h delete mode 100644 rpcs3qt-legacy/shortcut_settings.cpp delete mode 100644 rpcs3qt-legacy/shortcut_settings.h delete mode 100644 rpcs3qt-legacy/shortcut_utils.cpp delete mode 100644 rpcs3qt-legacy/shortcut_utils.h delete mode 100644 rpcs3qt-legacy/skylander_dialog.cpp delete mode 100644 rpcs3qt-legacy/skylander_dialog.h delete mode 100644 rpcs3qt-legacy/stylesheets.h delete mode 100644 rpcs3qt-legacy/syntax_highlighter.cpp delete mode 100644 rpcs3qt-legacy/syntax_highlighter.h delete mode 100644 rpcs3qt-legacy/system_cmd_dialog.cpp delete mode 100644 rpcs3qt-legacy/system_cmd_dialog.h delete mode 100644 rpcs3qt-legacy/table_item_delegate.cpp delete mode 100644 rpcs3qt-legacy/table_item_delegate.h delete mode 100644 rpcs3qt-legacy/tooltips.cpp delete mode 100644 rpcs3qt-legacy/tooltips.h delete mode 100644 rpcs3qt-legacy/trophy_manager_dialog.cpp delete mode 100644 rpcs3qt-legacy/trophy_manager_dialog.h delete mode 100644 rpcs3qt-legacy/trophy_notification_frame.cpp delete mode 100644 rpcs3qt-legacy/trophy_notification_frame.h delete mode 100644 rpcs3qt-legacy/trophy_notification_helper.cpp delete mode 100644 rpcs3qt-legacy/trophy_notification_helper.h delete mode 100644 rpcs3qt-legacy/update_manager.cpp delete mode 100644 rpcs3qt-legacy/update_manager.h delete mode 100644 rpcs3qt-legacy/user_account.cpp delete mode 100644 rpcs3qt-legacy/user_account.h delete mode 100644 rpcs3qt-legacy/user_manager_dialog.cpp delete mode 100644 rpcs3qt-legacy/user_manager_dialog.h delete mode 100644 rpcs3qt-legacy/uuid.cpp delete mode 100644 rpcs3qt-legacy/uuid.h delete mode 100644 rpcs3qt-legacy/vfs_dialog.cpp delete mode 100644 rpcs3qt-legacy/vfs_dialog.h delete mode 100644 rpcs3qt-legacy/vfs_dialog_path_widget.cpp delete mode 100644 rpcs3qt-legacy/vfs_dialog_path_widget.h delete mode 100644 rpcs3qt-legacy/vfs_dialog_tab.cpp delete mode 100644 rpcs3qt-legacy/vfs_dialog_tab.h delete mode 100644 rpcs3qt-legacy/vfs_dialog_usb_input.cpp delete mode 100644 rpcs3qt-legacy/vfs_dialog_usb_input.h delete mode 100644 rpcs3qt-legacy/vfs_dialog_usb_tab.cpp delete mode 100644 rpcs3qt-legacy/vfs_dialog_usb_tab.h delete mode 100644 rpcs3qt-legacy/vfs_tool_dialog.cpp delete mode 100644 rpcs3qt-legacy/vfs_tool_dialog.h delete mode 100644 rpcs3qt-legacy/vfs_tool_dialog.ui delete mode 100644 rpcs3qt-legacy/video_label.cpp delete mode 100644 rpcs3qt-legacy/video_label.h delete mode 100644 rpcs3qt-legacy/welcome_dialog.cpp delete mode 100644 rpcs3qt-legacy/welcome_dialog.h delete mode 100644 rpcs3qt-legacy/welcome_dialog.ui delete mode 100644 rpcs3qt-legacy/windows.qrc diff --git a/.ci/build-freebsd.sh b/.ci/build-freebsd.sh deleted file mode 100755 index 8b8eef7f5..000000000 --- a/.ci/build-freebsd.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -ex - -# Pull all the submodules except llvm and opencv -# Note: Tried to use git submodule status, but it takes over 20 seconds -# shellcheck disable=SC2046 -git submodule -q update --init --depth 1 $(awk '/path/ && !/llvm/ && !/opencv/ { print $3 }' .gitmodules) - -CONFIGURE_ARGS=" - -DWITH_LLVM=ON - -DUSE_SDL=OFF - -DUSE_PRECOMPILED_HEADERS=OFF - -DUSE_NATIVE_INSTRUCTIONS=OFF - -DUSE_SYSTEM_FFMPEG=ON - -DUSE_SYSTEM_CURL=ON - -DUSE_SYSTEM_LIBPNG=ON - -DUSE_SYSTEM_OPENCV=ON -" - -# base Clang workaround (missing clang-scan-deps) -CONFIGURE_ARGS="$CONFIGURE_ARGS -DCMAKE_CXX_SCAN_FOR_MODULES=OFF" - -# shellcheck disable=SC2086 -cmake -B build -G Ninja $CONFIGURE_ARGS -cmake --build build - -ccache --show-stats -ccache --zero-stats diff --git a/.ci/build-linux-aarch64.sh b/.ci/build-linux-aarch64.sh deleted file mode 100755 index 0ee670ea3..000000000 --- a/.ci/build-linux-aarch64.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/sh -ex - -git config --global --add safe.directory '*' - -# Pull all the submodules except llvm and opencv -# shellcheck disable=SC2046 -git submodule -q update --init $(awk '/path/ && !/llvm/ && !/opencv/ { print $3 }' .gitmodules) - -if [ "$COMPILER" = "gcc" ]; then - export CC=gcc-14 - export CXX=g++-14 -else - export CC=clang - export CXX=clang++ - export CFLAGS="$CFLAGS -fuse-ld=lld" -fi - -cmake -B build \ - -DCMAKE_INSTALL_PREFIX=/usr \ - -DCMAKE_C_FLAGS="$CFLAGS" \ - -DCMAKE_CXX_FLAGS="$CFLAGS" \ - -DUSE_NATIVE_INSTRUCTIONS=OFF \ - -DUSE_PRECOMPILED_HEADERS=OFF \ - -DUSE_SYSTEM_CURL=ON \ - -DUSE_SDL=OFF \ - -DUSE_SYSTEM_FFMPEG=OFF \ - -DUSE_SYSTEM_CURL=OFF \ - -DUSE_SYSTEM_OPENAL=OFF \ - -DUSE_SYSTEM_FFMPEG=OFF \ - -DUSE_DISCORD_RPC=ON \ - -DOpenGL_GL_PREFERENCE=LEGACY \ - -DSTATIC_LINK_LLVM=ON \ - -DBUILD_LLVM=on \ - -DWITH_RPCSX=off \ - -DWITH_RPCS3=on \ - -DWITH_RPCS3_QT_UI=on \ - -G Ninja - -cmake --build build; build_status=$?; - -shellcheck .ci/*.sh - -# If it compiled succesfully let's deploy. -# Azure and Cirrus publish PRs as artifacts only. -{ [ "$CI_HAS_ARTIFACTS" = "true" ]; -} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false" - -if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then - .ci/deploy-linux.sh "aarch64" -fi diff --git a/.ci/build-linux.sh b/.ci/build-linux.sh deleted file mode 100755 index 54b5adc83..000000000 --- a/.ci/build-linux.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/sh -ex - -git config --global --add safe.directory '*' - -# Pull all the submodules except llvm and opencv -# Note: Tried to use git submodule status, but it takes over 20 seconds -# shellcheck disable=SC2046 -git submodule -q update --init $(awk '/path/ && !/llvm/ && !/opencv/ { print $3 }' .gitmodules) - -if [ "$COMPILER" = "gcc" ]; then - # These are set in the dockerfile - export CC=gcc-14 - export CXX=g++-14 -else - export CC=clang - export CXX=clang++ - export LD=clang - export CFLAGS="$CFLAGS -fuse-ld=lld" -fi - -cmake -B build \ - -DCMAKE_INSTALL_PREFIX=/usr \ - -DCMAKE_C_FLAGS="$CFLAGS" \ - -DCMAKE_CXX_FLAGS="$CFLAGS" \ - -DUSE_NATIVE_INSTRUCTIONS=OFF \ - -DUSE_PRECOMPILED_HEADERS=OFF \ - -DUSE_SDL=OFF \ - -DUSE_SYSTEM_CURL=OFF \ - -DUSE_SYSTEM_OPENAL=OFF \ - -DUSE_SYSTEM_FFMPEG=OFF \ - -DUSE_DISCORD_RPC=ON \ - -DOpenGL_GL_PREFERENCE=LEGACY \ - -DSTATIC_LINK_LLVM=ON \ - -DBUILD_LLVM=on \ - -DWITH_RPCSX=off \ - -DWITH_RPCS3=on \ - -DWITH_RPCS3_QT_UI=on \ - -G Ninja - -cmake --build build; build_status=$?; - -shellcheck .ci/*.sh - -# If it compiled succesfully let's deploy. -# Azure and Cirrus publish PRs as artifacts only. -{ [ "$CI_HAS_ARTIFACTS" = "true" ]; -} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false" - -if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then - .ci/deploy-linux.sh "x86_64" -fi diff --git a/.ci/build-mac-arm64.sh b/.ci/build-mac-arm64.sh deleted file mode 100644 index a9698a888..000000000 --- a/.ci/build-mac-arm64.sh +++ /dev/null @@ -1,160 +0,0 @@ -#!/bin/sh -ex - -# shellcheck disable=SC2086 -brew_arm64_install_packages() { - for pkg in "$@"; do - echo "Fetching bottle for $pkg (arm64)..." - bottle_path="$("$BREW_ARM64_PATH/bin/brew" --cache --bottle-tag=arm64_sonoma "$pkg")" - if [ ! -f "$bottle_path" ]; then - if ! "$BREW_ARM64_PATH/bin/brew" fetch --force --verbose --debug --bottle-tag=arm64_sonoma "$pkg"; then - echo "Failed to fetch bottle for $pkg" - return 1 - fi - bottle_path="$("$BREW_ARM64_PATH/bin/brew" --cache --bottle-tag=arm64_sonoma "$pkg")" - fi - - echo "Installing $pkg (arm64)..." - "$BREW_ARM64_PATH/bin/brew" install --force --force-bottle --ignore-dependencies "$bottle_path" || true - done -} - -export HOMEBREW_NO_AUTO_UPDATE=1 -export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 -export HOMEBREW_NO_INSTALL_CLEANUP=1 - -/usr/local/bin/brew update -sudo rm -rf /usr/local/Cellar/curl /usr/local/opt/curl -/usr/local/bin/brew install -f --overwrite curl -/usr/local/bin/brew uninstall -f --ignore-dependencies ffmpeg -/usr/local/bin/brew install -f --build-from-source ffmpeg@5 || true -/usr/local/bin/brew install -f --overwrite python || true -/usr/local/bin/brew link --overwrite python || true -/usr/local/bin/brew install -f --overwrite nasm ninja p7zip ccache pipenv #create-dmg -/usr/local/bin/brew link -f curl || true -/usr/local/bin/brew install llvm@$LLVM_COMPILER_VER glew cmake sdl3 vulkan-headers coreutils -/usr/local/bin/brew link -f llvm@$LLVM_COMPILER_VER ffmpeg@5 || true - -export BREW_ARM64_PATH="/opt/homebrew1" -sudo mkdir -p "$BREW_ARM64_PATH" -sudo chmod 777 "$BREW_ARM64_PATH" -curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 -C "$BREW_ARM64_PATH" - -#"$BREW_ARM64_PATH/bin/brew" update -# libvorbis requires Homebrew-installed curl, but we can't run it on x64, and we also need the aarch64 libs, so we swap the binary -brew_arm64_install_packages curl -mv /opt/homebrew1/opt/curl/bin/curl /opt/homebrew1/opt/curl/bin/curl.bak -ln -s /usr/local/opt/curl/bin/curl /opt/homebrew1/opt/curl/bin/curl - -brew_arm64_install_packages 0mq aom aribb24 ca-certificates cjson dav1d ffmpeg@5 fontconfig freetype freetype2 gettext glew gmp gnutls lame libbluray libidn2 libnettle libogg libpng librist libsodium libsoxr libtasn libtasn1 libunistring libvmaf libvorbis libvpx libx11 libxau libxcb libxdmcp llvm@$LLVM_COMPILER_VER mbedtls molten-vk nettle opencore-amr openjpeg openssl opus p11-kit pkg-config pkgconfig pzstd rav1e sdl3 snappy speex srt svt-av1 theora vulkan-headers webp x264 x265 xz z3 zeromq zmq zstd -"$BREW_ARM64_PATH/bin/brew" link -f ffmpeg@5 -ln -s "/opt/homebrew1/opt/llvm@$LLVM_COMPILER_VER/lib/unwind/libunwind.1.dylib" "/opt/homebrew1/opt/llvm@$LLVM_COMPILER_VER/lib/libunwind.1.dylib" - -# moltenvk based on commit for 1.2.11 release -wget https://raw.githubusercontent.com/Homebrew/homebrew-core/6bfc8950c696d1f952425e8af2a6248603dc0df9/Formula/m/molten-vk.rb -/usr/local/bin/brew install -f --overwrite ./molten-vk.rb -export CXX=clang++ -export CC=clang - -export BREW_PATH; -BREW_PATH="$(brew --prefix)" -export BREW_X64_PATH; -BREW_X64_PATH="$("/usr/local/bin/brew" --prefix)" -export BREW_BIN="/usr/local/bin" -export BREW_SBIN="/usr/local/sbin" -export CMAKE_EXTRA_OPTS='-DLLVM_TARGETS_TO_BUILD=arm64' - -export WORKDIR; -WORKDIR="$(pwd)" - -# Get Qt -if [ ! -d "/tmp/Qt/$QT_VER" ]; then - mkdir -p "/tmp/Qt" - git clone https://github.com/engnr/qt-downloader.git - cd qt-downloader - git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597 - # nested Qt 6.8.3 URL workaround - # sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader - # sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader - cd "/tmp/Qt" - "$BREW_X64_PATH/bin/pipenv" run pip3 install py7zr requests semantic_version lxml - mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64" - # sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.8.3 workaround - "$BREW_X64_PATH/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64" -fi - -cd "$WORKDIR" -ditto "/tmp/Qt/$QT_VER" "qt-downloader/$QT_VER" - -export Qt6_DIR="$WORKDIR/qt-downloader/$QT_VER/clang_64/lib/cmake/Qt$QT_VER_MAIN" -export SDL3_DIR="$BREW_ARM64_PATH/opt/sdl3/lib/cmake/SDL3" - -export PATH="$BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER/bin:$WORKDIR/qt-downloader/$QT_VER/clang_64/bin:$BREW_BIN:$BREW_SBIN:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH" -export LDFLAGS="-L$BREW_ARM64_PATH/lib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libavcodec.dylib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libavformat.dylib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libavutil.dylib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libswscale.dylib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libswresample.dylib $BREW_ARM64_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++.1.dylib $BREW_ARM64_PATH/lib/libSDL3.dylib $BREW_ARM64_PATH/lib/libGLEW.dylib $BREW_ARM64_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/libunwind.1.dylib -Wl,-rpath,$BREW_ARM64_PATH/lib" -export CPPFLAGS="-I$BREW_ARM64_PATH/include -I$BREW_X64_PATH/include -no-pie -D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" -export CFLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" -export LIBRARY_PATH="$BREW_ARM64_PATH/lib" -export LD_LIBRARY_PATH="$BREW_ARM64_PATH/lib" - -export VULKAN_SDK -VULKAN_SDK="$BREW_ARM64_PATH/opt/molten-vk" -ln -s "$VULKAN_SDK/lib/libMoltenVK.dylib" "$VULKAN_SDK/lib/libvulkan.dylib" || true -export VK_ICD_FILENAMES="$VULKAN_SDK/share/vulkan/icd.d/MoltenVK_icd.json" - -export LLVM_DIR -LLVM_DIR="$BREW_ARM64_PATH/opt/llvm@$LLVM_COMPILER_VER" -# exclude ffmpeg, LLVM, and sdl from submodule update -# shellcheck disable=SC2046 -git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/ffmpeg/ && !/llvm/ && !/SDL/ { print $3 }' .gitmodules) - -# 3rdparty fixes -sed -i '' "s/extern const double NSAppKitVersionNumber;/const double NSAppKitVersionNumber = 1343;/g" 3rdparty/hidapi/hidapi/mac/hid.c - -rm -rf build -mkdir build && cd build || exit 1 - -export MACOSX_DEPLOYMENT_TARGET=14.0 - -"$BREW_X64_PATH/bin/cmake" .. \ - -DUSE_SDL=ON \ - -DUSE_DISCORD_RPC=ON \ - -DUSE_VULKAN=ON \ - -DUSE_ALSA=OFF \ - -DUSE_PULSE=OFF \ - -DUSE_AUDIOUNIT=ON \ - -DUSE_SYSTEM_FFMPEG=ON \ - -DLLVM_CCACHE_BUILD=OFF \ - -DLLVM_BUILD_RUNTIME=OFF \ - -DLLVM_BUILD_TOOLS=OFF \ - -DLLVM_INCLUDE_DOCS=OFF \ - -DLLVM_INCLUDE_EXAMPLES=OFF \ - -DLLVM_INCLUDE_TESTS=OFF \ - -DLLVM_INCLUDE_TOOLS=OFF \ - -DLLVM_INCLUDE_UTILS=OFF \ - -DLLVM_USE_PERF=OFF \ - -DLLVM_ENABLE_Z3_SOLVER=OFF \ - -DUSE_NATIVE_INSTRUCTIONS=OFF \ - -DUSE_SYSTEM_MVK=ON \ - -DUSE_SYSTEM_FAUDIO=OFF \ - -DUSE_SYSTEM_SDL=ON \ - -DUSE_SYSTEM_OPENCV=ON \ - $CMAKE_EXTRA_OPTS \ - -DLLVM_TARGET_ARCH=arm64 \ - -DCMAKE_OSX_ARCHITECTURES=arm64 \ - -DCMAKE_IGNORE_PATH="$BREW_X64_PATH/lib" \ - -DCMAKE_IGNORE_PREFIX_PATH=/usr/local/opt \ - -DCMAKE_SYSTEM_PROCESSOR=arm64 \ - -DCMAKE_TOOLCHAIN_FILE=buildfiles/cmake/TCDarwinARM64.cmake \ - -DCMAKE_CXX_FLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" \ - -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ - -G Ninja - -"$BREW_PATH/bin/ninja"; build_status=$?; - -cd .. - -{ [ "$CI_HAS_ARTIFACTS" = "true" ]; -} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false" - -if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then - .ci/deploy-mac-arm64.sh -fi diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh deleted file mode 100644 index 287bd3949..000000000 --- a/.ci/build-mac.sh +++ /dev/null @@ -1,123 +0,0 @@ -#!/bin/sh -ex - -# shellcheck disable=SC2086 -export HOMEBREW_NO_AUTO_UPDATE=1 -export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 -brew unlink certifi -brew install -f --overwrite nasm ninja p7zip ccache pipenv #create-dmg - -#/usr/sbin/softwareupdate --install-rosetta --agree-to-license -arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" -arch -x86_64 /usr/local/bin/brew update -arch -x86_64 /usr/local/bin/brew install -f --overwrite python || arch -x86_64 /usr/local/bin/brew link --overwrite python -arch -x86_64 /usr/local/bin/brew uninstall -f --ignore-dependencies ffmpeg -arch -x86_64 /usr/local/bin/brew install -f --build-from-source ffmpeg@5 -arch -x86_64 /usr/local/bin/brew reinstall -f --build-from-source gnutls freetype -arch -x86_64 /usr/local/bin/brew install llvm@$LLVM_COMPILER_VER glew cmake sdl3 vulkan-headers coreutils -arch -x86_64 /usr/local/bin/brew link -f llvm@$LLVM_COMPILER_VER ffmpeg@5 - -# moltenvk based on commit for 1.2.11 release -wget https://raw.githubusercontent.com/Homebrew/homebrew-core/6bfc8950c696d1f952425e8af2a6248603dc0df9/Formula/m/molten-vk.rb -arch -x86_64 /usr/local/bin/brew install -f --overwrite ./molten-vk.rb -export CXX=clang++ -export CC=clang - -export BREW_PATH; -BREW_PATH="$(brew --prefix)" -export BREW_X64_PATH; -BREW_X64_PATH="$("/usr/local/bin/brew" --prefix)" -export BREW_BIN="/usr/local/bin" -export BREW_SBIN="/usr/local/sbin" -export CMAKE_EXTRA_OPTS='-DLLVM_TARGETS_TO_BUILD=X86' - -export WORKDIR; -WORKDIR="$(pwd)" - -# Get Qt -if [ ! -d "/tmp/Qt/$QT_VER" ]; then - mkdir -p "/tmp/Qt" - git clone https://github.com/engnr/qt-downloader.git - cd qt-downloader - git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597 - # nested Qt 6.8.3 URL workaround - # sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader - # sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader - cd "/tmp/Qt" - "$BREW_X64_PATH/bin/pipenv" run pip3 install py7zr requests semantic_version lxml - mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64" - # sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.8.3 workaround - "$BREW_X64_PATH/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64" -fi - -cd "$WORKDIR" -ditto "/tmp/Qt/$QT_VER" "qt-downloader/$QT_VER" - -export Qt6_DIR="$WORKDIR/qt-downloader/$QT_VER/clang_64/lib/cmake/Qt$QT_VER_MAIN" -export SDL3_DIR="$BREW_X64_PATH/opt/sdl3/lib/cmake/SDL3" - -export PATH="$BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER/bin:$WORKDIR/qt-downloader/$QT_VER/clang_64/bin:$BREW_BIN:$BREW_SBIN:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH" -export LDFLAGS="-L$BREW_X64_PATH/lib -Wl,-rpath,$BREW_X64_PATH/lib" -export CPPFLAGS="-I$BREW_X64_PATH/include -msse -msse2 -mcx16 -no-pie -D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" -export CFLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" -export LIBRARY_PATH="$BREW_X64_PATH/lib" -export LD_LIBRARY_PATH="$BREW_X64_PATH/lib" - -export VULKAN_SDK -VULKAN_SDK="$BREW_X64_PATH/opt/molten-vk" -ln -s "$VULKAN_SDK/lib/libMoltenVK.dylib" "$VULKAN_SDK/lib/libvulkan.dylib" -export VK_ICD_FILENAMES="$VULKAN_SDK/share/vulkan/icd.d/MoltenVK_icd.json" - -export LLVM_DIR -LLVM_DIR="BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER" -# exclude ffmpeg, LLVM, opencv, and sdl from submodule update -# shellcheck disable=SC2046 -git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/ffmpeg/ && !/llvm/ && !/opencv/ && !/SDL/ { print $3 }' .gitmodules) - -# 3rdparty fixes -sed -i '' "s/extern const double NSAppKitVersionNumber;/const double NSAppKitVersionNumber = 1343;/g" 3rdparty/hidapi/hidapi/mac/hid.c - -mkdir build && cd build || exit 1 - -export MACOSX_DEPLOYMENT_TARGET=14.0 - -"$BREW_X64_PATH/bin/cmake" .. \ - -DUSE_SDL=ON \ - -DUSE_DISCORD_RPC=ON \ - -DUSE_VULKAN=ON \ - -DUSE_ALSA=OFF \ - -DUSE_PULSE=OFF \ - -DUSE_AUDIOUNIT=ON \ - -DUSE_SYSTEM_FFMPEG=ON \ - -DLLVM_CCACHE_BUILD=OFF \ - -DLLVM_BUILD_RUNTIME=OFF \ - -DLLVM_BUILD_TOOLS=OFF \ - -DLLVM_INCLUDE_DOCS=OFF \ - -DLLVM_INCLUDE_EXAMPLES=OFF \ - -DLLVM_INCLUDE_TESTS=OFF \ - -DLLVM_INCLUDE_TOOLS=OFF \ - -DLLVM_INCLUDE_UTILS=OFF \ - -DLLVM_USE_PERF=OFF \ - -DLLVM_ENABLE_Z3_SOLVER=OFF \ - -DUSE_NATIVE_INSTRUCTIONS=OFF \ - -DUSE_SYSTEM_MVK=ON \ - -DUSE_SYSTEM_FAUDIO=OFF \ - -DUSE_SYSTEM_SDL=ON \ - -DUSE_SYSTEM_OPENCV=ON \ - $CMAKE_EXTRA_OPTS \ - -DLLVM_TARGET_ARCH=X86_64 \ - -DCMAKE_OSX_ARCHITECTURES=x86_64 \ - -DCMAKE_IGNORE_PATH="$BREW_PATH/lib" \ - -DCMAKE_CXX_FLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" \ - -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ - -G Ninja - -"$BREW_PATH/bin/ninja"; build_status=$?; - -cd .. - -{ [ "$CI_HAS_ARTIFACTS" = "true" ]; -} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false" - -if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then - .ci/deploy-mac.sh -fi diff --git a/.ci/deploy-linux.sh b/.ci/deploy-linux.sh deleted file mode 100755 index 48a94c0d4..000000000 --- a/.ci/deploy-linux.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -ex - -cd build || exit 1 - -CPU_ARCH="${1:-x86_64}" - -if [ "$DEPLOY_APPIMAGE" = "true" ]; then - DESTDIR=AppDir ninja install - - sudo curl -fsSLo /usr/bin/linuxdeploy "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-$CPU_ARCH.AppImage" - sudo chmod a+x /usr/bin/linuxdeploy - sudo curl -fsSLo /usr/bin/linuxdeploy-plugin-qt "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-$CPU_ARCH.AppImage" - sudo chmod a+x /usr/bin/linuxdeploy-plugin-qt - curl -fsSLo linuxdeploy-plugin-checkrt.sh https://github.com/darealshinji/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt.sh - chmod +x ./linuxdeploy-plugin-checkrt.sh - - export EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so" - export EXTRA_QT_PLUGINS="svg;wayland-decoration-client;wayland-graphics-integration-client;wayland-shell-integration;waylandcompositor" - - APPIMAGE_EXTRACT_AND_RUN=1 linuxdeploy --appdir AppDir --plugin qt --plugin checkrt - - # Remove libwayland-client because it has platform-dependent exports and breaks other OSes - rm -f ./AppDir/usr/lib/libwayland-client.so* - - # Remove libvulkan because it causes issues with gamescope - rm -f ./AppDir/usr/lib/libvulkan.so* - - # Remove git directory containing local commit history file - rm -rf ./AppDir/usr/share/rpcs3/git - - linuxdeploy --appimage-extract - ./squashfs-root/plugins/linuxdeploy-plugin-appimage/usr/bin/appimagetool AppDir -g - - mv ./*.AppImage ../RPCS3-Qt-UI.AppImage -fi diff --git a/.ci/deploy-mac-arm64.sh b/.ci/deploy-mac-arm64.sh deleted file mode 100644 index da1fbe916..000000000 --- a/.ci/deploy-mac-arm64.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/sh -ex - -# shellcheck disable=SC2086 -cd build || exit 1 - -# Gather explicit version number and number of commits -COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ../rpcs3/rpcs3_version.cpp) -COMM_COUNT=$(git rev-list --count HEAD) -COMM_HASH=$(git rev-parse --short=8 HEAD) - -AVVER="${COMM_TAG}-${COMM_COUNT}" - -# AVVER is used for GitHub releases, it is the version number. -echo "AVVER=$AVVER" >> ../.ci/ci-vars.env - -cd bin -mkdir "rpcs3.app/Contents/lib/" || true - -cp "$(realpath /opt/homebrew1/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib)" "rpcs3.app/Contents/Frameworks/libc++abi.1.dylib" -cp "$(realpath /opt/homebrew1/lib/libsharpyuv.0.dylib)" "rpcs3.app/Contents/lib/libsharpyuv.0.dylib" -cp "$(realpath /opt/homebrew1/lib/libintl.8.dylib)" "rpcs3.app/Contents/lib/libintl.8.dylib" - -rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \ -"rpcs3.app/Contents/Frameworks/QtQml.framework" \ -"rpcs3.app/Contents/Frameworks/QtQmlModels.framework" \ -"rpcs3.app/Contents/Frameworks/QtQuick.framework" \ -"rpcs3.app/Contents/Frameworks/QtVirtualKeyboard.framework" \ -"rpcs3.app/Contents/Plugins/platforminputcontexts" \ -"rpcs3.app/Contents/Plugins/virtualkeyboard" \ -"rpcs3.app/Contents/Resources/git" - -../../.ci/optimize-mac.sh rpcs3.app - -# Hack -install_name_tool \ --delete_rpath /opt/homebrew1/lib \ --delete_rpath /opt/homebrew/lib \ --delete_rpath /opt/homebrew1/opt/llvm@$LLVM_COMPILER_VER/lib \ --delete_rpath /usr/local/lib RPCS3.app/Contents/MacOS/rpcs3 -#-delete_rpath /opt/homebrew1/Cellar/sdl3/3.2.8/lib - -# Need to do this rename hack due to case insensitive filesystem -mv rpcs3.app RPCS3_.app -mv RPCS3_.app RPCS3.app - -# NOTE: "--deep" is deprecated -codesign --deep -fs - RPCS3.app - -echo "[InternetShortcut]" > Quickstart.url -echo "URL=https://rpcs3.net/quickstart" >> Quickstart.url -echo "IconIndex=0" >> Quickstart.url - -#DMG_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos_arm64.dmg" -#"$BREW_X64_PATH/bin/create-dmg" --volname RPCS3 \ -#--window-size 800 400 \ -#--icon-size 100 \ -#--icon rpcs3.app 200 190 \ -#--add-file Quickstart.url Quickstart.url 400 20 \ -#--hide-extension rpcs3.app \ -#--hide-extension Quickstart.url \ -#--app-drop-link 600 185 \ -#--skip-jenkins \ -#--format ULMO \ -#"$DMG_FILEPATH" \ -#RPCS3.app -#FILESIZE=$(stat -f %z "$DMG_FILEPATH") -#SHA256SUM=$(shasum -a 256 "$DMG_FILEPATH" | awk '{ print $1 }') - -ARCHIVE_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos_arm64.7z" -"$BREW_X64_PATH/bin/7z" a -mx9 "$ARCHIVE_FILEPATH" RPCS3.app Quickstart.url -FILESIZE=$(stat -f %z "$ARCHIVE_FILEPATH") -SHA256SUM=$(shasum -a 256 "$ARCHIVE_FILEPATH" | awk '{ print $1 }') - -cd .. -echo "${SHA256SUM};${FILESIZE}B" > "$RELEASE_MESSAGE" -cd bin diff --git a/.ci/deploy-mac.sh b/.ci/deploy-mac.sh deleted file mode 100644 index c29335874..000000000 --- a/.ci/deploy-mac.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/sh -ex - -# shellcheck disable=SC2086 -cd build || exit 1 - -# Gather explicit version number and number of commits -COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ../rpcs3/rpcs3_version.cpp) -COMM_COUNT=$(git rev-list --count HEAD) -COMM_HASH=$(git rev-parse --short=8 HEAD) - -AVVER="${COMM_TAG}-${COMM_COUNT}" - -# AVVER is used for GitHub releases, it is the version number. -echo "AVVER=$AVVER" >> ../.ci/ci-vars.env - -cd bin -mkdir "rpcs3.app/Contents/lib/" - -cp "/usr/local/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib" "rpcs3.app/Contents/lib/libc++abi.1.dylib" -cp "$(realpath /usr/local/lib/libsharpyuv.0.dylib)" "rpcs3.app/Contents/lib/libsharpyuv.0.dylib" -cp "$(realpath /usr/local/lib/libintl.8.dylib)" "rpcs3.app/Contents/lib/libintl.8.dylib" - -rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \ -"rpcs3.app/Contents/Frameworks/QtQml.framework" \ -"rpcs3.app/Contents/Frameworks/QtQmlModels.framework" \ -"rpcs3.app/Contents/Frameworks/QtQuick.framework" \ -"rpcs3.app/Contents/Frameworks/QtVirtualKeyboard.framework" \ -"rpcs3.app/Contents/Plugins/platforminputcontexts" \ -"rpcs3.app/Contents/Plugins/virtualkeyboard" \ -"rpcs3.app/Contents/Resources/git" - -../../.ci/optimize-mac.sh rpcs3.app - -# Need to do this rename hack due to case insensitive filesystem -mv rpcs3.app RPCS3_.app -mv RPCS3_.app RPCS3.app - -# Hack -install_name_tool \ --delete_rpath /usr/local/lib \ --delete_rpath /usr/local/opt/llvm@$LLVM_COMPILER_VER/lib RPCS3.app/Contents/MacOS/rpcs3 -#-delete_rpath /usr/local/Cellar/sdl3/3.2.8/lib - -# NOTE: "--deep" is deprecated -codesign --deep -fs - RPCS3.app - -echo "[InternetShortcut]" > Quickstart.url -echo "URL=https://rpcs3.net/quickstart" >> Quickstart.url -echo "IconIndex=0" >> Quickstart.url - -#DMG_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos.dmg" -#"$BREW_X64_PATH/bin/create-dmg" --volname RPCS3 \ -#--window-size 800 400 \ -#--icon-size 100 \ -#--icon rpcs3.app 200 190 \ -#--add-file Quickstart.url Quickstart.url 400 20 \ -#--hide-extension rpcs3.app \ -#--hide-extension Quickstart.url \ -#--app-drop-link 600 185 \ -#--skip-jenkins \ -#--format ULMO \ -#"$DMG_FILEPATH" \ -#RPCS3.app -#FILESIZE=$(stat -f %z "$DMG_FILEPATH") -#SHA256SUM=$(shasum -a 256 "$DMG_FILEPATH" | awk '{ print $1 }') - -ARCHIVE_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos.7z" -"$BREW_X64_PATH/bin/7z" a -mx9 "$ARCHIVE_FILEPATH" RPCS3.app Quickstart.url -FILESIZE=$(stat -f %z "$ARCHIVE_FILEPATH") -SHA256SUM=$(shasum -a 256 "$ARCHIVE_FILEPATH" | awk '{ print $1 }') - -cd .. -echo "${SHA256SUM};${FILESIZE}B" > "$RELEASE_MESSAGE" -cd bin diff --git a/.ci/deploy-windows.sh b/.ci/deploy-windows.sh deleted file mode 100755 index c4e88e8f2..000000000 --- a/.ci/deploy-windows.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh -ex - -# First let's see print some info about our caches -"$(cygpath -u "$CCACHE_BIN_DIR")"/ccache.exe --show-stats -v - -# Remove unecessary files -rm -f ./rpcs3/bin/rpcs3.exp ./rpcs3/bin/rpcs3.lib ./rpcs3/bin/rpcs3.pdb ./rpcs3/bin/vc_redist.x64.exe -rm -rf ./rpcs3/bin/git - -# Prepare compatibility and SDL database for packaging -mkdir -p ./rpcs3/bin/config/input_configs -curl -fsSL 'https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt' 1> ./rpcs3/bin/config/input_configs/gamecontrollerdb.txt -cp -rf ./build-msvc/bin/ ./rpcs3/bin/ - -# Package artifacts -7z a -m0=LZMA2 -mx9 "$BUILD" ./rpcs3/bin/* - -# Move files to publishing directory -cp -- "$BUILD" ./RPCS3-Qt-UI.7z diff --git a/.ci/docker.env b/.ci/docker.env deleted file mode 100644 index 2b36fb34c..000000000 --- a/.ci/docker.env +++ /dev/null @@ -1,15 +0,0 @@ -# Variables set by Azure Pipelines -CI_HAS_ARTIFACTS -BUILD_REASON -BUILD_SOURCEVERSION -BUILD_ARTIFACTSTAGINGDIRECTORY -BUILD_REPOSITORY_NAME -BUILD_SOURCEBRANCHNAME -APPDIR -ARTDIR -RELEASE_MESSAGE -# Variables for build matrix -COMPILER -DEPLOY_APPIMAGE -# Private variables -GITHUB_TOKEN diff --git a/.ci/export-azure-vars.sh b/.ci/export-azure-vars.sh deleted file mode 100755 index 033dd41cc..000000000 --- a/.ci/export-azure-vars.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -e - -# Export variables for later stages of the Azure pipeline -# Values done in this manner will appear as environment variables -# in later stages. - -# From pure-sh-bible -# Setting 'IFS' tells 'read' where to split the string. -while IFS='=' read -r key val; do - # Skip over lines containing comments. - [ "${key##\#*}" ] || continue - echo "##vso[task.setvariable variable=$key]$val" -done < ".ci/ci-vars.env" diff --git a/.ci/export-cirrus-vars.sh b/.ci/export-cirrus-vars.sh deleted file mode 100644 index 561e77e92..000000000 --- a/.ci/export-cirrus-vars.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -e - -# Export variables for later stages of the Cirrus pipeline -# Values done in this manner will appear as environment variables -# in later stages. - -# From pure-sh-bible -# Setting 'IFS' tells 'read' where to split the string. -while IFS='=' read -r key val; do - # Skip over lines containing comments. - [ "${key##\#*}" ] || continue - export "$key"="$val" -done < ".ci/ci-vars.env" diff --git a/.ci/generate-qt-ts.sh b/.ci/generate-qt-ts.sh deleted file mode 100755 index a9fc139a9..000000000 --- a/.ci/generate-qt-ts.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -ex - -mkdir -p ../translations - -LUPDATE_PATH=$(find /usr -name lupdate -type f 2>/dev/null | head -n 1) -if [ -z "$LUPDATE_PATH" ]; then - echo "Error: lupdate not found!" - exit 1 -else - echo "lupdate found at: $LUPDATE_PATH" - $LUPDATE_PATH -recursive . -ts ../translations/rpcs3_template.ts - sed -i 's|filename="\.\./|filename="./|g' ../translations/rpcs3_template.ts -fi \ No newline at end of file diff --git a/.ci/get_keys-windows.sh b/.ci/get_keys-windows.sh deleted file mode 100644 index 8c7ea9c14..000000000 --- a/.ci/get_keys-windows.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -ex - -curl -fLo "./llvm.lock" "https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-${LLVM_VER}/llvmlibs_mt.7z.sha256" -curl -fLo "./glslang.lock" "https://github.com/RPCS3/glslang/releases/download/custom-build-win/glslanglibs_mt.7z.sha256" diff --git a/.ci/github-upload.sh b/.ci/github-upload.sh deleted file mode 100755 index 8cc89314d..000000000 --- a/.ci/github-upload.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/sh -ex - -ARTIFACT_DIR="$BUILD_ARTIFACTSTAGINGDIRECTORY" -generate_post_data() -{ - body=$(cat GitHubReleaseMessage.txt) - cat <> release.json - -cat release.json -id=$(grep '"id"' release.json | cut -d ':' -f2 | head -n1 | awk '{$1=$1;print}') -id=${id%?} -echo "${id:?}" - -upload_file() -{ - curl -fsS \ - -H "Authorization: token ${RPCS3_TOKEN}" \ - -H "Accept: application/vnd.github.v3+json" \ - -H "Content-Type: application/octet-stream" \ - --data-binary @"$2"/"$3" \ - "https://uploads.github.com/repos/$UPLOAD_REPO_FULL_NAME/releases/$1/assets?name=$3" -} - -for file in "$ARTIFACT_DIR"/*; do - name=$(basename "$file") - upload_file "$id" "$ARTIFACT_DIR" "$name" -done diff --git a/.ci/install-freebsd.sh b/.ci/install-freebsd.sh deleted file mode 100755 index de10561df..000000000 --- a/.ci/install-freebsd.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env -S su -m root -ex -# NOTE: this script is run under root permissions -# shellcheck shell=sh disable=SC2096 - -# RPCS3 often needs recent Qt and Vulkan-Headers -sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf - -export ASSUME_ALWAYS_YES=true -pkg info # debug - -# WITH_LLVM -pkg install "llvm$LLVM_COMPILER_VER" - -# Mandatory dependencies (qtX-base is pulled via qtX-multimedia) -pkg install git ccache cmake ninja "qt$QT_VER_MAIN-multimedia" "qt$QT_VER_MAIN-svg" glew openal-soft ffmpeg - -# Optional dependencies (libevdev is pulled by qtX-base) -pkg install pkgconf alsa-lib pulseaudio sdl3 evdev-proto vulkan-headers vulkan-loader diff --git a/.ci/optimize-mac.sh b/.ci/optimize-mac.sh deleted file mode 100644 index 5fea7877f..000000000 --- a/.ci/optimize-mac.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh - -file_path=$(find "$1/Contents/MacOS" -type f -print0 | head -n 1) - -if [ -z "$file_path" ]; then - echo "No executable file found in $1/Contents/MacOS" >&2 - exit 1 -fi - - -target_architecture="$(lipo "$file_path" -archs)" - -if [ -z "$target_architecture" ]; then - exit 1 -fi - -# shellcheck disable=SC3045 -find "$1" -type f -print0 | while IFS= read -r -d '' file; do - echo Thinning "$file" -> "$target_architecture" - lipo "$file" -thin "$target_architecture" -output "$file" || true -done diff --git a/.ci/setup-windows.sh b/.ci/setup-windows.sh deleted file mode 100755 index d06a078ab..000000000 --- a/.ci/setup-windows.sh +++ /dev/null @@ -1,134 +0,0 @@ -#!/bin/sh -ex - -# These are Azure specific, so we wrap them for portability -REPO_NAME="$BUILD_REPOSITORY_NAME" -REPO_BRANCH="$SYSTEM_PULLREQUEST_SOURCEBRANCH" -PR_NUMBER="$SYSTEM_PULLREQUEST_PULLREQUESTID" - -# Resource/dependency URLs -# Qt mirrors can be volatile and slow, so we list 2 -#QT_HOST="http://mirrors.ocf.berkeley.edu/qt/" -QT_HOST="http://qt.mirror.constant.com/" -QT_URL_VER=$(echo "$QT_VER" | sed "s/\.//g") -QT_VER_MSVC_UP=$(echo "${QT_VER_MSVC}" | tr '[:lower:]' '[:upper:]') -QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/qt${QT_VER_MAIN}_${QT_URL_VER}/qt${QT_VER_MAIN}_${QT_URL_VER}/qt.qt${QT_VER_MAIN}.${QT_URL_VER}." -QT_PREFIX_2="win64_${QT_VER_MSVC}_64/${QT_VER}-0-${QT_DATE}" -QT_SUFFIX="-Windows-Windows_11_23H2-${QT_VER_MSVC_UP}-Windows-Windows_11_23H2-X86_64.7z" -QT_BASE_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtbase${QT_SUFFIX}" -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}" -# LLVMLIBS_URL="https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-${LLVM_VER}/llvmlibs_mt.7z" -# GLSLANG_URL='https://github.com/RPCS3/glslang/releases/latest/download/glslanglibs_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" - -DEP_URLS=" \ - $QT_BASE_URL \ - $QT_DECL_URL \ - $QT_TOOL_URL \ - $QT_MM_URL \ - $QT_SVG_URL \ - $LLVMLIBS_URL \ - $GLSLANG_URL \ - $VULKAN_SDK_URL\ - $CCACHE_URL" - -# Azure pipelines doesn't make a cache dir if it doesn't exist, so we do it manually -[ -d "$DEPS_CACHE_DIR" ] || mkdir "$DEPS_CACHE_DIR" - -# Pull all the submodules except llvm, since it is built separately and we just download that build -# Note: Tried to use git submodule status, but it takes over 20 seconds -# shellcheck disable=SC2046 -git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/FAudio/ { print $3 }' .gitmodules) - -# Git bash doesn't have rev, so here it is -rev() -{ - echo "$1" | awk '{ for(i = length($0); i != 0; --i) { a = a substr($0, i, 1); } } END { print a }' -} - -# Usage: download_and_verify url checksum algo file -# Check to see if a file is already cached, and the checksum matches. If not, download it. -# Tries up to 3 times -download_and_verify() -{ - url="$1" - correctChecksum="$2" - algo="$3" - fileName="$4" - - for _ in 1 2 3; do - [ -e "$DEPS_CACHE_DIR/$fileName" ] || curl -fLo "$DEPS_CACHE_DIR/$fileName" "$url" - fileChecksum=$("${algo}sum" "$DEPS_CACHE_DIR/$fileName" | awk '{ print $1 }') - [ "$fileChecksum" = "$correctChecksum" ] && return 0 - done - - return 1; -} - -# Some dependencies install here -[ -d "./rpcs3/build/lib_ext/Release-x64" ] || mkdir -p "./rpcs3/build/lib_ext/Release-x64" - -for url in $DEP_URLS; do - # Get the filename from the URL and remove query strings (?arg=something). - fileName="$(rev "$(rev "$url" | cut -d'/' -f1)" | cut -d'?' -f1)" - [ -z "$fileName" ] && echo "Unable to parse url: $url" && exit 1 - - # shellcheck disable=SC1003 - case "$url" in - *qt*) checksum=$(curl -fL "${url}.sha1"); algo="sha1"; outDir="$QTDIR/" ;; - *llvm*) checksum=$(curl -fL "${url}.sha256"); algo="sha256"; outDir="./rpcs3/build/lib_ext/Release-x64" ;; - *glslang*) checksum=$(curl -fL "${url}.sha256"); algo="sha256"; outDir="./rpcs3/build/lib_ext/Release-x64" ;; - *ccache*) checksum=$CCACHE_SHA; algo="sha256"; outDir="$CCACHE_BIN_DIR" ;; - *Vulkan*) - # Vulkan setup needs to be run in batch environment - # Need to subshell this or else it doesn't wait - download_and_verify "$url" "$VULKAN_SDK_SHA" "sha256" "$fileName" - cp "$DEPS_CACHE_DIR/$fileName" . - _=$(echo "$fileName --accept-licenses --default-answer --confirm-command install" | cmd) - continue - ;; - *) echo "Unknown url resource: $url"; exit 1 ;; - esac - - download_and_verify "$url" "$checksum" "$algo" "$fileName" - 7z x -y "$DEPS_CACHE_DIR/$fileName" -aos -o"$outDir" -done - -# Setup ccache tool -[ -d "$CCACHE_DIR" ] || mkdir -p "$(cygpath -u "$CCACHE_DIR")" -CCACHE_SH_DIR=$(cygpath -u "$CCACHE_BIN_DIR") -mv "$CCACHE_SH_DIR"/ccache-*/* "$CCACHE_SH_DIR" -cp "$CCACHE_SH_DIR"/ccache.exe "$CCACHE_SH_DIR"/cl.exe - -# Gather explicit version number and number of commits -COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp) -COMM_COUNT=$(git rev-list --count HEAD) -COMM_HASH=$(git rev-parse --short=8 HEAD) - -# Format the above into filenames -if [ -n "$PR_NUMBER" ]; then - AVVER="${COMM_TAG}-${COMM_HASH}" - BUILD_RAW="rpcs3-v${AVVER}_win64" - BUILD="${BUILD_RAW}.7z" -else - AVVER="${COMM_TAG}-${COMM_COUNT}" - BUILD_RAW="rpcs3-v${AVVER}-${COMM_HASH}_win64" - BUILD="${BUILD_RAW}.7z" -fi - -# BRANCH is used for experimental build warnings for pr builds, used in main_window.cpp. -# BUILD is the name of the release artifact -# BUILD_RAW is just filename -# AVVER is used for GitHub releases, it is the version number. -BRANCH="${REPO_NAME}/${REPO_BRANCH}" - -# SC2129 -{ - echo "BRANCH=$BRANCH" - echo "BUILD=$BUILD" - echo "BUILD_RAW=$BUILD_RAW" - echo "AVVER=$AVVER" -} >> .ci/ci-vars.env diff --git a/.github/workflows/rpcs3.yml b/.github/workflows/rpcs3.yml deleted file mode 100644 index d09e10357..000000000 --- a/.github/workflows/rpcs3.yml +++ /dev/null @@ -1,222 +0,0 @@ -name: Build RPCS3 Qt UI (Legacy) - -defaults: - run: - shell: bash -on: - push: - paths-ignore: - - "**/*.md" - pull_request: - paths-ignore: - - "**/*.md" - workflow_dispatch: - -concurrency: - group: ${{ github.ref }}-${{ github.event_name }} - cancel-in-progress: false - -env: - BUILD_REPOSITORY_NAME: ${{ github.repository }} - BUILD_SOURCEBRANCHNAME: ${{ github.ref_name }} - BUILD_SOURCEVERSION: ${{ github.sha }} - BUILD_ARTIFACTSTAGINGDIRECTORY: ${{ github.workspace }}/artifacts/ - -jobs: - Linux_Build: - strategy: - fail-fast: false - matrix: - include: - - os: ubuntu-24.04 - build_sh: ".ci/build-linux.sh" - compiler: clang - - os: ubuntu-24.04 - build_sh: ".ci/build-linux.sh" - compiler: gcc - - os: ubuntu-24.04-arm - build_sh: ".ci/build-linux-aarch64.sh" - compiler: clang - name: RPCS3 Qt UI (Legacy) for Linux ${{ matrix.os }} ${{ matrix.compiler }} - runs-on: ${{ matrix.os }} - env: - CCACHE_DIR: ${{ github.workspace }}/ccache - CI_HAS_ARTIFACTS: true - DEPLOY_APPIMAGE: true - APPDIR: "./appdir" - ARTDIR: "./artifacts" - COMPILER: ${{ matrix.compiler }} - RX_VERSION: "Unknown" - RX_SHA: "Unknown" - steps: - - name: Checkout repository - uses: actions/checkout@main - with: - fetch-depth: 0 - - - name: Setup Cache - uses: actions/cache@main - with: - path: ${{ env.CCACHE_DIR }} - key: ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}-${{github.run_id}} - restore-keys: | - ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}- - - - name: Setup dependencies - run: | - echo "Types: deb" | sudo tee -a /etc/apt/sources.list.d/ubuntu.sources - echo "URIs: ${{ matrix.os == 'ubuntu-24.04-arm' && 'http://ports.ubuntu.com/ubuntu-ports' || 'http://azure.archive.ubuntu.com/ubuntu/' }}" | sudo tee -a /etc/apt/sources.list.d/ubuntu.sources - echo "Suites: plucky plucky-updates plucky-security" | sudo tee -a /etc/apt/sources.list.d/ubuntu.sources - echo "Components: main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ubuntu.sources - echo "Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg" | sudo tee -a /etc/apt/sources.list.d/ubuntu.sources - - sudo apt update - sudo apt install -y cmake build-essential libunwind-dev \ - libvulkan-dev vulkan-validationlayers \ - libsox-dev ninja-build libasound2-dev libglfw3-dev nasm libudev-dev \ - libpulse-dev libopenal-dev libglew-dev zlib1g-dev libedit-dev \ - libevdev-dev libjack-dev libsndio-dev libglvnd-dev \ - qt6-base-dev qt6-svg-dev qt6-base-private-dev qt6-multimedia-dev \ - clang lld gcc-14 g++-14 \ - - - name: Build - run: | - ${{ matrix.build_sh }} - - RX_VERSION=`cat .rx.version | awk -F'-' '{print $1}'` - RX_SHA=`cat .rx.version | awk -F'-' '{print $5}'` - - echo "RX_VERSION=$RX_VERSION" >> "${{ github.env }}" - echo "RX_SHA=$RX_SHA" >> "${{ github.env }}" - mv RPCS3-Qt-UI.AppImage RPCS3-Qt-UI-Linux-${{ runner.arch }}-${{ matrix.compiler }}.AppImage - - - name: Upload artifacts - uses: actions/upload-artifact@main - with: - name: RPCS3 Qt UI (Legacy) for Linux (${{ runner.arch }}, ${{ matrix.compiler }}) - path: RPCS3-Qt-UI-Linux-${{ runner.arch }}-${{ matrix.compiler }}.AppImage - compression-level: 0 - - - name: Deploy build - uses: softprops/action-gh-release@v2 - if: | - github.event_name != 'pull_request' && - github.ref == 'refs/heads/master' && - github.repository == 'RPCSX/rpcsx' - with: - prerelease: false - make_latest: true - repository: RPCSX/rpcsx-build - token: ${{ secrets.BUILD_TOKEN }} - tag_name: v${{ env.RX_VERSION }}-${{ env.RX_SHA }} - files: RPCS3-Qt-UI-Linux-${{ runner.arch }}-${{ matrix.compiler }}.AppImage - body: ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} - - - Windows_Build: - name: RPCS3 Qt UI (Legacy) for Windows - runs-on: windows-2025 - env: - COMPILER: msvc - QT_VER_MAIN: '6' - QT_VER: '6.8.3' - QT_VER_MSVC: 'msvc2022' - QT_DATE: '202503201308' - LLVM_VER: '19.1.7' - VULKAN_VER: '1.3.268.0' - VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5' - CCACHE_SHA: '1f39f3ad5aae3fe915e99ad1302633bc8f6718e58fa7c0de2b0ba7e080f0f08c' - CCACHE_BIN_DIR: 'C:\ccache_bin' - CCACHE_DIR: 'C:\ccache' - CCACHE_INODECACHE: 'true' - CCACHE_SLOPPINESS: 'time_macros' - DEPS_CACHE_DIR: ./dependency_cache - RX_VERSION: 'Unknown' - RX_SHA: 'Unknown' - - steps: - - name: Checkout repository - uses: actions/checkout@main - with: - fetch-depth: 0 - - - name: Setup env - run: | - echo "QTDIR=C:\Qt\${{ env.QT_VER }}\${{ env.QT_VER_MSVC }}_64" >> ${{ github.env }} - echo "VULKAN_SDK=C:\VulkanSDK\${{ env.VULKAN_VER }}" >> ${{ github.env }} - - # - name: Get Cache Keys - # run: .ci/get_keys-windows.sh - - # - name: Setup Build Ccache - # uses: actions/cache@main - # with: - # path: ${{ env.CCACHE_DIR }} - # key: "${{ runner.os }}-ccache-${{ env.COMPILER }}-${{github.run_id}}" - # restore-keys: ${{ runner.os }}-ccache-${{ env.COMPILER }}- - - # - name: Setup Dependencies Cache - # uses: actions/cache@main - # with: - # path: ${{ env.DEPS_CACHE_DIR }} - # key: "${{ runner.os }}-${{ env.COMPILER }}-${{ env.QT_VER }}-${{ env.VULKAN_SDK_SHA }}-${{ env.CCACHE_SHA }}-${{ hashFiles('llvm.lock') }}-${{ hashFiles('glslang.lock') }}" - # restore-keys: ${{ runner.os }}-${{ env.COMPILER }}- - - - name: Download and unpack dependencies - run: .ci/setup-windows.sh - - - name: Export Variables - run: | - while IFS='=' read -r key val; do - # Skip lines that are empty or start with '#' - [[ -z "$key" || "$key" =~ ^# ]] && continue - echo "$key=$val" >> "${{ github.env }}" - done < .ci/ci-vars.env - - # - name: Add msbuild to PATH - # uses: microsoft/setup-msbuild@main - - - name: Configure - run: | - cmake -B build-msvc -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DWITH_RPCSX=off -DWITH_RPCS3=on -DWITH_RPCS3_QT_UI=on -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DCMAKE_INSTALL_PREFIX="${sourceDir}/out/install/${presetName}" -DUSE_NATIVE_INSTRUCTIONS=on -DUSE_PRECOMPILED_HEADERS=on -DUSE_FAUDIO=off -DUSE_SYSTEM_CURL=off -DUSE_SYSTEM_ZLIB=off -DUSE_SYSTEM_OPENAL=off -DUSE_SYSTEM_OPENCV=off -DBUILD_LLVM=on -DSTATIC_LINK_LLVM=on - - - name: Export Version - run: | - RX_VERSION=`cat .rx.version | awk -F'-' '{print $1}'` - RX_SHA=`cat .rx.version | awk -F'-' '{print $5}'` - echo "RX_VERSION=$RX_VERSION" >> "${{ github.env }}" - echo "RX_SHA=$RX_SHA" >> "${{ github.env }}" - - - name: Build - run: | - cmake --build build-msvc --config Release - - - - name: Pack up build artifacts - run: | - mkdir -p "${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}" - .ci/deploy-windows.sh - mv RPCS3-Qt-UI.7z RPCS3-Qt-UI-Windows-${{ runner.arch }}-${{ env.COMPILER }}.7z - - - name: Upload artifacts (7z) - uses: actions/upload-artifact@main - with: - name: RPCS3 Qt UI (Legacy) for Windows (MSVC) - path: RPCS3-Qt-UI-Windows-${{ runner.arch }}-${{ env.COMPILER }}.7z - compression-level: 0 - if-no-files-found: error - - - name: Deploy build - uses: softprops/action-gh-release@v2 - if: | - github.event_name != 'pull_request' && - github.ref == 'refs/heads/master' && - github.repository == 'RPCSX/rpcsx' - with: - prerelease: false - make_latest: true - repository: RPCSX/rpcsx-build - token: ${{ secrets.BUILD_TOKEN }} - tag_name: v${{ env.RX_VERSION }}-${{ env.RX_SHA }} - files: RPCS3-Qt-UI-Windows-${{ runner.arch }}-${{ env.COMPILER }}.7z - body: ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f9ced89d..443462e6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,9 +131,9 @@ function(target_base_address target address) endfunction() -option(WITH_RPCSX "Enable RPCSX" ON) -option(WITH_RPCS3 "Enable RPCS3" OFF) -option(WITH_RPCS3_QT_UI "Enable RPCS3 UI" OFF) +option(WITH_PS3 "Enable PS3 emulation support" OFF) +option(WITH_PS4 "Enable PS4 emulation support" ON) + option(WITHOUT_OPENGL "Disable OpenGL" OFF) option(WITHOUT_OPENGLEW "Disable OpenGLEW" OFF) option(WITHOUT_OPENAL "Disable OpenAL" OFF) @@ -159,8 +159,7 @@ option(USE_SYSTEM_OPENCV "Prefer system OpenCV instead of the builtin one" ON) option(HAS_MEMORY_BREAKPOINTS "Add support for memory breakpoints to the interpreter" OFF) option(USE_LTO "Use LTO for building" ON) - -if (NOT WITH_RPCS3) +if (NOT WITH_PS3) set(WITHOUT_OPENGL on) set(WITHOUT_OPENGLEW on) set(WITHOUT_OPENAL on) @@ -252,6 +251,8 @@ add_subdirectory(3rdparty EXCLUDE_FROM_ALL) add_subdirectory(rx EXCLUDE_FROM_ALL) include(3rdparty/llvm/CMakeLists.txt) +include(ConfigureCompiler) +include(CheckFunctionExists) if (NOT RX_TAG) set(RX_TAG 0) @@ -266,7 +267,7 @@ target_compile_definitions(rx PRIVATE RX_TAG_VERSION=${RX_TAG_VERSION} ) -if (WITH_RPCSX) +if (WITH_PS4) find_package(nlohmann_json CONFIG) add_subdirectory(tools) add_subdirectory(orbis-kernel) @@ -274,14 +275,7 @@ endif() add_subdirectory(rpcsx) -if (WITH_RPCS3) - include(ConfigureCompiler) - include(CheckFunctionExists) - +if (WITH_PS3) add_subdirectory(rpcs3) add_subdirectory(ps3fw) endif() - -if (NOT ANDROID AND WITH_RPCS3_QT_UI AND WITH_RPCS3) - add_subdirectory(rpcs3qt-legacy) -endif() diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt index 9cd61d123..b834c0fe7 100644 --- a/android/CMakeLists.txt +++ b/android/CMakeLists.txt @@ -88,8 +88,8 @@ target_link_libraries(3rdparty_ffmpeg INTERFACE add_dependencies(3rdparty_ffmpeg ffmpeg-unpack) -set(WITH_RPCSX off) -set(WITH_RPCS3 on) +set(WITH_PS4 off) +set(WITH_PS3 on) set(USE_SYSTEM_LIBUSB off) set(USE_SYSTEM_CURL off) set(USE_DISCORD_RPC off) @@ -113,8 +113,6 @@ add_library(${CMAKE_PROJECT_NAME} SHARED src/rpcsx-android.cpp ) -target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC rpcs3/rpcs3) - target_link_libraries(${CMAKE_PROJECT_NAME} rpcs3 rpcsx::fw::ps3 diff --git a/rpcs3qt-legacy/.clang-format b/rpcs3qt-legacy/.clang-format deleted file mode 100644 index 08b2119b3..000000000 --- a/rpcs3qt-legacy/.clang-format +++ /dev/null @@ -1,32 +0,0 @@ -Standard: c++20 -UseTab: AlignWithSpaces -TabWidth: 4 -IndentWidth: 4 -AccessModifierOffset: -4 -PointerAlignment: Left -NamespaceIndentation: All -ColumnLimit: 0 -BreakBeforeBraces: Allman -BreakConstructorInitializers: BeforeColon -BreakBeforeBinaryOperators: None -BreakBeforeTernaryOperators: false -AlwaysBreakTemplateDeclarations: Yes -AllowShortIfStatementsOnASingleLine: Never -AllowShortBlocksOnASingleLine: Never -AllowShortCaseLabelsOnASingleLine: true -AllowShortFunctionsOnASingleLine: Empty -AllowShortLoopsOnASingleLine: false -AllowShortLambdasOnASingleLine: Empty -Cpp11BracedListStyle: true -IndentCaseLabels: false -SortIncludes: false -ReflowComments: true -AlignConsecutiveAssignments: false -AlignTrailingComments: true -AlignAfterOpenBracket: DontAlign -ConstructorInitializerAllOnOneLineOrOnePerLine: false -BinPackArguments: true -BinPackParameters: true -AlwaysBreakAfterReturnType: None -KeepEmptyLinesAtTheStartOfBlocks: true -IndentWrappedFunctionNames: false diff --git a/rpcs3qt-legacy/CMakeLists.txt b/rpcs3qt-legacy/CMakeLists.txt deleted file mode 100644 index f6b868a3e..000000000 --- a/rpcs3qt-legacy/CMakeLists.txt +++ /dev/null @@ -1,281 +0,0 @@ -include(qt6.cmake) - -add_library(rpcs3_ui STATIC - about_dialog.cpp - auto_pause_settings_dialog.cpp - basic_mouse_settings_dialog.cpp - breakpoint_handler.cpp - breakpoint_list.cpp - call_stack_list.cpp - camera_settings_dialog.cpp - cg_disasm_window.cpp - cheat_manager.cpp - config_adapter.cpp - config_checker.cpp - curl_handle.cpp - custom_dialog.cpp - custom_table_widget_item.cpp - debugger_add_bp_window.cpp - debugger_frame.cpp - debugger_list.cpp - downloader.cpp - dimensions_dialog.cpp - _discord_utils.cpp - emu_settings.cpp - elf_memory_dumping_dialog.cpp - emulated_pad_settings_dialog.cpp - fatal_error_dialog.cpp - find_dialog.cpp - flow_layout.cpp - flow_widget.cpp - flow_widget_item.cpp - game_compatibility.cpp - game_list.cpp - game_list_base.cpp - game_list_delegate.cpp - game_list_frame.cpp - game_list_grid.cpp - game_list_grid_item.cpp - game_list_table.cpp - gui_application.cpp - gl_gs_frame.cpp - gs_frame.cpp - gui_game_info.cpp - gui_settings.cpp - infinity_dialog.cpp - input_dialog.cpp - instruction_editor_dialog.cpp - ipc_settings_dialog.cpp - kernel_explorer.cpp - localized.cpp - localized_emu.cpp - log_frame.cpp - log_viewer.cpp - main_window.cpp - memory_string_searcher.cpp - memory_viewer_panel.cpp - microphone_creator.cpp - midi_creator.cpp - movie_item.cpp - movie_item_base.cpp - msg_dialog_frame.cpp - osk_dialog_frame.cpp - pad_led_settings_dialog.cpp - pad_motion_settings_dialog.cpp - pad_settings_dialog.cpp - patch_creator_dialog.cpp - patch_manager_dialog.cpp - permissions.cpp - persistent_settings.cpp - pkg_install_dialog.cpp - progress_dialog.cpp - progress_indicator.cpp - ps_move_tracker_dialog.cpp - qt_camera_handler.cpp - qt_camera_video_sink.cpp - qt_music_handler.cpp - qt_utils.cpp - qt_video_source.cpp - raw_mouse_settings_dialog.cpp - register_editor_dialog.cpp - recvmessage_dialog_frame.cpp - render_creator.cpp - rpcn_settings_dialog.cpp - rsx_debugger.cpp - save_data_dialog.cpp - save_data_info_dialog.cpp - save_data_list_dialog.cpp - save_manager_dialog.cpp - savestate_manager_dialog.cpp - screenshot_item.cpp - screenshot_manager_dialog.cpp - screenshot_preview.cpp - sendmessage_dialog_frame.cpp - settings.cpp - settings_dialog.cpp - shortcut_utils.cpp - shortcut_dialog.cpp - shortcut_handler.cpp - shortcut_settings.cpp - skylander_dialog.cpp - syntax_highlighter.cpp - system_cmd_dialog.cpp - table_item_delegate.cpp - tooltips.cpp - trophy_manager_dialog.cpp - trophy_notification_frame.cpp - trophy_notification_helper.cpp - update_manager.cpp - user_account.cpp - user_manager_dialog.cpp - uuid.cpp - vfs_dialog.cpp - vfs_dialog_path_widget.cpp - vfs_dialog_tab.cpp - vfs_dialog_usb_input.cpp - vfs_dialog_usb_tab.cpp - vfs_tool_dialog.cpp - video_label.cpp - welcome_dialog.cpp - - about_dialog.ui - camera_settings_dialog.ui - main_window.ui - pad_led_settings_dialog.ui - pad_motion_settings_dialog.ui - pad_settings_dialog.ui - patch_creator_dialog.ui - patch_manager_dialog.ui - ps_move_tracker_dialog.ui - settings_dialog.ui - shortcut_dialog.ui - welcome_dialog.ui - - resources.qrc -) - -if(HAS_MEMORY_BREAKPOINTS) - target_compile_definitions(rpcs3_ui PRIVATE RPCS3_HAS_MEMORY_BREAKPOINTS) -endif() - -if(WIN32) - target_sources(rpcs3_ui PUBLIC windows.qrc) - target_compile_definitions(rpcs3_ui PRIVATE UNICODE _UNICODE) -endif() - -set_target_properties(rpcs3_ui - PROPERTIES - AUTOMOC ON - AUTOUIC ON - AUTORCC ON) - -# AUTOMOC brings Windows.h to the sources, which have some definitions conflicting with winsock2.h -# define WIN32_LEAN_AND_MEAN resolve the problem -# https://docs.microsoft.com/en-us/windows/win32/winsock/creating-a-basic-winsock-application -# https://docs.microsoft.com/en-us/windows/win32/winprog/using-the-windows-headers#faster-builds-with-smaller-header-files -target_compile_definitions(rpcs3_ui PUBLIC WIN32_LEAN_AND_MEAN NOMINMAX) - -target_link_libraries(rpcs3_ui - PUBLIC - 3rdparty::qt6 - 3rdparty::yaml-cpp - rpcs3 - rpcsx::fw::ps3::api -) - -if(TARGET OpenGL::EGL) - target_link_libraries(rpcs3_ui PUBLIC OpenGL::EGL) -endif() - - -if (NOT ANDROID) - if(WIN32) - add_executable(rpcs3qt-ui-legacy WIN32) - target_sources(rpcs3qt-ui-legacy PRIVATE rpcs3.rc) - target_compile_definitions(rpcs3qt-ui-legacy PUBLIC WIN32_LEAN_AND_MEAN NOMINMAX UNICODE _UNICODE) - elseif(APPLE) - add_executable(rpcs3qt-ui-legacy MACOSX_BUNDLE) - target_sources(rpcs3qt-ui-legacy PRIVATE rpcs3.icns update_helper.sh) - set_source_files_properties(update_helper.sh PROPERTIES MACOSX_PACKAGE_LOCATION Resources) - set_target_properties(rpcs3qt-ui-legacy - PROPERTIES - MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/rpcs3.plist.in") - else() - add_executable(rpcs3qt-ui-legacy) - endif() - - set_target_properties(rpcs3qt-ui-legacy PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) - - target_sources(rpcs3qt-ui-legacy - PRIVATE - display_sleep_control.cpp - headless_application.cpp - main.cpp - main_application.cpp - - Input/basic_keyboard_handler.cpp - Input/basic_mouse_handler.cpp - Input/gui_pad_thread.cpp - Input/keyboard_pad_handler.cpp - ) - - set_target_properties(rpcs3qt-ui-legacy - PROPERTIES - AUTOMOC ON - AUTOUIC ON) - - target_link_libraries(rpcs3qt-ui-legacy - PRIVATE - rpcs3_emu - rpcs3_ui - 3rdparty::qt6 - rpcsx::fw::ps3 - rpcsx::fw::ps3::api) - - if(UNIX) - set(CMAKE_THREAD_PREFER_PTHREAD TRUE) - find_package(Threads REQUIRED) - target_link_libraries(rpcs3qt-ui-legacy PRIVATE Threads::Threads) - endif() - - if(WIN32) - target_link_libraries(rpcs3qt-ui-legacy PRIVATE bcrypt ws2_32 Iphlpapi Winmm Psapi gdi32 setupapi pdh) - else() - target_link_libraries(rpcs3qt-ui-legacy PRIVATE ${CMAKE_DL_LIBS}) - endif() - - # Copy icons to executable directory - if(APPLE) - if (CMAKE_BUILD_TYPE MATCHES "Debug" OR CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo") - set(QT_DEPLOY_FLAGS "-no-strip") - else() - set(QT_DEPLOY_FLAGS "") - endif() - qt_finalize_target(rpcs3qt-ui-legacy) - add_custom_command(TARGET rpcs3qt-ui-legacy POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/rpcs3.icns $/../Resources/rpcs3.icns - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/Icons $/../Resources/Icons - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/GuiConfigs $/../Resources/GuiConfigs - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/git $/../Resources/git - COMMAND "${MACDEPLOYQT_EXECUTABLE}" "${PROJECT_BINARY_DIR}/bin/rpcs3.app" "${QT_DEPLOY_FLAGS}") - elseif(UNIX) - add_custom_command(TARGET rpcs3qt-ui-legacy POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/Icons $/Icons - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/GuiConfigs $/GuiConfigs - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/git $/git) - elseif(WIN32) - add_custom_command(TARGET rpcs3qt-ui-legacy POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy $ $ - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/Icons $/Icons - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/GuiConfigs $/GuiConfigs - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/git $/git - COMMAND "${WINDEPLOYQT_EXECUTABLE}" --no-compiler-runtime --no-opengl-sw --no-patchqt - --no-translations --no-system-d3d-compiler --no-system-dxc-compiler --no-ffmpeg --no-quick-import - --plugindir "$,$/plugins,$/share/qt6/plugins>" - --verbose 0 "$") - endif() - - # Unix installation - if(UNIX AND NOT APPLE) - # Install the binary - install(TARGETS rpcs3qt-ui-legacy RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) - # Install the application icon and menu item - install(FILES rpcs3.svg - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps) - install(FILES rpcs3.png - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/48x48/apps) - install(FILES rpcs3.desktop - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications) - install(FILES rpcs3.metainfo.xml - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/metainfo) - # Install other files - install(DIRECTORY ${CMAKE_SOURCE_DIR}/rpcs3/bin/Icons - DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3) - install(DIRECTORY ${CMAKE_SOURCE_DIR}/rpcs3/bin/GuiConfigs - DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3) - install(DIRECTORY ${CMAKE_SOURCE_DIR}/rpcs3/bin/git - DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3) - install(DIRECTORY ${CMAKE_SOURCE_DIR}/rpcs3/bin/test - DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3) - endif() -endif() diff --git a/rpcs3qt-legacy/Icons/DualShock_3.svg b/rpcs3qt-legacy/Icons/DualShock_3.svg deleted file mode 100644 index 4e66fe993..000000000 --- a/rpcs3qt-legacy/Icons/DualShock_3.svg +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - -SELECT - -START - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/rpcs3qt-legacy/Icons/combo_config_bordered.png b/rpcs3qt-legacy/Icons/combo_config_bordered.png deleted file mode 100644 index e7c8b1b84a3903b538f6c23403244bfe66074a3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30346 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 diff --git a/rpcs3qt-legacy/Icons/configure.png b/rpcs3qt-legacy/Icons/configure.png deleted file mode 100644 index c023e773df0f58522c2778c080a66016f9621665..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9102 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 S%-4SJ0CPtitvYh<#{UDA+}Je$ diff --git a/rpcs3qt-legacy/Icons/controllers.png b/rpcs3qt-legacy/Icons/controllers.png deleted file mode 100644 index af31011fb6266fb82d61e45b39cd7d8e458b4959..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24754 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

-*|m>L$?s6gV>iN8fD+;@lG=_GAUmO663NjJdqB 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 XhrH%4C>HRG63}CL)rUoLrf>c);)hRU diff --git a/rpcs3qt-legacy/Icons/exit_fullscreen.png b/rpcs3qt-legacy/Icons/exit_fullscreen.png deleted file mode 100644 index d2f5a46070ddfea0384fe5a80442d030be0348e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4303 zcmeAS@N?(olHy`uVBq!ia0y~yU;;9k7&zE~)R&4Yzkn1=x}&cn1H;CC?mvmFK)yn< zN02WALzNl>LqiJ#14v233kHT#0|tgy2@DKYGZ+}e3+C(!v;j(R7I;J!1I@ky!i?`H z?h*wGvX^-Jy0SlFWfIoX*vPM^2^5knag8Vm&QB{TPb^AhC`ioAE78kKEm1JhGte`% zymD<*3Il@xucwP+NJZS+Ylhtavty5aTs&v7oa-EufGI-OT|u`3yjEziZ@I`m!8!Tm zlF7}_t|W0t@h)7{9Ubt(SwQMZLo-Js2cK0-cQcDw#&`L5AKAD5IlpvIy#9`P2d49X z|NfT;=!}5|fA-r}e>=8$yUX`yo5SZ{{IqJby+w72aO>WeZ%&#s9GYj-G~fF7^cVkr zzKKoW^5^1B`#H9|IuHH(_on}RTp^o_Z6(XT@^AMq{{8eu87TkrX8k;ne4N$q2RF6P zH>@b1SNs3X_KSaiy*X+eb+7*0<<0Kqfj9Wi+tsKsDEKgNG$=7DFevzsGDZVoG$oLb z3x3Fccyse}`ufc$@0EYMbaS!$A_K+?A^Z*Ub8Fwc`I_E;kF|PN_1l}p>6Q9({(ilA zf8O7@d*A$i6!}w8gu(YK!;hagr=Pbg{q`bu!}8txEvw&0-cvtcSM+W6X7~22Zzk#t zvtBVK-2VIMjd6M<(}8Ki48CA;I>UyiEDn=F3elOfuV!30dkj4B@u znIVw~zFyqCes1maH@nmHKmU9CW^ekIJq6!-f%Q}3w=Lh_ZuXzUNZ-2oL-jlT^ZQD^ zo!y*2bMKkozi#e6pHcbyraiDi<=nh{Z@ty;)tmSD&-?r2jd}Wh)9RVuUvCx%s`!7? z|GeDYmYbLF>7TPJo~AyUEk{eB;aCFI7kzuV`TXsac+1~6Z!T8W^dGF@^`8?0M zc_p}MdQmxW21CXw_5+|M>95|4+fD7-nVZ4Z}EHni@MTp7mcI- zJ$iFIy+3c!2KhO)FMJprZbY90)^6`F{(XIO{=AEyRBHB@eS4wC_50P$>T}?(Wcjyi zo6B3i-!hJhEB@A=-v4glhV650>p|lJ0}cK!PoMWQ{&QEsO6_jjZ6Mcpy85}Sb4q9e E0FYuHGXMYp diff --git a/rpcs3qt-legacy/Icons/fullscreen.png b/rpcs3qt-legacy/Icons/fullscreen.png deleted file mode 100644 index f75326efa390c2fabc0260c10cb073259f6ca365..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3961 zcmeAS@N?(olHy`uVBq!ia0y~yU;;9k7&zE~)R&4Yzkn1=x}&cn1H;CC?mvmFK)yn< zN02WALzNl>LqiJ#14v233kHT#0|tgy2@DKYGZ+}e3+C(!v;j(R7I;J!1I@ky!i?`H z?h*wGvX^-Jy0SlFWfIoX*vPM^2^5knag8Vm&QB{TPb^AhC`ioAE78kKEm1JhGte`% zymD<*3IhY*c25__kczms*EVwPcpiD|p+#oL5e`|a(vKWAO?+~V-hBsrRcG&*Vih_2 zpks`R0LLRijz^k1Qnj{}2>7e)`~Np`^6sbQu?6!F96tX0uN}yv0}Oub{rEh;KGp2j z^ZYG;pWgnOJL9jw@0Zcn&fnW-Ed6$S{+8O`Wwp|2_DA+ro)%)*HiyB1;YJbDfgBUY z28In!SsHF;FflMDoaSWMe36BL>A*B$hP1_;3@i=0>I`Sxgc&#(qWu_V^r|rkfb<$j z`!XmnH|9`*DH@g`QOk<~kY3#=L{r{_ef4%+HEKH5z(C_~3 z_SL^9`hHsuOl{wQsqNd}_v-okYj)o|c8bNJB7T2uMFT^YUFGkePz}Ni4pV^%>Up5n zC~GtfM$^Hd$_pLr2k!6tn_m&Xul5&Pz~_&*#q+PEx{yCfM>lVK~Y+e9v zLmbcl|D$Yg?^Mo)4|QL^UA?{kR?7bmZny96kGLlW=$ R_|5~l$J5o%Wt~$(697y7K#l+a diff --git a/rpcs3qt-legacy/Icons/grid.png b/rpcs3qt-legacy/Icons/grid.png deleted file mode 100644 index 128e32adf969cd4a85d2e13eb699b43c83d1355e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9395 zcmeI2dsI?+yT>V~Y$h|y3a?qy#VHFjGZk-@DK$+RUb6B+YH8(_6d@GQu`ESpni{hb zDVY*QAu}(bK;?ybQ!xv?{bvc^D+pecgd83K&V|H5O5j5P2P_f}3k-zU-c6ns+<#qcvnB+wGSot0>KfUIR*`L>p ze9mtQ-oHD*>Q46awRuKPYgYL{e^xA89FVisDd_z7ixzMG(}azWyI!|D`NrYQQ5<2K z<0Y!|)Oy8o6)Akwmw{NX*xxrF{>MJ&o5-#Z8?HIJV3yY+COq!&*eo~Yni}%vYhtTj zKNa6m4l2`?_gpsn^76=myt$QNcU zdoD+$qvKX^!>RMiqM3NBwY>I~5>fvLzdS{IpO~tAi8&Ltvwn`k$?;>q<8Bx+MHD8r zDIK7GK(9 zy^+j+WggFL^b_2e0E=7AtTAa^bvGjWUg@><>qm(@>-^`AH+yncm7bZc)Dl&j!;;qF zoq_nSB53FwJmV*hsR{$*Weqb+ZGodNj)?oH{3u!K6ZFtlPxFYG(Ez)d>Ka7+eQbm3 z8V!A$@6tt`uLi7@regP+E0EPmh0s}7Z0A&v_dWg(Lz{rE%YIbom5gZ``X)8|Usk}) zc--bku+CHLK`b(Vz0D*IeL9wOk}kSWZ-+%q%@ljHA9}cCZ2lBj`DUKGN<{2gbOwUD z^B9tI8~L4}LSoC4A{{8P?fKwky$XI7Tro$^x`V*Qr;H|Ny}K9Z!%nfXcKH|xJ|}dO z65DH;bnfLeB*VKmEay0c0SL+TUGal zl{`M{fOYSra~NO8i$CN}XJXEql??`h8~PdpD+3wKWfWvWATEUCIeq8-sZ3U~lI(!B z>SW48k!)#%hB34mK*vtG6pNaujPy{B9gi24`dcxAo$b1ZZ#hg4ju5GAJd8i0Qw7$8 ztdQ7k^P7E1SqZe+U_pah5i^l8qcXGJTz@~YGa$qf&ERbRQf#J~i|0+2&GJCtJhe1_ zYO;0(%Im87)Vt5ZVM&I9FzC4orJdsoaYES_GL=G^zyyc=2n`XHRETc(ba(F+H2hh* zx&wVe@O&Y%A6>Koihk)6qIab!1ZeJ0b@N60HC;CroUIqj;$9yKX*lKK9yZ2GKrQ)F zKuuAtYO{>eo!ALZn2a81_bQJxf3v(uT#7dE}XqFvMj z1BL1%g?)wm@U6aHZi4~QsbfuxEkLqeAn7yEz2ycQj53|fK&hueudXaIS^CVv2n5xc zUVxSvoL;dMWUVtrf-Wz5wA3Ke*a*bh1$qHGk-cc2;Zjhz87LA|^&E81z#tQ1l(`Gk ztBaAfXdig#J~L30rxpxq zhp0u;X&ZfQclYbTrlyII3`r{wlXX5PSAVSprXFf0U)f zl3-U!)Oz}S0>yIHcNDUQN$iO}ZCOb4D*QkP=aAZ^8){bg7cBRkmYX0*Iul-$KMR|_ zkng!8zh?ppWtes}pqqC92WonN60OtVYLlKxT_=+pZ9HmE|3 z&q8@a?^>C6yGSmSWa}%N>@S_8mdd#MHuS_Ff}Dv+HOg{+3$VCb z?VeH({8(k%rmyPdFwI^5agAIMzDTqN^%gVLU(BmP-a4U{6Uu|be>!gX7jDtVV9Lqf zm*lZj84cTTg7O?ICWmMSg?fm1CfEzq~mASH32+=~bm? zJLa^Ypbq%*mcMK&SPSNw+2>`qoYzwBr?eAty{Y&LFKd_&+58iQFin$0l-C0C0u>wJ z61~gjpZ!c`4@9X>#L->i^*;lUAza3?=B1^Pu**vPm^2yu7AdjUp*{JzqX zQlJa2d0)w*DVbD`e+1aSZ`m|bC=Wz9R^OeqT7~Lr4-}`>{NZ@4sSX?YG)zFb$;JuL zKWR4rjEM15s4E%6oTu{PV}y6*@=(u~qj)@=?8aD!I(~2DY;Og7woQ7XCIu)K*~zU` z*WWuuKd>EClj&RpQqy=;;8HE>>Zyr}6>8foO40eDt^40^^>Pct#bt&k(G^wrQeR#! zc~mRbhFX()kWr)moL>L$BmenEK90QK7!(FEK67jKLnEfdi@(~g9#-N8S$(UpgZH1; z*H^XnpM+{x(oA>35a$!72ahviE#-nLS#9J=; zm-5w27tOYm9o-?cS6z|hAek&Z_>C%z|63juHptt;R$_+%0@N-@^HtZCjJ%uNmi4t{XkUs^!Q0529jXi2fW^ zqP8CH|JbHb(I&+_nzaJk6mWB~waHoiQBDW68!lWUY{i>UCC}x^i&kqDL~JcKdE69g zAgs9QE#CCr3qw#*=I<)&8-3y{pj{c5jE#QzJ(6MrIQO!9Srx;OP9SW81RauRbxRy zs1}oNY&Kch%>iT=Yj=!Tlr<3T+oj4~%p@4jBd&<_0I)l=a=DFnf`9n2u;~@ANZ24j zAI8^^_*CfZ3N%eR204GJNVjTyOGpP;H`PYE_o95KqZUzY6XnqfFE{Y5%P;`PQ|4Yia)%4Y^+0+Zkm}xVa`; zkR3t2yE;I$1%i<#0MVSEchtk&H{s268EUT;*bp`fvDBLA(&WyB4SY?n)j1Yi3vv^td6zzyD&Nx3{p6;D84mirx&x^ofn0wOJaxji^nEhp)7=H-4QzC<<-jyPGU!S z8ADk-Mu(`b3q)OIq1P7;Km0s4u+nFt5+55b4*SQS<-WauNxQKP2c_XlC zh5s{+aG)I95Ei?dX@lAuYc~u9qRz)Y{CKPz4D%U|oVs}5@fdC%MZ}yGw4Fh%RY*6~ z&I(-Ic6(a+=VeEIL5ETSNY->d>$j@Jo>S4WsqXzaz|vNA^3t$Oo8{2q?9V@ky8DjT zZdk@SB99W{f#~v0Wg{*_o-vJvKMw+o6V8R+WsucXB*P2CTxTHdwoZ+|d5GPZaaJ%o zH=PTu3(ijH5LPs?{GTOI8RF`sADSV6U_|OtGdZ%BNn){+aN|M#^(czAni4bYb|GHm zQEH*uI#vJ|nVDJJruEL2<&4IOnG!g%qj8@Ud?Gw3t)lT_m^1!LIKvand?Q9}k@X^` z2&Yg*RJkp}es2g-_P1#5TN11av69JfbbLL_UocQ;K;^J^LywPFsktWzk&hF12Pd6q zz^n`U#{m5QAm|Zi(jSFNZ7p@zSc-qF?<>w5x}}8eVOaE$EZe#yMdKHpiI(js@ejz6 ze#hfusj=m0s9#j zB7iM+46%uI-&R=IBl(l7rU8$NHN$-$#C4IE`4tfzo?vt#lt`rngztlMpHQ>^atEwm zx=H37;s_R=fn3;22X(Et+O53V)#{hP4*zD9=!P1o-N^D#A^mXS!4;5tY~&19xL1h&AoE=0Sb}FqHM{E5u(0nj zWA!k7)=v=PFH8SGr`n%?u27m#A&yX=^h<_(Fy)fnnuJ9 z#PBKM3PSyaBYfIU5r!MCOnkC?0b4~@r`Mqwc#e)2=|R)pUVq|;SzlrS43u(2B( zdLV9F08*@$bGSFu6ckg-5-Jva=hry--eN$EQXa^@2$z(qj*|F7;tN^y0;#|ClI5H} z3WPeUjFAfmYM(qv`XMM}zo(b4;njo+?w@_GBG8GJblqeDj!;HWsZxhUYlz+$W0x?#xIgFN%`vy zC+W@>j_zY}b7zuH?dpVlY!u~KC20}Il4{U41T0ap9K2B%W-8#+vr{LELvSC}4LzDE z?Y8i}5#SNC1b@w4&mB`#zGV`r=Xq2FN~PT-iFY{SoUa=<=m-|Gy&$^iOJRB|d;!=; zBePyrunt?9|BdHWY_%NmRILP_gcYn4{W4JGP4!GHsZc}TEtkxgd2Ia{*k`6`ctr|E zA5^nHJ?-0@vDdiJJSi29_&#I*0&zPm;Eu%$>p-aRmtH=&qzoOMhlPJ794hhHb52C- z_T?mjAkTTJx{`2Ly%aX=qnL;LH-=U& z9iza)?NA5u3m16^v~AEEn)#XP6Kc}@Tou_3h~GzUrz<}766P$vQ&B{wN;y2bs`QNP z;dVss%C^a}rGxR{ljQ5buT@uhZsKb+;w1n)B&yM={sV7VF|?6LoF}kS;|_t?gjVyb z!aYjYsl=Z@(C)qzxTt*3Pt*F!ul+RoU($Ol;>XZW7>=MD+xBhvo zq;$=e{b|*)KJ4WcaZYj*u9cm}hd$|p+ZxIpBIR=P^#WE%d>1X*4<{sIA25##6=9mA z*;XTaAl&mbxGI%YNBo^wt=Qv6$vgp9ElX3cnWs_F4eGh5|s!0RYFiR<&y*Qkc!y=Fdca``zTnzzy0zj9kR9ynQ_} z9fqiX8allQh*^wWGGTe{XYHX!vN&N!BLE{Tn#Ll~B3S0@32O-s#J5n%K>+6EdT;gE z8ynm&OM%NdLG{CU7DW<$(vF`X~L6+Ug##7O(wH6WU z_TP57((2)J`sM09(Zv5|;hlVBjo79ySE*5)k{-#t9~+N-+G#w*^ebu^ z)C9}nxEw3*OW*By+?Jpes*|c$-cn?$BS1xVjfK6WRCW8Kb5*X43~y>~#4vXenjTdW zLuT;fT8-5Cf!9>3Zr!1@l!1d@-LttA_q?U0>|VgQU1>XUh0`7F&w0SckXAJ})IC3C z#;-YSl*HcG{y(Di2S1pZk==DS25$}eoZzcvf(1kAqW*_8ZDL}P1TsL0f=BAO9vjUo zq@AjeR+0;N59=-c(nh0Yi@1Je*ty`?3oSE-MTOz{@aQWj%s^DM-?*GizB}@EqH?(A zII#Z4fSB}7R|m5uXgN~#w>RSXyetpSkBu(F&IZTCyxe|GK>N4gg>o$>^xVkTc1Oor z#}aHfd=Z4U6gtnSwJdP8i0a9T!OsVMSJ`3;nNePp6iy*uWb7!z&H~5YX{<%P<)|e9 z_#6b!0SH}!Hb*l0W-)L8Am=4GV*o_!XKtDcz)c5m0D!gU4d$T%KYA`7iI-3Kk6V5` zu|EeUQU_e7^EMCk-7r?XN*zf3cu|N(@q?JmPZ`5CtM)}qXOqm6UmmVmncpxK>`Fr2 z<>9=G7b2&MfdZR@@m`9cN?QuX5IpXpGg*7dm+eZ}JHdTB2+g-YPwUqcohivk@j$}( zpn=rgjg);57J<-WZH!=v?B>Ys>lsXT85Rph?^0DyQ;%!q`T)9T0H&)3fWC87Ws1KU zhe{UG4X<5I{B@+2@Sc3E-e=En#2p{6^Aq;WI=27BTO)3AqYGw54g~ zr_!3n6ddKJ_;uM4GLs?nX0U7ybT6T$DS;r-G9!;$h2Ujks2)|{W5T6DoRx_iZXJ0o zWG0$~C>^=nGRZk~RGylo6((lffw(bw3o(#}eni%h7`&sQpkoCYQ!yemz}7x1u&2Y< zT!wVz-xNaCdy)-y<9B4{U()#D#6Wg&gXPiq+BLVjS^?wYwH)@iKlG?KoqH%v{8{Su z_6Rrd`0BbW=WaaFJ~4}`@&qp12(PO2s|Urds+&4qaE?u6&|?Cl(4Whu?6OR%IUL>1|u0;OhqMJK5pxf|l;31+K53DWSl;M>or`i@@lK z5r+}Yd!C!!_3?+MN=kk|9d7~peulXQ92D&qdpIP2jE-Lh`q)v5B&oZw*tgn>rbget zM{D%+)c!73_r%~ABmcZ#RE_hd^3kh-*}`W?D3xuy++57gwM_sY#u3xY!)|W4>3jxW z8Cjwo9uFim{{h|#O0kitoc>&^Gsx*UT}>>YgiL5NG#q3s+6?pIDtT1-0-Pfg>9)-Mu9c!byq{gq;2TfyCt1;|!Jzo)u}$pxe6?^s`va$*nKZ0m098?HJd|PA>xttFLrH{EXRumGLjVz++=sU-wQk(T&Ux&AY_*4QRQ^KF^DVoVIgHwF+;pzeFNe zTnfykC_O<1PDquq)q}c(Pg!r2Zwsk8lM=g&%O?DaD`5{!Ni21)F48oY?fVAbTT8S} z6d;ef0x3{9_nMpU5J?~Ws4LuTa8YY+3(QTffCwE<%g0a5TK^tZ3bnl0ib|&&p)$sJlP8nn( zmvTdB4U-ThsQ0FwJ)AI?@$(d4UueR|GNnSdl|50Di(+5=YU8uWs);91Ff&1%OrieV zyFut|F&7g0WM9amazEjZmGux+%K>L;msY5wC6=+c3ix@f5B1hnM@ zb>7E$gGQkJQ(2KG{H_+x9L-i_s8e+DrpsjFw8rq#Pq?A4)ywG(niU?PqP&o!DK>kg teWzmy=G&{&zZWtQ*X@r?#*Zd;&V7!f@O@-72-d+yj;v^n&|{{deVQ*8hM diff --git a/rpcs3qt-legacy/Icons/open.png b/rpcs3qt-legacy/Icons/open.png deleted file mode 100644 index 354171345af7f41ba6ccc8f0af0a7dec58ea1886..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11299 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 diff --git a/rpcs3qt-legacy/Icons/pause.png b/rpcs3qt-legacy/Icons/pause.png deleted file mode 100644 index e5a22b88ca6275eb169ddcd3ba5c77b5e28f4849..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7524 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 JwY#Fu{SN>mG*18k diff --git a/rpcs3qt-legacy/Icons/play.png b/rpcs3qt-legacy/Icons/play.png deleted file mode 100644 index 8675cd3b6958539181634fce99b135e4d35ea2f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8029 zcmch6iCfdh_Wp!m&;sJp3Q7@F7Fz|PAk-iPT!9c!R*kYKhy)6>%A&zQl&jY3!lr;M zA=Z!(5U3iUf+1M1a;YLTF(9FUb)yNgR1&~I$nSi-zrW$j^Y};(GiTm8bI$vonOxlK zzsLBCRbL>4jD5U!1|p;fzv`id1o%&N@`VQe(?8}#^+M=Qj^W(l1@L*%58ek(AoShe z*gu@>8o^yKu1wjrKP4zRHYNS=@uTR6!}O#Shoqw^R?ZGYhfP~w^xTa>XtmsDr&sXF zh|z)cKVy$FhkyL?{7r`t%}-x?dE(4p?p<^H_kY_Q@e1A;6}tS6H7j4gzs@Rl%g@V* z%l3Ww)n3ze3%_Xn(rD?I0^7AeeX+Lurtzchv`Ov7oU_}UCa<-WeasX`ROse*#J+Fs z8uu7J)Z1VLu>P-pjQ;$VULcHS{3y56hREGU+3XvQ{x$qP)`(N7+}@fgceN;OEq_ie z;w4C^ifYw8)f<&4B@|q`-ix+#Fat}(i3SGcW&5%qt|60`=oNoN*Qp6%; z#L(PC_S($@MYb*f4QNw6M(+(a z*$7?kTSd=N)e6^B)0SH9bmcFXCPmy$mgqQ6(FtD_$GW%F%828;WM2vdMXN+Z~mV7w7txkE> zQ?!1jQpFV>Ilc!W0yj-Dd%8Muups=W(!HZQ_U0^z&-m+l0c2h5O_$j|`|v#5mI)Vu zJm-}#_@E)#MMk-V+KgV5=opjpgU)@sn&>j+zcxrs14l#ljyvpkS%{=Us)kI>T>ougktcufCZ_Z3xIyhm>Q$dQ&sdToKve3E@`z5FM+SoZW% zQ>Bws=Qm>O2!A;8qW;~Epo46kV=aUHil=H2235=SP+LV+(9BgzkbA$_US{>f4$?-M z+hTuneU^bhJC)ERvaQ!uP`A81lsiRf$YQ}pe)gYlu})1zRZ4N<>pN|ObZM}grJiT9 z3{6xBp5ohfrt_Z$mZ$%cL36`-I;!c|z!P58Evwa;ZBF?|vysYwh`_q26gQ16oLg^E z>_Ta<(?hE5kp?V-bPCC4^hS>_0f$IkXu`8$sU$>ErTP$~Lx|LgCc5KwERyNX)rK$T zaSgP#yIzRYj)~QN~JCm8xoINBbhgQN!o@s}}PIG%=cc z!gxJas1x$5)lA-ZDlZx=Ro+2JScr?#$aZfhd#f`&M{VQ!tWa$B7bh2Zl(5-Kp%;qVhHs0a6+ibM_k~b#{xL$?qfb(fS4UrP^#<&#IuLPYEFg>* zEj5(h%AdxgxG(W-6SQmd>tuHxxpC#xC1}+{M4W@|z*$((wQrLUiav(SsOtT%&IgYh ze8`LMdv$jadh#10%Ezc;pFMne=*cT@bJw8V&k>QWJbi{DF_0EuHLxxknMJCPWgaB0 z6WJt|mANcz^EN|IhL{4_bbNK(;H*uQ zf7|JUh^;?e+8wd%1j1 z`+g37O33kb^sccRH$W2x1lCw+7C)VLNqSRpv10(C%y!hRRgO|>|#;N^w zPYF2MGsIDZI)0JqI$G4|j}f5fQ#0gTpuE#2Q_QHERmE>l<5(*;p*R%}_18O3$W#FfqK_6{3v zBi9x_uwK|rBp~A&jcaO%##_EdHjg9VSm%i-?&ZMs=l-*N2D!}&bt3}0Qf)33^O({V z9bP+7bPVcV-{Kmc=TQ-n=i(jlrM?*+&)OZXJy;{tGvx=^wlBtCK7;doQ(b60$c|s~ z-FLNl$ZRQ|^(0)|Ut_u;Vcktry9-9R%PgEHwR-U?k_%)ePz+Sd`TrmeFU0YSp6D~M zIe}pQO7lA4T>{#2M>G8$xx8fl+r;bdZ4m*+*@mU#>-_Avy;5xb-t3p$q_;z!pWL@3>Kh}Z^t~kc&z(mB{_5m@3moe)5mixS>&_%ddZne( zLP$K8j~ROL*tXEP=gy;gKcMN=0I)7p#~b<<+c$TwBnbdYb!4fj7ulx(q+L2Y~?GcHXC-zSL@XJ?t&l)qty%BcI@K9cBZbV*> zkIPa$+Cvyrr}^Nw(R|xlS>taJ%k)=uAuD5^#%SO=B)*S2-^w$3CE`8|%OO1XZz$zR z31@b1M&6G`-lxPc(r6oi$$>JhjGLOxCkj%2kZ+P5dp?hAdkJ&?3ah9G>!>i2u^kHV z@)exts_@iCD3<>S(@Tmrn(G6NT3u5&L&%v_>UzdFC zW_0-)%t}`G9V~uMsi%OrP|mh>gY)+(>7+HI`8|G9Cg}VVWVNMc7J3K}cJ_@P;QFXn zMkvs!W^zGh?qE5)_`&*xM&KwuDb0k+}g761wFgMSG~Wj1=n^NR-6hkU!-*&t>_5=_i}LWR@3#tLZMPgR8=uc z5bqhXY7m!7mePko7NdM&fNRNjX){dv1!4ThpRIn`Pe1}U+3>HhJ|;H@fl|)+fZwr@ z_QjEK{3$A+^*zVO1 zn#`n!|H~ zcZnRBAZ`g@J)9QM?9ALanj5b}*~l3~y1sdx)>>re?sC9oVKfG_TA2}hM91po@8Q2& zMkK)cx9VDxle{CnLEda~7DWBW2<1Nsu{VEUZBYn78RX9#%Kb5P$i0b|6)^4xHFq?d zs0TBtRcXu1D*UM#{8JrQ&k}cHCgoso`{FDY*(^?N@lK1(^Z=J_S{Ot^7bf=r6nH@K z;)OCpP-x69A1rh6Uv1dNg45a}(quNfm0yb}i$8ccst{1f1DOHfr^3&TFDr1cK4_1mz#cE&D}@h7Bo`IeF+t)%I0c3>i!+gx z>!eQk(H9N_t>!ns_$|c?dZ)wTB`l2JS=&UQ`fW&+#^%2YFsG4}#^nfwepyoR_C^L>jB1B8lpn{+w^~~oDxeKOD zW=e&JH!tiaL9k0sU6Q0rR?|f>OE_DwIkTCi9_4A29O#!ZHucFCj1lInE%Mpdd%UMi zICroyzbac&GGQGPzX0zO%bc%*V?w0GX1M71dv{SzEO-7lSk4DA>mB?|E18nt7p=lj zKdNnwO!iYJwZ$*OSl8#4qE7Wmn0fXN1X=QTvyF%>+xIBPo9F_tRwWkkdToXm5ko(J zXxRQrr7DZLXcZ0W!u=!tpk`&MS0Zcr1B)B+(N<>PU}KsJBoFl0fy{CL zNlW^;)@mJgM*pORFt(q~1L2UIbV9@xpXaZ}{r_Lo%UWQz=S}BkPfQei-i*u# z593}g{3#FH*-W!--xtMqu;ZUOZnos}4*p5mQ+)!3dJ{i`2{7e`L!SXnI0U6WQ`_6G z8?pT#{>BXHbdXk4Q4sZcuky^iDQbfr=tP-jK9cXq@8zEAaL97T5-4_PE+t;`RQ2oILh=kB}q7Ci=;kKqLW$8ynZ^JS^iHS0ahj@l=1HN-B1uYFqVGS5^EN?9W8F*c~^ZLDVb{NHxPYBTxyF> zo)XB2mT$B#;sn8tRPD+%-3>*u#D!k@HjG>z1JeweH(L~@`PEpPX2LzsO3)=sA4*gt z2&P6O?e9-W4Z#!hcI#wPz%ZBbTpW5gnf$qE`L;cik=GXcl-jz@{6WD6De8jTRjaSF zi|@6bvujsv$9UsDRBJ%XOemUkxiFcy5PY!O^lXuYe4=OQMuanLQqADM<%mmd}9=vUsFl8rFJ zEvO#<`Qm7XZmsNY>()hPM2y{ohrAk?$@U|&o^ltNVP>ZKQ7!>ts`+wx6&e&6?Kx?o3zNCQ+%)$fv`Y^z05F7}G_`9qvy&D#nKV z%y?LTRpCx@cdcc!HbI@Oik+?;k@zUy3D}bv_bgXog1O(``&eZ<*FL{r+ieA>l3nLN z(y|Sf2?*F=!#NCY2_g0)HN$fa%<)O7ykj&oZapCH3mvj_t&~b46#M9X`p(6mm)|#U z(0(U!lYN?VnER~GPOu5>S_^iXZ;dr)7kJ`HwSvZR_cbDC*a*)d&e!mpiAtz=m_w!XCm4J#yLC4$a(b2S?K_ju7*9 zUbr;A^;k&7Fp1aHIL=IloXGqKZ*myrHU}}U zCb(xYd1Q8){{~yu1@J#m#h&bK)vC*~a)g=3TP*i)#Is(^Ck5E?As6om2FDEcuLaAX zGE8TqsFz}8cz{f_fu@ZqJEzm6>}2@Tw%(t>!=*h}9>nw?<|kL`@VaiV00b&^2lj^ zY0$t}R_JjLGI28j(NzGnQI#zWsGj_U!x;TaUGqfm-B!y=6K?ascqkcTeEN)Rc;q>t zuwZS`9Zuc1wYmN@0!u7*{0nna!S0e z6nb=N&g_P;X4iP^fy1l{HvhE0#{gs466W%!n%2@ZO5@l9On^HdG*1lWKleA_wp%x8 z&V)i3oNAKPk-3X04VNMlhYY({!Dlg=JeMK+o#24|p86sRLNYoHj>TZ0tP3dTwGsb4oh>5ne=9EOu2ht5t7 zT?2M4EpVMZ^Bk(vnJe zG!yHyl*b!xkM{)g)uM0j%SU`!bZp9Q03qkc7ir;R%`tj);f9-^7lhwPLWaCjd(28AOX($Ns1HoURN? z(c_z~Jkuy-gyQ23CFc~k=)LeR8086{v4iMU+@t3R_t9K%L{E5g5bRR8DXzdlY^^nk z@`f?Ku+#94-5zLN6zcx2^kj>!IM_53f_eETcehv^w5E{0UBffDCyJQx`j-IB5PE zrs`^5Zh z!v=qUgZCsi+iBNocT_WH6AoA+_gM6InmSFFs@R}d0D1EHRj^YTs-r3}`L8QFG=rPL z<2{?I#qIslt%`B!RL7JEZM%%)xIyGKE!(!a+tudHDGj0m39O`)9V7>lb!~Ye6~PUg zmDaSr{QBTE*uHoMSG)}G=~grO$b@O+VCWlqeem#P%YaGSUU=<|)b{m`&QAuq4-akr^cZX_(Ubmg1z)iao&}C$ z>)&)jmjR5fUv*!Y0eQ!QF8OjJR7u`*?^*61yV)r`w79H4g0or;1MNpL(yK=^iUP16 zn)O$DdhiN;;oe^HcWee;2KoRYWe_3FM>w3_!Jaj znJ^NvV%QqREx@;_YK}6)xboKY{+QVa?8S+wjyDz8NKK_qfr)S5{AS|0VI?XYWGdhh zp25Vvc&LHTe~v0X1ouz(jLv_x3<=q|sE^99tlD8+T=h|Af;C@_>t0Dp%nOP;KtDvJSbp}Fr=guaikg#bm+QAL7JcXYy}jD zYP+QY>vLD66)WDsO9m22__(Me?z(hIzvRgx#1Ifzw6PvFj`MuDo;f>GJjl0!D(_CH zftzC8Vx%5YKUG~+JrMeTOd}&vnD}1-ehppP7}IW-^f1kF#y)_XN~!G&Zi*!htgnCUsfixAzl78c47@K>by%p!!v6?v^4&~Rzz~fJQ!fX9z zLg3m{H22zowWi8ckwzV^{IPY(e(_Abzxv2)7mRU)ch-0c4*?r|qJ=O1|Tz zw+E+NA=W0rnyAj;-`xFBzxKB2fTh?uA`6vJV07Q~yq+-Q;S;M2gI z=oGNu^|t73F?*rWx&?+$jMP2M;%SC@ld!k_+B~HdyNJuk(A|PByKcfW<|#&Ob;yED l1s?t1{j@o6y!R{%_i<}!;$iy4pV-1ayZm?7ZI3$p{{Z1UpSS=3 diff --git a/rpcs3qt-legacy/Icons/refresh.png b/rpcs3qt-legacy/Icons/refresh.png deleted file mode 100644 index 425e147eccf250ee5db5eb47a3cff8507a6dad14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13725 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 diff --git a/rpcs3qt-legacy/Input/basic_keyboard_handler.cpp b/rpcs3qt-legacy/Input/basic_keyboard_handler.cpp deleted file mode 100644 index 8a7a5773c..000000000 --- a/rpcs3qt-legacy/Input/basic_keyboard_handler.cpp +++ /dev/null @@ -1,347 +0,0 @@ -#include "basic_keyboard_handler.h" - -#include - -#include "Emu/system_config.h" -#include "Emu/Io/interception.h" - -#ifdef _WIN32 -#include "windows.h" -#endif - -LOG_CHANNEL(input_log, "Input"); - -void basic_keyboard_handler::Init(keyboard_consumer& consumer, const u32 max_connect) -{ - KbInfo& info = consumer.GetInfo(); - std::vector& keyboards = consumer.GetKeyboards(); - - info = {}; - keyboards.clear(); - - for (u32 i = 0; i < max_connect; i++) - { - Keyboard kb{}; - kb.m_config.arrange = g_cfg.sys.keyboard_type; - - if (consumer.id() == keyboard_consumer::identifier::overlays) - { - // Enable key repeat - kb.m_key_repeat = true; - } - - LoadSettings(kb); - - keyboards.emplace_back(kb); - } - - info.max_connect = max_connect; - info.now_connect = std::min(::size32(keyboards), max_connect); - info.info = input::g_keyboards_intercepted ? CELL_KB_INFO_INTERCEPTED : 0; // Ownership of keyboard data: 0=Application, 1=System - info.status[0] = CELL_KB_STATUS_CONNECTED; // (TODO: Support for more keyboards) -} - -/* Sets the target window for the event handler, and also installs an event filter on the target. */ -void basic_keyboard_handler::SetTargetWindow(QWindow* target) -{ - if (target != nullptr) - { - m_target = target; - target->installEventFilter(this); - } - else - { - // If this is hit, it probably means that some refactoring occurs because currently a gsframe is created in Load. - // We still want events so filter from application instead since target is null. - QApplication::instance()->installEventFilter(this); - input_log.error("Trying to set keyboard handler to a null target window."); - } -} - -bool basic_keyboard_handler::eventFilter(QObject* watched, QEvent* event) -{ - if (!event) [[unlikely]] - { - return false; - } - - if (input::g_active_mouse_and_keyboard != input::active_mouse_and_keyboard::emulated) - { - if (!m_keys_released) - { - ReleaseAllKeys(); - } - return false; - } - - m_keys_released = false; - - // !m_target is for future proofing when gsrender isn't automatically initialized on load. - // !m_target->isVisible() is a hack since currently a guiless application will STILL inititialize a gsrender (providing a valid target) - if (!m_target || !m_target->isVisible() || watched == m_target) - { - switch (event->type()) - { - case QEvent::KeyPress: - { - keyPressEvent(static_cast(event)); - break; - } - case QEvent::KeyRelease: - { - keyReleaseEvent(static_cast(event)); - break; - } - case QEvent::FocusOut: - { - ReleaseAllKeys(); - break; - } - default: - { - break; - } - } - } - return false; -} - -void basic_keyboard_handler::keyPressEvent(QKeyEvent* keyEvent) -{ - if (!keyEvent) [[unlikely]] - { - return; - } - - if (m_consumers.empty()) - { - keyEvent->ignore(); - return; - } - - const int key = getUnmodifiedKey(keyEvent); - - if (key < 0 || !HandleKey(static_cast(key), keyEvent->nativeScanCode(), true, keyEvent->isAutoRepeat(), keyEvent->text().toStdU32String())) - { - keyEvent->ignore(); - } -} - -void basic_keyboard_handler::keyReleaseEvent(QKeyEvent* keyEvent) -{ - if (!keyEvent) [[unlikely]] - { - return; - } - - if (m_consumers.empty()) - { - keyEvent->ignore(); - return; - } - - const int key = getUnmodifiedKey(keyEvent); - - if (key < 0 || !HandleKey(static_cast(key), keyEvent->nativeScanCode(), false, keyEvent->isAutoRepeat(), keyEvent->text().toStdU32String())) - { - keyEvent->ignore(); - } -} - -// This should get the actual unmodified key without getting too crazy. -// key() only shows the modifiers and the modified key (e.g. no easy way of knowing that - was pressed in 'SHIFT+-' in order to get _) -s32 basic_keyboard_handler::getUnmodifiedKey(QKeyEvent* keyEvent) -{ - if (!keyEvent) [[unlikely]] - { - return -1; - } - - const int key = keyEvent->key(); - - if (key < 0) - { - return key; - } - - u32 raw_key = static_cast(key); - -#ifdef _WIN32 - if (keyEvent->modifiers() != Qt::NoModifier && !keyEvent->text().isEmpty()) - { - u32 mapped_key = static_cast(MapVirtualKeyA(static_cast(keyEvent->nativeVirtualKey()), MAPVK_VK_TO_CHAR)); - - if (raw_key != mapped_key) - { - if (mapped_key > 0x80000000) // diacritics - { - mapped_key -= 0x80000000; - } - raw_key = mapped_key; - } - } -#endif - - return static_cast(raw_key); -} - -void basic_keyboard_handler::LoadSettings(Keyboard& keyboard) -{ - std::vector buttons; - - // Meta Keys - buttons.emplace_back(Qt::Key_Control, CELL_KB_MKEY_L_CTRL); - buttons.emplace_back(Qt::Key_Shift, CELL_KB_MKEY_L_SHIFT); - buttons.emplace_back(Qt::Key_Alt, CELL_KB_MKEY_L_ALT); - buttons.emplace_back(Qt::Key_Meta, CELL_KB_MKEY_L_WIN); - // buttons.emplace_back(, CELL_KB_MKEY_R_CTRL); // There is no way to know if it's left or right in Qt at the moment - // buttons.emplace_back(, CELL_KB_MKEY_R_SHIFT); // There is no way to know if it's left or right in Qt at the moment - // buttons.emplace_back(, CELL_KB_MKEY_R_ALT); // There is no way to know if it's left or right in Qt at the moment - // buttons.emplace_back(, CELL_KB_MKEY_R_WIN); // There is no way to know if it's left or right in Qt at the moment - - buttons.emplace_back(Qt::Key_Super_L, CELL_KB_MKEY_L_WIN); // The super keys are supposed to be the windows keys, but they trigger the meta key instead. Let's assign the windows keys to both. - buttons.emplace_back(Qt::Key_Super_R, CELL_KB_MKEY_R_WIN); // The super keys are supposed to be the windows keys, but they trigger the meta key instead. Let's assign the windows keys to both. - - // CELL_KB_RAWDAT - // buttons.emplace_back(, CELL_KEYC_NO_EVENT); // Redundant, listed for completeness - // buttons.emplace_back(, CELL_KEYC_E_ROLLOVER); - // buttons.emplace_back(, CELL_KEYC_E_POSTFAIL); - // buttons.emplace_back(, CELL_KEYC_E_UNDEF); - buttons.emplace_back(Qt::Key_Escape, CELL_KEYC_ESCAPE); - buttons.emplace_back(Qt::Key_Kanji, CELL_KEYC_106_KANJI); - buttons.emplace_back(Qt::Key_CapsLock, CELL_KEYC_CAPS_LOCK); - buttons.emplace_back(Qt::Key_F1, CELL_KEYC_F1); - buttons.emplace_back(Qt::Key_F2, CELL_KEYC_F2); - buttons.emplace_back(Qt::Key_F3, CELL_KEYC_F3); - buttons.emplace_back(Qt::Key_F4, CELL_KEYC_F4); - buttons.emplace_back(Qt::Key_F5, CELL_KEYC_F5); - buttons.emplace_back(Qt::Key_F6, CELL_KEYC_F6); - buttons.emplace_back(Qt::Key_F7, CELL_KEYC_F7); - buttons.emplace_back(Qt::Key_F8, CELL_KEYC_F8); - buttons.emplace_back(Qt::Key_F9, CELL_KEYC_F9); - buttons.emplace_back(Qt::Key_F10, CELL_KEYC_F10); - buttons.emplace_back(Qt::Key_F11, CELL_KEYC_F11); - buttons.emplace_back(Qt::Key_F12, CELL_KEYC_F12); - buttons.emplace_back(Qt::Key_Print, CELL_KEYC_PRINTSCREEN); - buttons.emplace_back(Qt::Key_ScrollLock, CELL_KEYC_SCROLL_LOCK); - buttons.emplace_back(Qt::Key_Pause, CELL_KEYC_PAUSE); - buttons.emplace_back(Qt::Key_Insert, CELL_KEYC_INSERT); - buttons.emplace_back(Qt::Key_Home, CELL_KEYC_HOME); - buttons.emplace_back(Qt::Key_PageUp, CELL_KEYC_PAGE_UP); - buttons.emplace_back(Qt::Key_Delete, CELL_KEYC_DELETE); - buttons.emplace_back(Qt::Key_End, CELL_KEYC_END); - buttons.emplace_back(Qt::Key_PageDown, CELL_KEYC_PAGE_DOWN); - buttons.emplace_back(Qt::Key_Right, CELL_KEYC_RIGHT_ARROW); - buttons.emplace_back(Qt::Key_Left, CELL_KEYC_LEFT_ARROW); - buttons.emplace_back(Qt::Key_Down, CELL_KEYC_DOWN_ARROW); - buttons.emplace_back(Qt::Key_Up, CELL_KEYC_UP_ARROW); - // buttons.emplace_back(, CELL_KEYC_NUM_LOCK); - // buttons.emplace_back(, CELL_KEYC_APPLICATION); // This is probably the PS key on the PS3 keyboard - buttons.emplace_back(Qt::Key_Kana_Shift, CELL_KEYC_KANA); // maybe Key_Kana_Lock - buttons.emplace_back(Qt::Key_Henkan, CELL_KEYC_HENKAN); - buttons.emplace_back(Qt::Key_Muhenkan, CELL_KEYC_MUHENKAN); - - // CELL_KB_KEYPAD - buttons.emplace_back(Qt::Key_NumLock, CELL_KEYC_KPAD_NUMLOCK); - buttons.emplace_back(Qt::Key_division, CELL_KEYC_KPAD_SLASH); // should ideally be slash but that's occupied obviously - buttons.emplace_back(Qt::Key_multiply, CELL_KEYC_KPAD_ASTERISK); // should ideally be asterisk but that's occupied obviously - // buttons.emplace_back(Qt::Key_Minus, CELL_KEYC_KPAD_MINUS); // should ideally be minus but that's occupied obviously - buttons.emplace_back(Qt::Key_Plus, CELL_KEYC_KPAD_PLUS); - buttons.emplace_back(Qt::Key_Enter, CELL_KEYC_KPAD_ENTER); - // buttons.emplace_back(Qt::Key_1, CELL_KEYC_KPAD_1); - // buttons.emplace_back(Qt::Key_2, CELL_KEYC_KPAD_2); - // buttons.emplace_back(Qt::Key_3, CELL_KEYC_KPAD_3); - // buttons.emplace_back(Qt::Key_4, CELL_KEYC_KPAD_4); - // buttons.emplace_back(Qt::Key_5, CELL_KEYC_KPAD_5); - // buttons.emplace_back(Qt::Key_6, CELL_KEYC_KPAD_6); - // buttons.emplace_back(Qt::Key_7, CELL_KEYC_KPAD_7); - // buttons.emplace_back(Qt::Key_8, CELL_KEYC_KPAD_8); - // buttons.emplace_back(Qt::Key_9, CELL_KEYC_KPAD_9); - // buttons.emplace_back(Qt::Key_0, CELL_KEYC_KPAD_0); - // buttons.emplace_back(Qt::Key_Delete, CELL_KEYC_KPAD_PERIOD); - - // ASCII Printable characters - buttons.emplace_back(Qt::Key_A, CELL_KEYC_A); - buttons.emplace_back(Qt::Key_B, CELL_KEYC_B); - buttons.emplace_back(Qt::Key_C, CELL_KEYC_C); - buttons.emplace_back(Qt::Key_D, CELL_KEYC_D); - buttons.emplace_back(Qt::Key_E, CELL_KEYC_E); - buttons.emplace_back(Qt::Key_F, CELL_KEYC_F); - buttons.emplace_back(Qt::Key_G, CELL_KEYC_G); - buttons.emplace_back(Qt::Key_H, CELL_KEYC_H); - buttons.emplace_back(Qt::Key_I, CELL_KEYC_I); - buttons.emplace_back(Qt::Key_J, CELL_KEYC_J); - buttons.emplace_back(Qt::Key_K, CELL_KEYC_K); - buttons.emplace_back(Qt::Key_L, CELL_KEYC_L); - buttons.emplace_back(Qt::Key_M, CELL_KEYC_M); - buttons.emplace_back(Qt::Key_N, CELL_KEYC_N); - buttons.emplace_back(Qt::Key_O, CELL_KEYC_O); - buttons.emplace_back(Qt::Key_P, CELL_KEYC_P); - buttons.emplace_back(Qt::Key_Q, CELL_KEYC_Q); - buttons.emplace_back(Qt::Key_R, CELL_KEYC_R); - buttons.emplace_back(Qt::Key_S, CELL_KEYC_S); - buttons.emplace_back(Qt::Key_T, CELL_KEYC_T); - buttons.emplace_back(Qt::Key_U, CELL_KEYC_U); - buttons.emplace_back(Qt::Key_V, CELL_KEYC_V); - buttons.emplace_back(Qt::Key_W, CELL_KEYC_W); - buttons.emplace_back(Qt::Key_X, CELL_KEYC_X); - buttons.emplace_back(Qt::Key_Y, CELL_KEYC_Y); - buttons.emplace_back(Qt::Key_Z, CELL_KEYC_Z); - - buttons.emplace_back(Qt::Key_1, CELL_KEYC_1); - buttons.emplace_back(Qt::Key_2, CELL_KEYC_2); - buttons.emplace_back(Qt::Key_3, CELL_KEYC_3); - buttons.emplace_back(Qt::Key_4, CELL_KEYC_4); - buttons.emplace_back(Qt::Key_5, CELL_KEYC_5); - buttons.emplace_back(Qt::Key_6, CELL_KEYC_6); - buttons.emplace_back(Qt::Key_7, CELL_KEYC_7); - buttons.emplace_back(Qt::Key_8, CELL_KEYC_8); - buttons.emplace_back(Qt::Key_9, CELL_KEYC_9); - buttons.emplace_back(Qt::Key_0, CELL_KEYC_0); - - buttons.emplace_back(Qt::Key_Return, CELL_KEYC_ENTER); - buttons.emplace_back(Qt::Key_Backspace, CELL_KEYC_BS); - buttons.emplace_back(Qt::Key_Tab, CELL_KEYC_TAB); - buttons.emplace_back(Qt::Key_Space, CELL_KEYC_SPACE); - buttons.emplace_back(Qt::Key_Minus, CELL_KEYC_MINUS); - buttons.emplace_back(Qt::Key_Equal, CELL_KEYC_EQUAL_101); - buttons.emplace_back(Qt::Key_AsciiCircum, CELL_KEYC_ACCENT_CIRCONFLEX_106); - buttons.emplace_back(Qt::Key_At, CELL_KEYC_ATMARK_106); - buttons.emplace_back(Qt::Key_Semicolon, CELL_KEYC_SEMICOLON); - buttons.emplace_back(Qt::Key_Apostrophe, CELL_KEYC_QUOTATION_101); - buttons.emplace_back(Qt::Key_Colon, CELL_KEYC_COLON_106); - buttons.emplace_back(Qt::Key_Comma, CELL_KEYC_COMMA); - buttons.emplace_back(Qt::Key_Period, CELL_KEYC_PERIOD); - buttons.emplace_back(Qt::Key_Slash, CELL_KEYC_SLASH); - buttons.emplace_back(Qt::Key_yen, CELL_KEYC_YEN_106); - - // Some buttons share the same key code on different layouts - if (keyboard.m_config.arrange == CELL_KB_MAPPING_106) - { - buttons.emplace_back(Qt::Key_Backslash, CELL_KEYC_BACKSLASH_106); - buttons.emplace_back(Qt::Key_BracketLeft, CELL_KEYC_LEFT_BRACKET_106); - buttons.emplace_back(Qt::Key_BracketRight, CELL_KEYC_RIGHT_BRACKET_106); - } - else - { - buttons.emplace_back(Qt::Key_Backslash, CELL_KEYC_BACKSLASH_101); - buttons.emplace_back(Qt::Key_BracketLeft, CELL_KEYC_LEFT_BRACKET_101); - buttons.emplace_back(Qt::Key_BracketRight, CELL_KEYC_RIGHT_BRACKET_101); - } - - // Made up helper buttons - buttons.emplace_back(Qt::Key_Less, CELL_KEYC_LESS); - buttons.emplace_back(Qt::Key_NumberSign, CELL_KEYC_HASHTAG); - buttons.emplace_back(Qt::Key_ssharp, CELL_KEYC_SSHARP); - buttons.emplace_back(Qt::Key_QuoteLeft, CELL_KEYC_BACK_QUOTE); - buttons.emplace_back(Qt::Key_acute, CELL_KEYC_BACK_QUOTE); - - // Fill our map - for (const KbButton& button : buttons) - { - if (!keyboard.m_keys.try_emplace(button.m_keyCode, button).second) - { - input_log.error("basic_keyboard_handler failed to set key code %d", button.m_keyCode); - } - } -} diff --git a/rpcs3qt-legacy/Input/basic_keyboard_handler.h b/rpcs3qt-legacy/Input/basic_keyboard_handler.h deleted file mode 100644 index 284890025..000000000 --- a/rpcs3qt-legacy/Input/basic_keyboard_handler.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "util/types.hpp" -#include "Emu/Io/KeyboardHandler.h" - -#include -#include - -class basic_keyboard_handler final : public KeyboardHandlerBase, public QObject -{ - using KeyboardHandlerBase::KeyboardHandlerBase; - -public: - void Init(keyboard_consumer& consumer, const u32 max_connect) override; - - void SetTargetWindow(QWindow* target); - bool eventFilter(QObject* watched, QEvent* event) override; - void keyPressEvent(QKeyEvent* event); - void keyReleaseEvent(QKeyEvent* event); - static s32 getUnmodifiedKey(QKeyEvent* event); - -private: - void LoadSettings(Keyboard& keyboard); - - QWindow* m_target = nullptr; -}; diff --git a/rpcs3qt-legacy/Input/basic_mouse_handler.cpp b/rpcs3qt-legacy/Input/basic_mouse_handler.cpp deleted file mode 100644 index da0486260..000000000 --- a/rpcs3qt-legacy/Input/basic_mouse_handler.cpp +++ /dev/null @@ -1,280 +0,0 @@ -#include -#include - -#include "util/types.hpp" -#include "util/logs.hpp" - -#include "basic_mouse_handler.h" -#include "keyboard_pad_handler.h" -#include "../gs_frame.h" -#include "Emu/Io/interception.h" -#include "Emu/Io/mouse_config.h" - -mouse_config g_cfg_mouse; - -void basic_mouse_handler::Init(const u32 max_connect) -{ - if (m_info.max_connect > 0) - { - // Already initialized - return; - } - - if (!g_cfg_mouse.load()) - { - input_log.notice("basic_mouse_handler: Could not load basic mouse config. Using defaults."); - } - - reload_config(); - - m_mice.clear(); - m_mice.emplace_back(Mouse()); - - m_info = {}; - m_info.max_connect = max_connect; - m_info.now_connect = std::min(::size32(m_mice), max_connect); - m_info.info = input::g_mice_intercepted ? CELL_MOUSE_INFO_INTERCEPTED : 0; // Ownership of mouse data: 0=Application, 1=System - for (u32 i = 1; i < max_connect; i++) - { - m_info.status[i] = CELL_MOUSE_STATUS_DISCONNECTED; - m_info.mode[i] = CELL_MOUSE_INFO_TABLET_MOUSE_MODE; - m_info.tablet_is_supported[i] = CELL_MOUSE_INFO_TABLET_NOT_SUPPORTED; - } - m_info.status[0] = CELL_MOUSE_STATUS_CONNECTED; // (TODO: Support for more mice) - m_info.vendor_id[0] = 0x1234; - m_info.product_id[0] = 0x1234; - - type = mouse_handler::basic; -} - -void basic_mouse_handler::reload_config() -{ - input_log.notice("Basic mouse config=\n%s", g_cfg_mouse.to_string()); - - m_buttons[CELL_MOUSE_BUTTON_1] = get_mouse_button(g_cfg_mouse.mouse_button_1); - m_buttons[CELL_MOUSE_BUTTON_2] = get_mouse_button(g_cfg_mouse.mouse_button_2); - m_buttons[CELL_MOUSE_BUTTON_3] = get_mouse_button(g_cfg_mouse.mouse_button_3); - m_buttons[CELL_MOUSE_BUTTON_4] = get_mouse_button(g_cfg_mouse.mouse_button_4); - m_buttons[CELL_MOUSE_BUTTON_5] = get_mouse_button(g_cfg_mouse.mouse_button_5); - m_buttons[CELL_MOUSE_BUTTON_6] = get_mouse_button(g_cfg_mouse.mouse_button_6); - m_buttons[CELL_MOUSE_BUTTON_7] = get_mouse_button(g_cfg_mouse.mouse_button_7); - m_buttons[CELL_MOUSE_BUTTON_8] = get_mouse_button(g_cfg_mouse.mouse_button_8); -} - -/* Sets the target window for the event handler, and also installs an event filter on the target. */ -void basic_mouse_handler::SetTargetWindow(QWindow* target) -{ - if (target) - { - m_target = target; - target->installEventFilter(this); - } - else - { - // If this is hit, it probably means that some refactoring occurs because currently a gsframe is created in Load. - // We still want events so filter from application instead since target is null. - QApplication::instance()->installEventFilter(this); - input_log.error("Trying to set mouse handler to a null target window."); - } -} - -bool basic_mouse_handler::eventFilter(QObject* target, QEvent* ev) -{ - if (m_info.max_connect == 0) - { - // Not initialized - return false; - } - - if (!ev) [[unlikely]] - { - return false; - } - - if (input::g_active_mouse_and_keyboard != input::active_mouse_and_keyboard::emulated) - { - return false; - } - - // !m_target is for future proofing when gsrender isn't automatically initialized on load to ensure events still occur - // !m_target->isVisible() is a hack since currently a guiless application will STILL inititialize a gsrender (providing a valid target) - if (!m_target || !m_target->isVisible() || target == m_target) - { - if (g_cfg_mouse.reload_requested.exchange(false)) - { - reload_config(); - } - - switch (ev->type()) - { - case QEvent::MouseButtonPress: - MouseButton(static_cast(ev), true); - break; - case QEvent::MouseButtonRelease: - MouseButton(static_cast(ev), false); - break; - case QEvent::MouseMove: - MouseMove(static_cast(ev)); - break; - case QEvent::Wheel: - MouseScroll(static_cast(ev)); - break; - case QEvent::KeyPress: - Key(static_cast(ev), true); - break; - case QEvent::KeyRelease: - Key(static_cast(ev), false); - break; - case QEvent::Leave: - { - // Issue mouse move on leave. Otherwise we may not get any mouse event at the screen borders. - const QPoint window_pos = m_target->mapToGlobal(m_target->position()) / m_target->devicePixelRatio(); - const QPoint cursor_pos = QCursor::pos() - window_pos; - - if (cursor_pos.x() <= 0 || cursor_pos.x() >= m_target->width() || cursor_pos.y() <= 0 || cursor_pos.y() >= m_target->height()) - { - MouseMove(cursor_pos); - } - break; - } - default: - return false; - } - } - return false; -} - -void basic_mouse_handler::Key(QKeyEvent* event, bool pressed) -{ - if (!event) [[unlikely]] - { - return; - } - - const int key = event->key(); - if (const auto it = std::find_if(m_buttons.cbegin(), m_buttons.cend(), [key](const auto& entry) - { - return entry.second.code == key && entry.second.is_key; - }); - it != m_buttons.cend()) - { - MouseHandlerBase::Button(0, it->first, pressed); - } -} - -void basic_mouse_handler::MouseButton(QMouseEvent* event, bool pressed) -{ - if (!event) [[unlikely]] - { - return; - } - - const int button = event->button(); - if (const auto it = std::find_if(m_buttons.cbegin(), m_buttons.cend(), [button](const auto& entry) - { - return entry.second.code == button && !entry.second.is_key; - }); - it != m_buttons.cend()) - { - MouseHandlerBase::Button(0, it->first, pressed); - } -} - -void basic_mouse_handler::MouseScroll(QWheelEvent* event) -{ - if (!event) [[unlikely]] - { - return; - } - - const QPoint delta = event->angleDelta(); - const s8 x = std::clamp(delta.x() / 120, -128, 127); - const s8 y = std::clamp(delta.y() / 120, -128, 127); - MouseHandlerBase::Scroll(0, x, y); -} - -bool basic_mouse_handler::get_mouse_lock_state() const -{ - if (auto game_frame = dynamic_cast(m_target)) - return game_frame->get_mouse_lock_state(); - return false; -} - -basic_mouse_handler::mouse_button basic_mouse_handler::get_mouse_button(const cfg::string& button) -{ - const std::string name = button.to_string(); - const auto it = std::find_if(mouse_list.cbegin(), mouse_list.cend(), [&name](const auto& entry) - { - return entry.second == name; - }); - - if (it != mouse_list.cend()) - { - return mouse_button{ - .code = static_cast(it->first), - .is_key = false}; - } - - if (const u32 key = keyboard_pad_handler::GetKeyCode(QString::fromStdString(name))) - { - return mouse_button{ - .code = static_cast(key), - .is_key = true}; - } - - return mouse_button{ - .code = Qt::MouseButton::NoButton, - .is_key = false}; -} - -void basic_mouse_handler::MouseMove(QMouseEvent* event) -{ - if (!event) [[unlikely]] - { - return; - } - - if (is_time_for_update()) - { - MouseMove(event->pos()); - } -} - -void basic_mouse_handler::MouseMove(const QPoint& e_pos) -{ - if (!m_target) - return; - - // get the screen dimensions - const QSize screen = m_target->size(); - - if (m_target->isActive() && get_mouse_lock_state()) - { - // get the center of the screen in global coordinates - QPoint p_center = m_target->geometry().topLeft() + QPoint(screen.width() / 2, screen.height() / 2); - - // reset the mouse to the center for consistent results since edge movement won't be registered - QCursor::setPos(m_target->screen(), p_center); - - // convert the center into screen coordinates - p_center = m_target->mapFromGlobal(p_center); - - // current mouse position, starting at the center - static QPoint p_real(p_center); - - // get the delta of the mouse position to the screen center - const QPoint p_delta = e_pos - p_center; - - // update the current position without leaving the screen borders - p_real.setX(std::clamp(p_real.x() + p_delta.x(), 0, screen.width())); - p_real.setY(std::clamp(p_real.y() + p_delta.y(), 0, screen.height())); - - // pass the 'real' position and the current delta to the screen center - MouseHandlerBase::Move(0, p_real.x(), p_real.y(), screen.width(), screen.height(), true, p_delta.x(), p_delta.y()); - } - else - { - // pass the absolute position - MouseHandlerBase::Move(0, e_pos.x(), e_pos.y(), screen.width(), screen.height()); - } -} diff --git a/rpcs3qt-legacy/Input/basic_mouse_handler.h b/rpcs3qt-legacy/Input/basic_mouse_handler.h deleted file mode 100644 index ebc962e64..000000000 --- a/rpcs3qt-legacy/Input/basic_mouse_handler.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "util/types.hpp" -#include "Emu/Io/MouseHandler.h" - -#include -#include -#include - -namespace cfg -{ - class string; -} - -class basic_mouse_handler final : public MouseHandlerBase, public QObject -{ - using MouseHandlerBase::MouseHandlerBase; - -public: - void Init(const u32 max_connect) override; - - void SetTargetWindow(QWindow* target); - void Key(QKeyEvent* event, bool pressed); - void MouseButton(QMouseEvent* event, bool pressed); - void MouseScroll(QWheelEvent* event); - void MouseMove(QMouseEvent* event); - void MouseMove(const QPoint& e_pos); - - bool eventFilter(QObject* obj, QEvent* ev) override; - -private: - void reload_config(); - bool get_mouse_lock_state() const; - - struct mouse_button - { - int code = Qt::MouseButton::NoButton; - bool is_key = false; - }; - static mouse_button get_mouse_button(const cfg::string& button); - - QWindow* m_target = nullptr; - std::map m_buttons; -}; diff --git a/rpcs3qt-legacy/Input/gui_pad_thread.cpp b/rpcs3qt-legacy/Input/gui_pad_thread.cpp deleted file mode 100644 index 9c3da66ea..000000000 --- a/rpcs3qt-legacy/Input/gui_pad_thread.cpp +++ /dev/null @@ -1,815 +0,0 @@ -#include "stdafx.h" -#include "gui_pad_thread.h" -#include "Input/ds3_pad_handler.h" -#include "Input/ds4_pad_handler.h" -#include "Input/dualsense_pad_handler.h" -#include "Input/skateboard_pad_handler.h" -#ifdef _WIN32 -#include "Input/xinput_pad_handler.h" -#include "Input/mm_joystick_handler.h" -#elif HAVE_LIBEVDEV -#include "Input/evdev_joystick_handler.h" -#endif -#ifdef HAVE_SDL3 -#include "Input/sdl_pad_handler.h" -#endif -#include "Emu/Io/PadHandler.h" -#include "Emu/system_config.h" -#include "../gui_settings.h" - -#ifdef __linux__ -#include -#include -#include -#define CHECK_IOCTRL_RET(res) \ - if (res == -1) \ - { \ - gui_log.error("gui_pad_thread: ioctl failed (errno=%d=%s)", res, strerror(errno)); \ - } -#elif defined(__APPLE__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#pragma GCC diagnostic ignored "-Wmissing-declarations" -#pragma GCC diagnostic ignored "-Wnullability-completeness" -#pragma GCC diagnostic ignored "-Wdeprecated-anon-enum-enum-conversion" -#include -#include -#pragma GCC diagnostic pop -#endif - -#include - -LOG_CHANNEL(gui_log, "GUI"); - -atomic_t gui_pad_thread::m_reset = false; - -gui_pad_thread::gui_pad_thread() -{ - m_thread = std::make_unique>>("Gui Pad Thread", [this]() - { - run(); - }); -} - -gui_pad_thread::~gui_pad_thread() -{ - if (m_thread) - { - auto& thread = *m_thread; - thread = thread_state::aborting; - thread(); - m_thread.reset(); - } - -#ifdef __linux__ - if (m_uinput_fd != 1) - { - gui_log.notice("gui_pad_thread: closing /dev/uinput"); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_DEV_DESTROY)); - int res = close(m_uinput_fd); - if (res == -1) - { - gui_log.error("gui_pad_thread: Failed to close /dev/uinput (errno=%d=%s)", res, strerror(errno)); - } - m_uinput_fd = -1; - } -#endif -} - -void gui_pad_thread::update_settings(const std::shared_ptr& settings) -{ - ensure(!!settings); - - m_allow_global_input = settings->GetValue(gui::nav_global).toBool(); -} - -bool gui_pad_thread::init() -{ - m_handler.reset(); - m_pad.reset(); - - // Initialize last button states as pressed to avoid unwanted button presses when starting the thread. - m_last_button_state.fill(true); - m_timestamp = steady_clock::now(); - m_initial_timestamp = steady_clock::now(); - m_last_auto_repeat_button = pad_button::pad_button_max_enum; - - g_cfg_input_configs.load(); - - std::string active_config = g_cfg_input_configs.active_configs.get_value(""); - - if (active_config.empty()) - { - active_config = g_cfg_input_configs.active_configs.get_value(g_cfg_input_configs.global_key); - } - - gui_log.notice("gui_pad_thread: Using input configuration: '%s'", active_config); - - // Load in order to get the pad handlers - if (!g_cfg_input.load("", active_config)) - { - gui_log.notice("gui_pad_thread: Loaded empty pad config"); - } - - // Adjust to the different pad handlers - for (usz i = 0; i < g_cfg_input.player.size(); i++) - { - std::shared_ptr handler; - gui_pad_thread::InitPadConfig(g_cfg_input.player[i]->config, g_cfg_input.player[i]->handler, handler); - } - - // Reload with proper defaults - if (!g_cfg_input.load("", active_config)) - { - gui_log.notice("gui_pad_thread: Reloaded empty pad config"); - } - - gui_log.trace("gui_pad_thread: Using pad config:\n%s", g_cfg_input); - - for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; i++) // max 7 pads - { - cfg_player* cfg = g_cfg_input.player[i]; - - const pad_handler handler_type = cfg->handler.get(); - std::shared_ptr cur_pad_handler = GetHandler(handler_type); - - if (!cur_pad_handler) - { - continue; - } - - cur_pad_handler->Init(); - - m_handler = cur_pad_handler; - m_pad = std::make_shared(handler_type, i, CELL_PAD_STATUS_DISCONNECTED, CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_ACTUATOR, CELL_PAD_DEV_TYPE_STANDARD); - - if (!cur_pad_handler->bindPadToDevice(m_pad)) - { - gui_log.error("gui_pad_thread: Failed to bind device '%s' to handler %s.", cfg->device.to_string(), handler_type); - } - - gui_log.notice("gui_pad_thread: Pad %d: device='%s', handler=%s, VID=0x%x, PID=0x%x, class_type=0x%x, class_profile=0x%x", - i, cfg->device.to_string(), m_pad->m_pad_handler, m_pad->m_vendor_id, m_pad->m_product_id, m_pad->m_class_type, m_pad->m_class_profile); - - if (handler_type != pad_handler::null) - { - input_log.notice("gui_pad_thread %d: config=\n%s", i, cfg->to_string()); - } - - // We only use one pad - break; - } - - if (!m_handler || !m_pad) - { - gui_log.notice("gui_pad_thread: No devices configured."); - return false; - } - -#ifdef __linux__ - gui_log.notice("gui_pad_thread: opening /dev/uinput"); - - m_uinput_fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); - if (m_uinput_fd == -1) - { - gui_log.error("gui_pad_thread: Failed to open /dev/uinput (errno=%d=%s)", m_uinput_fd, strerror(errno)); - return false; - } - - struct uinput_setup usetup{}; - usetup.id.bustype = BUS_USB; - usetup.id.vendor = 0x1234; - usetup.id.product = 0x1234; - std::strcpy(usetup.name, "RPCS3 GUI Input Device"); - - // The ioctls below will enable the device that is about to be created to pass events. - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_EVBIT, EV_KEY)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_ESC)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_ENTER)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_BACKSPACE)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_TAB)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_LEFT)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_RIGHT)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_UP)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_DOWN)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, BTN_LEFT)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, BTN_RIGHT)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, BTN_MIDDLE)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_EVBIT, EV_REL)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_RELBIT, REL_X)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_RELBIT, REL_Y)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_RELBIT, REL_WHEEL)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_RELBIT, REL_HWHEEL)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_DEV_SETUP, &usetup)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_DEV_CREATE)); -#endif - - return true; -} - -std::shared_ptr gui_pad_thread::GetHandler(pad_handler type) -{ - switch (type) - { - case pad_handler::null: - case pad_handler::keyboard: - case pad_handler::move: - // Makes no sense to use this if we are in the GUI anyway - return nullptr; - case pad_handler::ds3: - return std::make_shared(); - case pad_handler::ds4: - return std::make_shared(); - case pad_handler::dualsense: - return std::make_shared(); - case pad_handler::skateboard: - return std::make_shared(); -#ifdef _WIN32 - case pad_handler::xinput: - return std::make_shared(); - case pad_handler::mm: - return std::make_shared(); -#endif -#ifdef HAVE_SDL3 - case pad_handler::sdl: - return std::make_shared(); -#endif -#ifdef HAVE_LIBEVDEV - case pad_handler::evdev: - return std::make_shared(); -#endif - } - - return nullptr; -} - -void gui_pad_thread::InitPadConfig(cfg_pad& cfg, pad_handler type, std::shared_ptr& handler) -{ - if (!handler) - { - handler = GetHandler(type); - - if (handler) - { - handler->init_config(&cfg); - } - } -} - -void gui_pad_thread::run() -{ - gui_log.notice("gui_pad_thread: Pad thread started"); - - m_reset = true; - - while (thread_ctrl::state() != thread_state::aborting) - { - if (m_reset && m_reset.exchange(false)) - { - if (!init()) - { - gui_log.warning("gui_pad_thread: Pad thread stopped (init failed during reset)"); - return; - } - } - - // Only process input if there is an active window - if (m_handler && m_pad && (m_allow_global_input || QApplication::activeWindow())) - { - m_handler->process(); - - if (thread_ctrl::state() == thread_state::aborting) - { - break; - } - - process_input(); - } - - thread_ctrl::wait_for(10000); - } - - gui_log.notice("gui_pad_thread: Pad thread stopped"); -} - -void gui_pad_thread::process_input() -{ - if (!m_pad || !(m_pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) - { - return; - } - - constexpr u64 ms_threshold = 500; - - const auto on_button_pressed = [this](pad_button button_id, bool pressed, u16 value) - { - if (button_id == m_mouse_boost_button) - { - m_boost_mouse = pressed; - return; - } - - u16 key = 0; - mouse_button btn = mouse_button::none; - mouse_wheel wheel = mouse_wheel::none; - float wheel_delta = 0.0f; - const float wheel_multiplier = pressed ? (m_boost_mouse ? 10.0f : 1.0f) : 0.0f; - const float move_multiplier = pressed ? (m_boost_mouse ? 40.0f : 20.0f) : 0.0f; - - switch (button_id) - { -#ifdef _WIN32 - case pad_button::dpad_up: key = VK_UP; break; - case pad_button::dpad_down: key = VK_DOWN; break; - case pad_button::dpad_left: key = VK_LEFT; break; - case pad_button::dpad_right: key = VK_RIGHT; break; - case pad_button::circle: key = VK_ESCAPE; break; - case pad_button::cross: key = VK_RETURN; break; - case pad_button::square: key = VK_BACK; break; - case pad_button::triangle: key = VK_TAB; break; -#elif defined(__linux__) - case pad_button::dpad_up: key = KEY_UP; break; - case pad_button::dpad_down: key = KEY_DOWN; break; - case pad_button::dpad_left: key = KEY_LEFT; break; - case pad_button::dpad_right: key = KEY_RIGHT; break; - case pad_button::circle: key = KEY_ESC; break; - case pad_button::cross: key = KEY_ENTER; break; - case pad_button::square: key = KEY_BACKSPACE; break; - case pad_button::triangle: key = KEY_TAB; break; -#elif defined(__APPLE__) - case pad_button::dpad_up: key = kVK_UpArrow; break; - case pad_button::dpad_down: key = kVK_DownArrow; break; - case pad_button::dpad_left: key = kVK_LeftArrow; break; - case pad_button::dpad_right: key = kVK_RightArrow; break; - case pad_button::circle: key = kVK_Escape; break; - case pad_button::cross: key = kVK_Return; break; - case pad_button::square: key = kVK_Delete; break; - case pad_button::triangle: key = kVK_Tab; break; -#endif - case pad_button::L1: btn = mouse_button::left; break; - case pad_button::R1: btn = mouse_button::right; break; - case pad_button::rs_up: - wheel = mouse_wheel::vertical; - wheel_delta = 10.0f * wheel_multiplier; - break; - case pad_button::rs_down: - wheel = mouse_wheel::vertical; - wheel_delta = -10.0f * wheel_multiplier; - break; - case pad_button::rs_left: - wheel = mouse_wheel::horizontal; - wheel_delta = -10.0f * wheel_multiplier; - break; - case pad_button::rs_right: - wheel = mouse_wheel::horizontal; - wheel_delta = 10.0f * wheel_multiplier; - break; - case pad_button::ls_up: m_mouse_delta_y -= (abs(value - 128) / 255.f) * move_multiplier; break; - case pad_button::ls_down: m_mouse_delta_y += (abs(value - 128) / 255.f) * move_multiplier; break; - case pad_button::ls_left: m_mouse_delta_x -= (abs(value - 128) / 255.f) * move_multiplier; break; - case pad_button::ls_right: m_mouse_delta_x += (abs(value - 128) / 255.f) * move_multiplier; break; - default: return; - } - - if (key) - { - send_key_event(key, pressed); - } - else if (btn != mouse_button::none) - { - send_mouse_button_event(btn, pressed); - } - else if (wheel != mouse_wheel::none && pressed) - { - send_mouse_wheel_event(wheel, wheel_delta); - } - }; - - const auto handle_button_press = [&](pad_button button_id, bool pressed, u16 value) - { - if (button_id >= pad_button::pad_button_max_enum) - { - return; - } - - bool& last_state = m_last_button_state[static_cast(button_id)]; - - if (pressed) - { - const bool is_auto_repeat_button = m_auto_repeat_buttons.contains(button_id); - const bool is_mouse_move_button = m_mouse_move_buttons.contains(button_id); - - if (!last_state) - { - if (button_id != m_mouse_boost_button && !is_mouse_move_button) - { - // The button was not pressed before, so this is a new button press. Reset auto-repeat. - m_timestamp = steady_clock::now(); - m_initial_timestamp = m_timestamp; - m_last_auto_repeat_button = is_auto_repeat_button ? button_id : pad_button::pad_button_max_enum; - } - - on_button_pressed(static_cast(button_id), true, value); - } - else if (is_auto_repeat_button) - { - if (m_last_auto_repeat_button == button_id && m_input_timer.GetMsSince(m_initial_timestamp) > ms_threshold && m_input_timer.GetMsSince(m_timestamp) > m_auto_repeat_buttons.at(button_id)) - { - // The auto-repeat button was pressed for at least the given threshold in ms and will trigger at an interval. - m_timestamp = steady_clock::now(); - on_button_pressed(static_cast(button_id), true, value); - } - else if (m_last_auto_repeat_button == pad_button::pad_button_max_enum) - { - // An auto-repeat button was already pressed before and will now start triggering again after the next threshold. - m_last_auto_repeat_button = button_id; - } - } - else if (is_mouse_move_button) - { - on_button_pressed(static_cast(button_id), pressed, value); - } - } - else if (last_state) - { - if (m_last_auto_repeat_button == button_id) - { - // We stopped pressing an auto-repeat button, so re-enable auto-repeat for other buttons. - m_last_auto_repeat_button = pad_button::pad_button_max_enum; - } - - on_button_pressed(static_cast(button_id), false, value); - } - - last_state = pressed; - }; - - for (const auto& button : m_pad->m_buttons) - { - pad_button button_id = pad_button::pad_button_max_enum; - if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1) - { - switch (button.m_outKeyCode) - { - case CELL_PAD_CTRL_LEFT: - button_id = pad_button::dpad_left; - break; - case CELL_PAD_CTRL_RIGHT: - button_id = pad_button::dpad_right; - break; - case CELL_PAD_CTRL_DOWN: - button_id = pad_button::dpad_down; - break; - case CELL_PAD_CTRL_UP: - button_id = pad_button::dpad_up; - break; - case CELL_PAD_CTRL_L3: - button_id = pad_button::L3; - break; - case CELL_PAD_CTRL_R3: - button_id = pad_button::R3; - break; - case CELL_PAD_CTRL_SELECT: - button_id = pad_button::select; - break; - case CELL_PAD_CTRL_START: - button_id = pad_button::start; - break; - default: - break; - } - } - else if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL2) - { - switch (button.m_outKeyCode) - { - case CELL_PAD_CTRL_TRIANGLE: - button_id = pad_button::triangle; - break; - case CELL_PAD_CTRL_CIRCLE: - button_id = g_cfg.sys.enter_button_assignment == enter_button_assign::circle ? pad_button::cross : pad_button::circle; - break; - case CELL_PAD_CTRL_SQUARE: - button_id = pad_button::square; - break; - case CELL_PAD_CTRL_CROSS: - button_id = g_cfg.sys.enter_button_assignment == enter_button_assign::circle ? pad_button::circle : pad_button::cross; - break; - case CELL_PAD_CTRL_L1: - button_id = pad_button::L1; - break; - case CELL_PAD_CTRL_R1: - button_id = pad_button::R1; - break; - case CELL_PAD_CTRL_L2: - button_id = pad_button::L2; - break; - case CELL_PAD_CTRL_R2: - button_id = pad_button::R2; - break; - case CELL_PAD_CTRL_PS: - button_id = pad_button::ps; - break; - default: - break; - } - } - - handle_button_press(button_id, button.m_pressed, button.m_value); - } - - for (const AnalogStick& stick : m_pad->m_sticks) - { - pad_button button_id = pad_button::pad_button_max_enum; - pad_button release_id = pad_button::pad_button_max_enum; - - switch (stick.m_offset) - { - case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X: - button_id = (stick.m_value <= 128) ? pad_button::ls_left : pad_button::ls_right; - release_id = (stick.m_value > 128) ? pad_button::ls_left : pad_button::ls_right; - break; - case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y: - button_id = (stick.m_value <= 128) ? pad_button::ls_up : pad_button::ls_down; - release_id = (stick.m_value > 128) ? pad_button::ls_up : pad_button::ls_down; - break; - case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X: - button_id = (stick.m_value <= 128) ? pad_button::rs_left : pad_button::rs_right; - release_id = (stick.m_value > 128) ? pad_button::rs_left : pad_button::rs_right; - break; - case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y: - button_id = (stick.m_value <= 128) ? pad_button::rs_up : pad_button::rs_down; - release_id = (stick.m_value > 128) ? pad_button::rs_up : pad_button::rs_down; - break; - default: - break; - } - - bool pressed; - - if (m_mouse_move_buttons.contains(button_id)) - { - // Mouse move sticks are always pressed if they surpass a tiny deadzone. - constexpr int deadzone = 5; - pressed = std::abs(stick.m_value - 128) > deadzone; - } - else - { - // Let's say other sticks are only pressed if they are almost completely tilted. Otherwise navigation feels really wacky. - pressed = stick.m_value < 30 || stick.m_value > 225; - } - - // Release other direction on the same axis first - handle_button_press(release_id, false, stick.m_value); - - // Handle currently pressed stick direction - handle_button_press(button_id, pressed, stick.m_value); - } - - // Send mouse move event at the end to prevent redundant calls - - // Round to lower integer - const int delta_x = m_mouse_delta_x; - const int delta_y = m_mouse_delta_y; - - if (delta_x || delta_y) - { - // Remove integer part - m_mouse_delta_x -= delta_x; - m_mouse_delta_y -= delta_y; - - // Send event - send_mouse_move_event(delta_x, delta_y); - } -} - -#ifdef __linux__ -void gui_pad_thread::emit_event(int type, int code, int val) -{ - struct input_event ie{}; - ie.type = type; - ie.code = code; - ie.value = val; - - int res = write(m_uinput_fd, &ie, sizeof(ie)); - if (res == -1) - { - gui_log.error("gui_pad_thread::emit_event: write failed (errno=%d=%s)", res, strerror(errno)); - } -} -#endif - -void gui_pad_thread::send_key_event(u32 key, bool pressed) -{ - gui_log.trace("gui_pad_thread::send_key_event: key=%d, pressed=%d", key, pressed); - -#ifdef _WIN32 - INPUT input{}; - input.type = INPUT_KEYBOARD; - input.ki.wVk = key; - - if (!pressed) - { - input.ki.dwFlags = KEYEVENTF_KEYUP; - } - - if (SendInput(1, &input, sizeof(INPUT)) != 1) - { - gui_log.error("gui_pad_thread: SendInput() failed: %s", fmt::win_error{GetLastError(), nullptr}); - } -#elif defined(__linux__) - emit_event(EV_KEY, key, pressed ? 1 : 0); - emit_event(EV_SYN, SYN_REPORT, 0); -#elif defined(__APPLE__) - CGEventRef ev = CGEventCreateKeyboardEvent(NULL, static_cast(key), pressed); - if (!ev) - { - gui_log.error("gui_pad_thread: CGEventCreateKeyboardEvent() failed"); - return; - } - - CGEventPost(kCGHIDEventTap, ev); - CFRelease(ev); -#endif -} - -void gui_pad_thread::send_mouse_button_event(mouse_button btn, bool pressed) -{ - gui_log.trace("gui_pad_thread::send_mouse_button_event: btn=%d, pressed=%d", static_cast(btn), pressed); - -#ifdef _WIN32 - INPUT input{}; - input.type = INPUT_MOUSE; - - switch (btn) - { - case mouse_button::none: return; - case mouse_button::left: input.mi.dwFlags = pressed ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP; break; - case mouse_button::right: input.mi.dwFlags = pressed ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP; break; - case mouse_button::middle: input.mi.dwFlags = pressed ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP; break; - } - - if (SendInput(1, &input, sizeof(INPUT)) != 1) - { - gui_log.error("gui_pad_thread: SendInput() failed: %s", fmt::win_error{GetLastError(), nullptr}); - } -#elif defined(__linux__) - int key = 0; - - switch (btn) - { - case mouse_button::none: return; - case mouse_button::left: key = BTN_LEFT; break; - case mouse_button::right: key = BTN_RIGHT; break; - case mouse_button::middle: key = BTN_MIDDLE; break; - } - - emit_event(EV_KEY, key, pressed ? 1 : 0); - emit_event(EV_SYN, SYN_REPORT, 0); -#elif defined(__APPLE__) - CGEventType type{}; - CGMouseButton mouse_btn{}; - - switch (btn) - { - case mouse_button::none: return; - case mouse_button::left: - type = pressed ? kCGEventLeftMouseDown : kCGEventLeftMouseUp; - mouse_btn = kCGMouseButtonLeft; - break; - case mouse_button::right: - type = pressed ? kCGEventRightMouseDown : kCGEventRightMouseUp; - mouse_btn = kCGMouseButtonRight; - break; - case mouse_button::middle: - type = pressed ? kCGEventOtherMouseDown : kCGEventOtherMouseUp; - mouse_btn = kCGMouseButtonCenter; - break; - } - - CGEventRef ev = CGEventCreateMouseEvent(NULL, type, CGPointMake(m_mouse_abs_x, m_mouse_abs_y), mouse_btn); - if (!ev) - { - gui_log.error("gui_pad_thread: CGEventCreateMouseEvent() failed"); - return; - } - - CGEventPost(kCGHIDEventTap, ev); - CFRelease(ev); -#endif -} - -void gui_pad_thread::send_mouse_wheel_event(mouse_wheel wheel, int delta) -{ - gui_log.trace("gui_pad_thread::send_mouse_wheel_event: wheel=%d, delta=%d", static_cast(wheel), delta); - - if (!delta) - { - return; - } - -#ifdef _WIN32 - INPUT input{}; - input.type = INPUT_MOUSE; - input.mi.mouseData = delta; - - switch (wheel) - { - case mouse_wheel::none: return; - case mouse_wheel::vertical: input.mi.dwFlags = MOUSEEVENTF_WHEEL; break; - case mouse_wheel::horizontal: input.mi.dwFlags = MOUSEEVENTF_HWHEEL; break; - } - - if (SendInput(1, &input, sizeof(INPUT)) != 1) - { - gui_log.error("gui_pad_thread: SendInput() failed: %s", fmt::win_error{GetLastError(), nullptr}); - } -#elif defined(__linux__) - int axis = 0; - - switch (wheel) - { - case mouse_wheel::none: return; - case mouse_wheel::vertical: axis = REL_WHEEL; break; - case mouse_wheel::horizontal: axis = REL_HWHEEL; break; - } - - emit_event(EV_REL, axis, delta); - emit_event(EV_SYN, SYN_REPORT, 0); -#elif defined(__APPLE__) - int v_delta = 0; - int h_delta = 0; - - switch (wheel) - { - case mouse_wheel::none: return; - case mouse_wheel::vertical: v_delta = delta; break; - case mouse_wheel::horizontal: h_delta = delta; break; - } - - constexpr u32 wheel_count = 2; - CGEventRef ev = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, wheel_count, v_delta, h_delta); - if (!ev) - { - gui_log.error("gui_pad_thread: CGEventCreateScrollWheelEvent() failed"); - return; - } - - CGEventPost(kCGHIDEventTap, ev); - CFRelease(ev); -#endif -} - -void gui_pad_thread::send_mouse_move_event(int delta_x, int delta_y) -{ - gui_log.trace("gui_pad_thread::send_mouse_move_event: delta_x=%d, delta_y=%d", delta_x, delta_y); - - if (!delta_x && !delta_y) - { - return; - } - -#ifdef _WIN32 - INPUT input{}; - input.type = INPUT_MOUSE; - input.mi.dwFlags = MOUSEEVENTF_MOVE; - input.mi.dx = delta_x; - input.mi.dy = delta_y; - - if (SendInput(1, &input, sizeof(INPUT)) != 1) - { - gui_log.error("gui_pad_thread: SendInput() failed: %s", fmt::win_error{GetLastError(), nullptr}); - } -#elif defined(__linux__) - if (delta_x) - emit_event(EV_REL, REL_X, delta_x); - if (delta_y) - emit_event(EV_REL, REL_Y, delta_y); - emit_event(EV_SYN, SYN_REPORT, 0); -#elif defined(__APPLE__) - CGDirectDisplayID display = CGMainDisplayID(); - const usz width = CGDisplayPixelsWide(display); - const usz height = CGDisplayPixelsHigh(display); - const float mouse_abs_x = std::clamp(m_mouse_abs_x + delta_x, 0.0f, width - 1.0f); - const float mouse_abs_y = std::clamp(m_mouse_abs_y + delta_y, 0.0f, height - 1.0f); - - if (m_mouse_abs_x == mouse_abs_x && m_mouse_abs_y == mouse_abs_y) - { - return; - } - - m_mouse_abs_x = mouse_abs_x; - m_mouse_abs_y = mouse_abs_y; - - CGEventRef ev = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, CGPointMake(m_mouse_abs_x, m_mouse_abs_y), {}); - if (!ev) - { - gui_log.error("gui_pad_thread: CGEventCreateMouseEvent() failed"); - return; - } - - CGEventPost(kCGHIDEventTap, ev); - CFRelease(ev); -#endif -} diff --git a/rpcs3qt-legacy/Input/gui_pad_thread.h b/rpcs3qt-legacy/Input/gui_pad_thread.h deleted file mode 100644 index d4a8c169d..000000000 --- a/rpcs3qt-legacy/Input/gui_pad_thread.h +++ /dev/null @@ -1,101 +0,0 @@ -#pragma once - -#include "util/types.hpp" -#include "util/atomic.hpp" -#include "Emu/Io/pad_types.h" -#include "Emu/Io/pad_config.h" -#include "Emu/Io/pad_config_types.h" -#include "util/Timer.h" -#include "util/Thread.h" - -class PadHandlerBase; -class gui_settings; - -class gui_pad_thread -{ -public: - gui_pad_thread(); - virtual ~gui_pad_thread(); - - void update_settings(const std::shared_ptr& settings); - - static std::shared_ptr GetHandler(pad_handler type); - static void InitPadConfig(cfg_pad& cfg, pad_handler type, std::shared_ptr& handler); - - static void reset() - { - m_reset = true; - } - -protected: - bool init(); - void run(); - - void process_input(); - - enum class mouse_button - { - none, - left, - right, - middle - }; - - enum class mouse_wheel - { - none, - vertical, - horizontal - }; - - void send_key_event(u32 key, bool pressed); - void send_mouse_button_event(mouse_button btn, bool pressed); - void send_mouse_wheel_event(mouse_wheel wheel, int delta); - void send_mouse_move_event(int delta_x, int delta_y); - -#ifdef __linux__ - int m_uinput_fd = -1; - void emit_event(int type, int code, int val); -#endif - - std::shared_ptr m_handler; - std::shared_ptr m_pad; - - std::unique_ptr>> m_thread; - atomic_t m_allow_global_input = false; - static atomic_t m_reset; - - std::array(pad_button::pad_button_max_enum)> m_last_button_state{}; - - steady_clock::time_point m_timestamp; - steady_clock::time_point m_initial_timestamp; - Timer m_input_timer; - - static constexpr u64 auto_repeat_ms_interval_default = 200; - pad_button m_last_auto_repeat_button = pad_button::pad_button_max_enum; - std::map m_auto_repeat_buttons = { - {pad_button::dpad_up, auto_repeat_ms_interval_default}, - {pad_button::dpad_down, auto_repeat_ms_interval_default}, - {pad_button::dpad_left, auto_repeat_ms_interval_default}, - {pad_button::dpad_right, auto_repeat_ms_interval_default}, - {pad_button::rs_up, 1}, // used for wheel scrolling by default - {pad_button::rs_down, 1}, // used for wheel scrolling by default - {pad_button::rs_left, 1}, // used for wheel scrolling by default - {pad_button::rs_right, 1} // used for wheel scrolling by default - }; - - // Mouse movement should just work without delays - std::map m_mouse_move_buttons = { - {pad_button::ls_up, 1}, - {pad_button::ls_down, 1}, - {pad_button::ls_left, 1}, - {pad_button::ls_right, 1}}; - pad_button m_mouse_boost_button = pad_button::L2; - bool m_boost_mouse = false; - float m_mouse_delta_x = 0.0f; - float m_mouse_delta_y = 0.0f; -#ifdef __APPLE__ - float m_mouse_abs_x = 0.0f; - float m_mouse_abs_y = 0.0f; -#endif -}; diff --git a/rpcs3qt-legacy/Input/keyboard_pad_handler.cpp b/rpcs3qt-legacy/Input/keyboard_pad_handler.cpp deleted file mode 100644 index 56d88e594..000000000 --- a/rpcs3qt-legacy/Input/keyboard_pad_handler.cpp +++ /dev/null @@ -1,1311 +0,0 @@ -#include "keyboard_pad_handler.h" -#include "Input/pad_thread.h" -#include "Emu/Io/pad_config.h" -#include "Emu/Io/KeyboardHandler.h" -#include "Emu/Io/interception.h" -#include "Input/product_info.h" -#include "../gs_frame.h" - -#include - -bool keyboard_pad_handler::Init() -{ - const steady_clock::time_point now = steady_clock::now(); - m_last_mouse_move_left = now; - m_last_mouse_move_right = now; - m_last_mouse_move_up = now; - m_last_mouse_move_down = now; - return true; -} - -keyboard_pad_handler::keyboard_pad_handler() - : QObject(), PadHandlerBase(pad_handler::keyboard) -{ - init_configs(); - - // set capabilities - b_has_config = true; -} - -void keyboard_pad_handler::init_config(cfg_pad* cfg) -{ - if (!cfg) - return; - - // Set default button mapping - cfg->ls_left.def = GetKeyName(Qt::Key_A); - cfg->ls_down.def = GetKeyName(Qt::Key_S); - cfg->ls_right.def = GetKeyName(Qt::Key_D); - cfg->ls_up.def = GetKeyName(Qt::Key_W); - cfg->rs_left.def = GetKeyName(Qt::Key_Delete); - cfg->rs_down.def = GetKeyName(Qt::Key_End); - cfg->rs_right.def = GetKeyName(Qt::Key_PageDown); - cfg->rs_up.def = GetKeyName(Qt::Key_Home); - cfg->start.def = GetKeyName(Qt::Key_Return); - cfg->select.def = GetKeyName(Qt::Key_Space); - cfg->ps.def = GetKeyName(Qt::Key_Backspace); - cfg->square.def = GetKeyName(Qt::Key_Z); - cfg->cross.def = GetKeyName(Qt::Key_X); - cfg->circle.def = GetKeyName(Qt::Key_C); - cfg->triangle.def = GetKeyName(Qt::Key_V); - cfg->left.def = GetKeyName(Qt::Key_Left); - cfg->down.def = GetKeyName(Qt::Key_Down); - cfg->right.def = GetKeyName(Qt::Key_Right); - cfg->up.def = GetKeyName(Qt::Key_Up); - cfg->r1.def = GetKeyName(Qt::Key_E); - cfg->r2.def = GetKeyName(Qt::Key_T); - cfg->r3.def = GetKeyName(Qt::Key_G); - cfg->l1.def = GetKeyName(Qt::Key_Q); - cfg->l2.def = GetKeyName(Qt::Key_R); - cfg->l3.def = GetKeyName(Qt::Key_F); - - cfg->pressure_intensity_button.def = GetKeyName(Qt::NoButton); - cfg->analog_limiter_button.def = GetKeyName(Qt::NoButton); - - cfg->lstick_anti_deadzone.def = 0; - cfg->rstick_anti_deadzone.def = 0; - cfg->lstickdeadzone.def = 0; - cfg->rstickdeadzone.def = 0; - cfg->ltriggerthreshold.def = 0; - cfg->rtriggerthreshold.def = 0; - cfg->lpadsquircling.def = 8000; - cfg->rpadsquircling.def = 8000; - - // apply defaults - cfg->from_default(); -} - -void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value) -{ - if (!pad::g_enabled) - { - return; - } - - value = Clamp0To255(value); - - for (auto& pad : m_pads_internal) - { - const auto register_new_button_value = [&](std::map& pressed_keys) -> u16 - { - u16 actual_value = 0; - - // Make sure we keep this button pressed until all related keys are released. - if (pressed) - { - pressed_keys[code] = value; - } - else - { - pressed_keys.erase(code); - } - - // Get the max value of all pressed keys for this button - for (const auto& [key, val] : pressed_keys) - { - actual_value = std::max(actual_value, val); - } - - return actual_value; - }; - - // Find out if special buttons are pressed (introduced by RPCS3). - // Activate the buttons here if possible since keys don't auto-repeat. This ensures that they are already pressed in the following loop. - if (pad.m_pressure_intensity_button_index >= 0) - { - Button& pressure_intensity_button = pad.m_buttons[pad.m_pressure_intensity_button_index]; - - if (pressure_intensity_button.m_key_codes.contains(code)) - { - const u16 actual_value = register_new_button_value(pressure_intensity_button.m_pressed_keys); - - pressure_intensity_button.m_pressed = actual_value > 0; - pressure_intensity_button.m_value = actual_value; - } - } - - const bool adjust_pressure = pad.get_pressure_intensity_button_active(m_pressure_intensity_toggle_mode, pad.m_player_id); - const bool adjust_pressure_changed = pad.m_adjust_pressure_last != adjust_pressure; - - if (adjust_pressure_changed) - { - pad.m_adjust_pressure_last = adjust_pressure; - } - - if (pad.m_analog_limiter_button_index >= 0) - { - Button& analog_limiter_button = pad.m_buttons[pad.m_analog_limiter_button_index]; - - if (analog_limiter_button.m_key_codes.contains(code)) - { - const u16 actual_value = register_new_button_value(analog_limiter_button.m_pressed_keys); - - analog_limiter_button.m_pressed = actual_value > 0; - analog_limiter_button.m_value = actual_value; - } - } - - const bool analog_limiter_enabled = pad.get_analog_limiter_button_active(m_analog_limiter_toggle_mode, pad.m_player_id); - const bool analog_limiter_changed = pad.m_analog_limiter_enabled_last != analog_limiter_enabled; - const u32 l_stick_multiplier = analog_limiter_enabled ? m_l_stick_multiplier : 100; - const u32 r_stick_multiplier = analog_limiter_enabled ? m_r_stick_multiplier : 100; - - if (analog_limiter_changed) - { - pad.m_analog_limiter_enabled_last = analog_limiter_enabled; - } - - // Handle buttons - for (usz i = 0; i < pad.m_buttons.size(); i++) - { - // Ignore special buttons - if (static_cast(i) == pad.m_pressure_intensity_button_index || - static_cast(i) == pad.m_analog_limiter_button_index) - continue; - - Button& button = pad.m_buttons[i]; - - bool update_button = true; - - if (!button.m_key_codes.contains(code)) - { - // Handle pressure changes anyway - update_button = adjust_pressure_changed; - } - else - { - button.m_actual_value = register_new_button_value(button.m_pressed_keys); - - // to get the fastest response time possible we don't wanna use any lerp with factor 1 - if (button.m_analog) - { - update_button = m_analog_lerp_factor >= 1.0f; - } - else if (button.m_trigger) - { - update_button = m_trigger_lerp_factor >= 1.0f; - } - } - - if (!update_button) - continue; - - if (button.m_actual_value > 0) - { - // Modify pressure if necessary if the button was pressed - if (adjust_pressure) - { - button.m_value = pad.m_pressure_intensity; - } - else if (m_pressure_intensity_deadzone > 0) - { - button.m_value = NormalizeDirectedInput(button.m_actual_value, m_pressure_intensity_deadzone, 255); - } - else - { - button.m_value = button.m_actual_value; - } - - button.m_pressed = button.m_value > 0; - } - else - { - button.m_value = 0; - button.m_pressed = false; - } - } - - // Handle sticks - for (usz i = 0; i < pad.m_sticks.size(); i++) - { - AnalogStick& stick = pad.m_sticks[i]; - - const bool is_left_stick = i < 2; - - const bool is_max = stick.m_key_codes_max.contains(code); - const bool is_min = stick.m_key_codes_min.contains(code); - - if (!is_max && !is_min) - { - if (!analog_limiter_changed) - continue; - - // Update already pressed sticks - const bool is_min_pressed = !stick.m_pressed_keys_min.empty(); - const bool is_max_pressed = !stick.m_pressed_keys_max.empty(); - - const u32 stick_multiplier = is_left_stick ? l_stick_multiplier : r_stick_multiplier; - - const u16 actual_min_value = is_min_pressed ? MultipliedInput(255, stick_multiplier) : 255; - const u16 normalized_min_value = std::ceil(actual_min_value / 2.0); - - const u16 actual_max_value = is_max_pressed ? MultipliedInput(255, stick_multiplier) : 255; - const u16 normalized_max_value = std::ceil(actual_max_value / 2.0); - - m_stick_min[i] = is_min_pressed ? std::min(normalized_min_value, 128) : 0; - m_stick_max[i] = is_max_pressed ? std::min(128 + normalized_max_value, 255) : 128; - } - else - { - const u16 actual_value = pressed ? MultipliedInput(value, is_left_stick ? l_stick_multiplier : r_stick_multiplier) : value; - u16 normalized_value = std::ceil(actual_value / 2.0); - - const auto register_new_stick_value = [&](std::map& pressed_keys, bool is_max) - { - // Make sure we keep this stick pressed until all related keys are released. - if (pressed) - { - pressed_keys[code] = normalized_value; - } - else - { - pressed_keys.erase(code); - } - - // Get the min/max value of all pressed keys for this stick - for (const auto& [key, val] : pressed_keys) - { - normalized_value = is_max ? std::max(normalized_value, val) : std::min(normalized_value, val); - } - }; - - if (is_max) - { - register_new_stick_value(stick.m_pressed_keys_max, true); - - const bool is_max_pressed = !stick.m_pressed_keys_max.empty(); - - m_stick_max[i] = is_max_pressed ? std::min(128 + normalized_value, 255) : 128; - } - - if (is_min) - { - register_new_stick_value(stick.m_pressed_keys_min, false); - - const bool is_min_pressed = !stick.m_pressed_keys_min.empty(); - - m_stick_min[i] = is_min_pressed ? std::min(normalized_value, 128) : 0; - } - } - - m_stick_val[i] = m_stick_max[i] - m_stick_min[i]; - - const f32 stick_lerp_factor = is_left_stick ? m_l_stick_lerp_factor : m_r_stick_lerp_factor; - - // to get the fastest response time possible we don't wanna use any lerp with factor 1 - if (stick_lerp_factor >= 1.0f) - { - stick.m_value = m_stick_val[i]; - } - } - } -} - -void keyboard_pad_handler::release_all_keys() -{ - for (auto& pad : m_pads_internal) - { - for (Button& button : pad.m_buttons) - { - button.m_pressed = false; - button.m_value = 0; - button.m_actual_value = 0; - } - - for (usz i = 0; i < pad.m_sticks.size(); i++) - { - m_stick_min[i] = 0; - m_stick_max[i] = 128; - m_stick_val[i] = 128; - pad.m_sticks[i].m_value = 128; - } - } - - m_keys_released = true; -} - -bool keyboard_pad_handler::eventFilter(QObject* target, QEvent* ev) -{ - if (!ev) [[unlikely]] - { - return false; - } - - if (input::g_active_mouse_and_keyboard != input::active_mouse_and_keyboard::pad) - { - if (!m_keys_released) - { - release_all_keys(); - } - return false; - } - - m_keys_released = false; - - // !m_target is for future proofing when gsrender isn't automatically initialized on load. - // !m_target->isVisible() is a hack since currently a guiless application will STILL inititialize a gsrender (providing a valid target) - if (!m_target || !m_target->isVisible() || target == m_target) - { - switch (ev->type()) - { - case QEvent::KeyPress: - keyPressEvent(static_cast(ev)); - break; - case QEvent::KeyRelease: - keyReleaseEvent(static_cast(ev)); - break; - case QEvent::MouseButtonPress: - mousePressEvent(static_cast(ev)); - break; - case QEvent::MouseButtonRelease: - mouseReleaseEvent(static_cast(ev)); - break; - case QEvent::MouseMove: - mouseMoveEvent(static_cast(ev)); - break; - case QEvent::Wheel: - mouseWheelEvent(static_cast(ev)); - break; - case QEvent::FocusOut: - release_all_keys(); - break; - default: - break; - } - } - return false; -} - -/* Sets the target window for the event handler, and also installs an event filter on the target. */ -void keyboard_pad_handler::SetTargetWindow(QWindow* target) -{ - if (target != nullptr) - { - m_target = target; - target->installEventFilter(this); - } - else - { - QApplication::instance()->installEventFilter(this); - // If this is hit, it probably means that some refactoring occurs because currently a gsframe is created in Load. - // We still want events so filter from application instead since target is null. - input_log.error("Trying to set pad handler to a null target window."); - } -} - -void keyboard_pad_handler::processKeyEvent(QKeyEvent* event, bool pressed) -{ - if (!event) [[unlikely]] - { - return; - } - - if (event->isAutoRepeat()) - { - event->ignore(); - return; - } - - const auto handle_key = [this, pressed, event]() - { - QStringList list = GetKeyNames(event); - if (list.isEmpty()) - return; - - const bool is_num_key = list.removeAll("Num") > 0; - const QString name = QString::fromStdString(GetKeyName(event, true)); - - // TODO: Edge case: switching numlock keeps numpad keys pressed due to now different modifier - - // Handle every possible key combination, for example: ctrl+A -> {ctrl, A, ctrl+A} - for (const QString& keyname : list) - { - // skip the 'original keys' when handling numpad keys - if (is_num_key && !keyname.contains("Num")) - continue; - // skip held modifiers when handling another key - if (keyname != name && list.count() > 1 && (keyname == "Alt" || keyname == "AltGr" || keyname == "Ctrl" || keyname == "Meta" || keyname == "Shift")) - continue; - Key(GetKeyCode(keyname), pressed); - } - }; - - handle_key(); - event->ignore(); -} - -void keyboard_pad_handler::keyPressEvent(QKeyEvent* event) -{ - if (!event) [[unlikely]] - { - return; - } - - if (event->modifiers() & Qt::AltModifier) - { - switch (event->key()) - { - case Qt::Key_I: - m_deadzone_y = std::min(m_deadzone_y + 1, 255); - input_log.success("mouse move adjustment: deadzone y = %d", m_deadzone_y); - event->ignore(); - return; - case Qt::Key_U: - m_deadzone_y = std::max(0, m_deadzone_y - 1); - input_log.success("mouse move adjustment: deadzone y = %d", m_deadzone_y); - event->ignore(); - return; - case Qt::Key_Y: - m_deadzone_x = std::min(m_deadzone_x + 1, 255); - input_log.success("mouse move adjustment: deadzone x = %d", m_deadzone_x); - event->ignore(); - return; - case Qt::Key_T: - m_deadzone_x = std::max(0, m_deadzone_x - 1); - input_log.success("mouse move adjustment: deadzone x = %d", m_deadzone_x); - event->ignore(); - return; - case Qt::Key_K: - m_multi_y = std::min(m_multi_y + 0.1, 5.0); - input_log.success("mouse move adjustment: multiplier y = %d", static_cast(m_multi_y * 100)); - event->ignore(); - return; - case Qt::Key_J: - m_multi_y = std::max(0.0, m_multi_y - 0.1); - input_log.success("mouse move adjustment: multiplier y = %d", static_cast(m_multi_y * 100)); - event->ignore(); - return; - case Qt::Key_H: - m_multi_x = std::min(m_multi_x + 0.1, 5.0); - input_log.success("mouse move adjustment: multiplier x = %d", static_cast(m_multi_x * 100)); - event->ignore(); - return; - case Qt::Key_G: - m_multi_x = std::max(0.0, m_multi_x - 0.1); - input_log.success("mouse move adjustment: multiplier x = %d", static_cast(m_multi_x * 100)); - event->ignore(); - return; - default: - break; - } - } - processKeyEvent(event, true); -} - -void keyboard_pad_handler::keyReleaseEvent(QKeyEvent* event) -{ - processKeyEvent(event, false); -} - -void keyboard_pad_handler::mousePressEvent(QMouseEvent* event) -{ - if (!event) [[unlikely]] - { - return; - } - - Key(event->button(), true); - event->ignore(); -} - -void keyboard_pad_handler::mouseReleaseEvent(QMouseEvent* event) -{ - if (!event) [[unlikely]] - { - return; - } - - Key(event->button(), false, 0); - event->ignore(); -} - -bool keyboard_pad_handler::get_mouse_lock_state() const -{ - if (gs_frame* game_frame = dynamic_cast(m_target)) - return game_frame->get_mouse_lock_state(); - return false; -} - -void keyboard_pad_handler::mouseMoveEvent(QMouseEvent* event) -{ - if (!m_mouse_move_used || !event) - { - event->ignore(); - return; - } - - static int movement_x = 0; - static int movement_y = 0; - - if (m_target && m_target->isActive() && get_mouse_lock_state()) - { - // get the screen dimensions - const QSize screen = m_target->size(); - - // get the center of the screen in global coordinates - QPoint p_center = m_target->geometry().topLeft() + QPoint(screen.width() / 2, screen.height() / 2); - - // reset the mouse to the center for consistent results since edge movement won't be registered - QCursor::setPos(m_target->screen(), p_center); - - // convert the center into screen coordinates - p_center = m_target->mapFromGlobal(p_center); - - // get the delta of the mouse position to the screen center - const QPoint p_delta = event->pos() - p_center; - - if (m_mouse_movement_mode == mouse_movement_mode::relative) - { - movement_x = p_delta.x(); - movement_y = p_delta.y(); - } - else - { - // current mouse position, starting at the center - static QPoint p_real(p_center); - - // update the current position without leaving the screen borders - p_real.setX(std::clamp(p_real.x() + p_delta.x(), 0, screen.width())); - p_real.setY(std::clamp(p_real.y() + p_delta.y(), 0, screen.height())); - - // get the delta of the real mouse position to the screen center - const QPoint p_real_delta = p_real - p_center; - - movement_x = p_real_delta.x(); - movement_y = p_real_delta.y(); - } - } - else if (m_mouse_movement_mode == mouse_movement_mode::relative) - { - static int last_pos_x = 0; - static int last_pos_y = 0; - const QPoint e_pos = event->pos(); - - movement_x = e_pos.x() - last_pos_x; - movement_y = e_pos.y() - last_pos_y; - - last_pos_x = e_pos.x(); - last_pos_y = e_pos.y(); - } - else if (m_target && m_target->isActive()) - { - // get the screen dimensions - const QSize screen = m_target->size(); - - // get the center of the screen in global coordinates - QPoint p_center = m_target->geometry().topLeft() + QPoint(screen.width() / 2, screen.height() / 2); - - // convert the center into screen coordinates - p_center = m_target->mapFromGlobal(p_center); - - // get the delta of the mouse position to the screen center - const QPoint p_delta = event->pos() - p_center; - - movement_x = p_delta.x(); - movement_y = p_delta.y(); - } - - movement_x *= m_multi_x; - movement_y *= m_multi_y; - - int deadzone_x = 0; - int deadzone_y = 0; - - if (movement_x == 0 && movement_y != 0) - { - deadzone_y = m_deadzone_y; - } - else if (movement_y == 0 && movement_x != 0) - { - deadzone_x = m_deadzone_x; - } - else if (movement_x != 0 && movement_y != 0 && m_deadzone_x != 0 && m_deadzone_y != 0) - { - // Calculate the point on our deadzone ellipsis intersected with the line (0, 0)(movement_x, movement_y) - // Ellipsis: 1 = (x²/a²) + (y²/b²) ; where: a = m_deadzone_x and b = m_deadzone_y - // Line: y = mx + t ; where: t = 0 and m = (movement_y / movement_x) - // Combined: x = +-(a*b)/sqrt(a²m²+b²) ; where +- is always +, since we only want the magnitude - - const double a = m_deadzone_x; - const double b = m_deadzone_y; - const double m = static_cast(movement_y) / static_cast(movement_x); - - deadzone_x = a * b / std::sqrt(std::pow(a, 2) * std::pow(m, 2) + std::pow(b, 2)); - deadzone_y = std::abs(m * deadzone_x); - } - - if (movement_x < 0) - { - Key(mouse::move_right, false); - Key(mouse::move_left, true, std::min(deadzone_x + std::abs(movement_x), 255)); - m_last_mouse_move_left = steady_clock::now(); - } - else if (movement_x > 0) - { - Key(mouse::move_left, false); - Key(mouse::move_right, true, std::min(deadzone_x + movement_x, 255)); - m_last_mouse_move_right = steady_clock::now(); - } - - // in Qt mouse up is equivalent to movement_y < 0 - if (movement_y < 0) - { - Key(mouse::move_down, false); - Key(mouse::move_up, true, std::min(deadzone_y + std::abs(movement_y), 255)); - m_last_mouse_move_up = steady_clock::now(); - } - else if (movement_y > 0) - { - Key(mouse::move_up, false); - Key(mouse::move_down, true, std::min(deadzone_y + movement_y, 255)); - m_last_mouse_move_down = steady_clock::now(); - } - - event->ignore(); -} - -void keyboard_pad_handler::mouseWheelEvent(QWheelEvent* event) -{ - if (!m_mouse_wheel_used || !event) - { - return; - } - - const QPoint direction = event->angleDelta(); - - if (direction.isNull()) - { - // Scrolling started/ended event, no direction given - return; - } - - if (const int x = direction.x()) - { - const bool to_left = event->inverted() ? x < 0 : x > 0; - - if (to_left) - { - Key(mouse::wheel_left, true); - m_last_wheel_move_left = steady_clock::now(); - } - else - { - Key(mouse::wheel_right, true); - m_last_wheel_move_right = steady_clock::now(); - } - } - if (const int y = direction.y()) - { - const bool to_up = event->inverted() ? y < 0 : y > 0; - - if (to_up) - { - Key(mouse::wheel_up, true); - m_last_wheel_move_up = steady_clock::now(); - } - else - { - Key(mouse::wheel_down, true); - m_last_wheel_move_down = steady_clock::now(); - } - } -} - -std::vector keyboard_pad_handler::list_devices() -{ - std::vector list_devices; - list_devices.emplace_back(std::string(pad::keyboard_device_name), false); - return list_devices; -} - -std::string keyboard_pad_handler::GetMouseName(const QMouseEvent* event) -{ - return GetMouseName(event->button()); -} - -std::string keyboard_pad_handler::GetMouseName(u32 button) -{ - if (const auto it = mouse_list.find(button); it != mouse_list.cend()) - return it->second; - return "FAIL"; -} - -QStringList keyboard_pad_handler::GetKeyNames(const QKeyEvent* keyEvent) -{ - QStringList list; - - if (keyEvent->modifiers() & Qt::ShiftModifier) - { - list.append("Shift"); - list.append(QKeySequence(keyEvent->key() | Qt::ShiftModifier).toString(QKeySequence::NativeText)); - } - if (keyEvent->modifiers() & Qt::AltModifier) - { - list.append("Alt"); - list.append(QKeySequence(keyEvent->key() | Qt::AltModifier).toString(QKeySequence::NativeText)); - } - if (keyEvent->modifiers() & Qt::ControlModifier) - { - list.append("Ctrl"); - list.append(QKeySequence(keyEvent->key() | Qt::ControlModifier).toString(QKeySequence::NativeText)); - } - if (keyEvent->modifiers() & Qt::MetaModifier) - { - list.append("Meta"); - list.append(QKeySequence(keyEvent->key() | Qt::MetaModifier).toString(QKeySequence::NativeText)); - } - if (keyEvent->modifiers() & Qt::KeypadModifier) - { - list.append("Num"); // helper object, not used as actual key - list.append(QKeySequence(keyEvent->key() | Qt::KeypadModifier).toString(QKeySequence::NativeText)); - } - - // Handle special cases - if (const std::string name = native_scan_code_to_string(keyEvent->nativeScanCode()); !name.empty()) - { - list.append(QString::fromStdString(name)); - } - - switch (keyEvent->key()) - { - case Qt::Key_Alt: - list.append("Alt"); - break; - case Qt::Key_AltGr: - list.append("AltGr"); - break; - case Qt::Key_Shift: - list.append("Shift"); - break; - case Qt::Key_Control: - list.append("Ctrl"); - break; - case Qt::Key_Meta: - list.append("Meta"); - break; - default: - list.append(QKeySequence(keyEvent->key()).toString(QKeySequence::NativeText)); - break; - } - - list.removeDuplicates(); - return list; -} - -std::string keyboard_pad_handler::GetKeyName(const QKeyEvent* keyEvent, bool with_modifiers) -{ - // Handle special cases first - if (std::string name = native_scan_code_to_string(keyEvent->nativeScanCode()); !name.empty()) - { - return name; - } - - switch (keyEvent->key()) - { - case Qt::Key_Alt: return "Alt"; - case Qt::Key_AltGr: return "AltGr"; - case Qt::Key_Shift: return "Shift"; - case Qt::Key_Control: return "Ctrl"; - case Qt::Key_Meta: return "Meta"; - case Qt::Key_NumLock: return QKeySequence(keyEvent->key()).toString(QKeySequence::NativeText).toStdString(); -#ifdef __APPLE__ - // On macOS, the arrow keys are considered to be part of the keypad; - // since most Mac keyboards lack a keypad to begin with, - // we change them to regular arrows to avoid confusion - case Qt::Key_Left: return "←"; - case Qt::Key_Up: return "↑"; - case Qt::Key_Right: return "→"; - case Qt::Key_Down: return "↓"; -#endif - default: - break; - } - - if (with_modifiers) - { - return QKeySequence(keyEvent->key() | keyEvent->modifiers()).toString(QKeySequence::NativeText).toStdString(); - } - - return QKeySequence(keyEvent->key()).toString(QKeySequence::NativeText).toStdString(); -} - -std::string keyboard_pad_handler::GetKeyName(const u32& keyCode) -{ - return QKeySequence(keyCode).toString(QKeySequence::NativeText).toStdString(); -} - -std::set keyboard_pad_handler::GetKeyCodes(const cfg::string& cfg_string) -{ - std::set key_codes; - for (const std::string& key_name : cfg_pad::get_buttons(cfg_string)) - { - if (u32 code = GetKeyCode(QString::fromStdString(key_name)); code != Qt::NoButton) - { - key_codes.insert(code); - } - } - return key_codes; -} - -u32 keyboard_pad_handler::GetKeyCode(const QString& keyName) -{ - if (keyName.isEmpty()) - return Qt::NoButton; - if (const int native_scan_code = native_scan_code_from_string(keyName.toStdString()); native_scan_code >= 0) - return Qt::Key_unknown + native_scan_code; // Special cases that can't be expressed with Qt::Key - if (keyName == "Alt") - return Qt::Key_Alt; - if (keyName == "AltGr") - return Qt::Key_AltGr; - if (keyName == "Shift") - return Qt::Key_Shift; - if (keyName == "Ctrl") - return Qt::Key_Control; - if (keyName == "Meta") - return Qt::Key_Meta; -#ifdef __APPLE__ - // QKeySequence doesn't work properly for the arrow keys on macOS - if (keyName == "Num←") - return Qt::Key_Left; - if (keyName == "Num↑") - return Qt::Key_Up; - if (keyName == "Num→") - return Qt::Key_Right; - if (keyName == "Num↓") - return Qt::Key_Down; -#endif - - const QKeySequence seq(keyName); - u32 key_code = Qt::NoButton; - - if (seq.count() == 1 && seq[0].key() != Qt::Key_unknown) - key_code = seq[0].key(); - else - input_log.notice("GetKeyCode(%s): seq.count() = %d", keyName, seq.count()); - - return key_code; -} - -int keyboard_pad_handler::native_scan_code_from_string([[maybe_unused]] const std::string& key) -{ - // NOTE: Qt throws a Ctrl key at us when using Alt Gr first, so right Alt does not work at the moment - if (key == "Shift Left") - return native_key::shift_l; - if (key == "Shift Right") - return native_key::shift_r; - if (key == "Ctrl Left") - return native_key::ctrl_l; - if (key == "Ctrl Right") - return native_key::ctrl_r; - if (key == "Alt Left") - return native_key::alt_l; - if (key == "Alt Right") - return native_key::alt_r; - if (key == "Meta Left") - return native_key::meta_l; - if (key == "Meta Right") - return native_key::meta_r; -#ifdef _WIN32 - if (key == "Num+0" || key == "Num+Ins") - return 82; - if (key == "Num+1" || key == "Num+End") - return 79; - if (key == "Num+2" || key == "Num+Down") - return 80; - if (key == "Num+3" || key == "Num+PgDown") - return 81; - if (key == "Num+4" || key == "Num+Left") - return 75; - if (key == "Num+5" || key == "Num+Clear") - return 76; - if (key == "Num+6" || key == "Num+Right") - return 77; - if (key == "Num+7" || key == "Num+Home") - return 71; - if (key == "Num+8" || key == "Num+Up") - return 72; - if (key == "Num+9" || key == "Num+PgUp") - return 73; - if (key == "Num+," || key == "Num+Del") - return 83; - if (key == "Num+/") - return 309; - if (key == "Num+*") - return 55; - if (key == "Num+-") - return 74; - if (key == "Num++") - return 78; - if (key == "Num+Enter") - return 284; -#else - // TODO -#endif - return -1; -} - -std::string keyboard_pad_handler::native_scan_code_to_string(int native_scan_code) -{ - // NOTE: the other Qt function "nativeVirtualKey" does not distinguish between VK_SHIFT and VK_RSHIFT key in Qt at the moment - // NOTE: Qt throws a Ctrl key at us when using Alt Gr first, so right Alt does not work at the moment - // NOTE: for MacOs: nativeScanCode may not work - switch (native_scan_code) - { - case native_key::shift_l: return "Shift Left"; - case native_key::shift_r: return "Shift Right"; - case native_key::ctrl_l: return "Ctrl Left"; - case native_key::ctrl_r: return "Ctrl Right"; - case native_key::alt_l: return "Alt Left"; - case native_key::alt_r: return "Alt Right"; - case native_key::meta_l: return "Meta Left"; - case native_key::meta_r: return "Meta Right"; -#ifdef _WIN32 - case 82: return "Num+0"; // Also "Num+Ins" depending on numlock - case 79: return "Num+1"; // Also "Num+End" depending on numlock - case 80: return "Num+2"; // Also "Num+Down" depending on numlock - case 81: return "Num+3"; // Also "Num+PgDown" depending on numlock - case 75: return "Num+4"; // Also "Num+Left" depending on numlock - case 76: return "Num+5"; // Also "Num+Clear" depending on numlock - case 77: return "Num+6"; // Also "Num+Right" depending on numlock - case 71: return "Num+7"; // Also "Num+Home" depending on numlock - case 72: return "Num+8"; // Also "Num+Up" depending on numlock - case 73: return "Num+9"; // Also "Num+PgUp" depending on numlock - case 83: return "Num+,"; // Also "Num+Del" depending on numlock - case 309: return "Num+/"; - case 55: return "Num+*"; - case 74: return "Num+-"; - case 78: return "Num++"; - case 284: return "Num+Enter"; -#else - // TODO -#endif - default: return ""; - } -} - -bool keyboard_pad_handler::bindPadToDevice(std::shared_ptr pad) -{ - if (!pad || pad->m_player_id >= g_cfg_input.player.size()) - return false; - - const cfg_player* player_config = g_cfg_input.player[pad->m_player_id]; - if (!player_config || player_config->device.to_string() != pad::keyboard_device_name) - return false; - - m_pad_configs[pad->m_player_id].from_string(player_config->config.to_string()); - const cfg_pad* cfg = &m_pad_configs[pad->m_player_id]; - if (cfg == nullptr) - return false; - - m_mouse_movement_mode = cfg->mouse_move_mode; - m_mouse_move_used = false; - m_mouse_wheel_used = false; - m_deadzone_x = cfg->mouse_deadzone_x; - m_deadzone_y = cfg->mouse_deadzone_y; - m_multi_x = cfg->mouse_acceleration_x / 100.0; - m_multi_y = cfg->mouse_acceleration_y / 100.0; - m_l_stick_lerp_factor = cfg->l_stick_lerp_factor / 100.0f; - m_r_stick_lerp_factor = cfg->r_stick_lerp_factor / 100.0f; - m_analog_lerp_factor = cfg->analog_lerp_factor / 100.0f; - m_trigger_lerp_factor = cfg->trigger_lerp_factor / 100.0f; - m_l_stick_multiplier = cfg->lstickmultiplier; - m_r_stick_multiplier = cfg->rstickmultiplier; - m_analog_limiter_toggle_mode = cfg->analog_limiter_toggle_mode.get(); - m_pressure_intensity_toggle_mode = cfg->pressure_intensity_toggle_mode.get(); - m_pressure_intensity_deadzone = cfg->pressure_intensity_deadzone.get(); - - const auto find_keys = [this](const cfg::string& name) - { - std::set keys = FindKeyCodes(mouse_list, name, false); - for (const u32& key : GetKeyCodes(name)) - keys.insert(key); - - if (!keys.empty()) - { - if (!m_mouse_move_used && (keys.contains(mouse::move_left) || keys.contains(mouse::move_right) || keys.contains(mouse::move_up) || keys.contains(mouse::move_down))) - { - m_mouse_move_used = true; - } - else if (!m_mouse_wheel_used && (keys.contains(mouse::wheel_left) || keys.contains(mouse::wheel_right) || keys.contains(mouse::wheel_up) || keys.contains(mouse::wheel_down))) - { - m_mouse_wheel_used = true; - } - } - return keys; - }; - - u32 pclass_profile = 0x0; - - for (const input::product_info& product : input::get_products_by_class(cfg->device_class_type)) - { - if (product.vendor_id == cfg->vendor_id && product.product_id == cfg->product_id) - { - pclass_profile = product.pclass_profile; - } - } - - // Fixed assign change, default is both sensor and press off - pad->Init( - CELL_PAD_STATUS_DISCONNECTED, - CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE, - CELL_PAD_DEV_TYPE_STANDARD, - cfg->device_class_type, - pclass_profile, - cfg->vendor_id, - cfg->product_id, - cfg->pressure_intensity); - - if (b_has_pressure_intensity_button) - { - pad->m_buttons.emplace_back(special_button_offset, find_keys(cfg->pressure_intensity_button), special_button_value::pressure_intensity); - pad->m_pressure_intensity_button_index = static_cast(pad->m_buttons.size()) - 1; - } - - if (b_has_analog_limiter_button) - { - pad->m_buttons.emplace_back(special_button_offset, find_keys(cfg->analog_limiter_button), special_button_value::analog_limiter); - pad->m_analog_limiter_button_index = static_cast(pad->m_buttons.size()) - 1; - } - - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->left), CELL_PAD_CTRL_LEFT); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->down), CELL_PAD_CTRL_DOWN); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->right), CELL_PAD_CTRL_RIGHT); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->up), CELL_PAD_CTRL_UP); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->start), CELL_PAD_CTRL_START); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->r3), CELL_PAD_CTRL_R3); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->l3), CELL_PAD_CTRL_L3); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->select), CELL_PAD_CTRL_SELECT); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->ps), CELL_PAD_CTRL_PS); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->square), CELL_PAD_CTRL_SQUARE); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->cross), CELL_PAD_CTRL_CROSS); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->circle), CELL_PAD_CTRL_CIRCLE); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->triangle), CELL_PAD_CTRL_TRIANGLE); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->r1), CELL_PAD_CTRL_R1); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->l1), CELL_PAD_CTRL_L1); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->r2), CELL_PAD_CTRL_R2); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->l2), CELL_PAD_CTRL_L2); - - if (pad->m_class_type == CELL_PAD_PCLASS_TYPE_SKATEBOARD) - { - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->ir_nose), CELL_PAD_CTRL_PRESS_TRIANGLE); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->ir_tail), CELL_PAD_CTRL_PRESS_CIRCLE); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->ir_left), CELL_PAD_CTRL_PRESS_CROSS); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->ir_right), CELL_PAD_CTRL_PRESS_SQUARE); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->tilt_left), CELL_PAD_CTRL_PRESS_L1); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->tilt_right), CELL_PAD_CTRL_PRESS_R1); - } - - pad->m_sticks[0] = AnalogStick(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, find_keys(cfg->ls_left), find_keys(cfg->ls_right)); - pad->m_sticks[1] = AnalogStick(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, find_keys(cfg->ls_up), find_keys(cfg->ls_down)); - pad->m_sticks[2] = AnalogStick(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, find_keys(cfg->rs_left), find_keys(cfg->rs_right)); - pad->m_sticks[3] = AnalogStick(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, find_keys(cfg->rs_up), find_keys(cfg->rs_down)); - - pad->m_sensors[0] = AnalogSensor(CELL_PAD_BTN_OFFSET_SENSOR_X, 0, 0, 0, DEFAULT_MOTION_X); - pad->m_sensors[1] = AnalogSensor(CELL_PAD_BTN_OFFSET_SENSOR_Y, 0, 0, 0, DEFAULT_MOTION_Y); - pad->m_sensors[2] = AnalogSensor(CELL_PAD_BTN_OFFSET_SENSOR_Z, 0, 0, 0, DEFAULT_MOTION_Z); - pad->m_sensors[3] = AnalogSensor(CELL_PAD_BTN_OFFSET_SENSOR_G, 0, 0, 0, DEFAULT_MOTION_G); - - pad->m_vibrateMotors[0] = VibrateMotor(true, 0); - pad->m_vibrateMotors[1] = VibrateMotor(false, 0); - - m_bindings.emplace_back(pad, nullptr, nullptr); - m_pads_internal.push_back(*pad); - - return true; -} - -void keyboard_pad_handler::process() -{ - constexpr double stick_interval = 10.0; - constexpr double button_interval = 10.0; - - const auto now = steady_clock::now(); - - const double elapsed_stick = std::chrono::duration_cast(now - m_stick_time).count() / 1000.0; - const double elapsed_button = std::chrono::duration_cast(now - m_button_time).count() / 1000.0; - - const bool update_sticks = elapsed_stick > stick_interval; - const bool update_buttons = elapsed_button > button_interval; - - if (update_sticks) - { - m_stick_time = now; - } - - if (update_buttons) - { - m_button_time = now; - } - - if (m_mouse_move_used && m_mouse_movement_mode == mouse_movement_mode::relative) - { - constexpr double mouse_interval = 30.0; - - const double elapsed_left = std::chrono::duration_cast(now - m_last_mouse_move_left).count() / 1000.0; - const double elapsed_right = std::chrono::duration_cast(now - m_last_mouse_move_right).count() / 1000.0; - const double elapsed_up = std::chrono::duration_cast(now - m_last_mouse_move_up).count() / 1000.0; - const double elapsed_down = std::chrono::duration_cast(now - m_last_mouse_move_down).count() / 1000.0; - - // roughly 1-2 frames to process the next mouse move - if (elapsed_left > mouse_interval) - { - Key(mouse::move_left, false); - m_last_mouse_move_left = now; - } - if (elapsed_right > mouse_interval) - { - Key(mouse::move_right, false); - m_last_mouse_move_right = now; - } - if (elapsed_up > mouse_interval) - { - Key(mouse::move_up, false); - m_last_mouse_move_up = now; - } - if (elapsed_down > mouse_interval) - { - Key(mouse::move_down, false); - m_last_mouse_move_down = now; - } - } - - const auto get_lerped = [](f32 v0, f32 v1, f32 lerp_factor) - { - // linear interpolation from the current value v0 to the desired value v1 - const f32 res = std::lerp(v0, v1, lerp_factor); - - // round to the correct direction to prevent sticky values on small factors - return (v0 <= v1) ? std::ceil(res) : std::floor(res); - }; - - for (uint i = 0; i < m_pads_internal.size(); i++) - { - auto& pad = m_pads_internal[i]; - - if (last_connection_status[i] == false) - { - ensure(m_bindings[i].pad); - m_bindings[i].pad->m_port_status |= CELL_PAD_STATUS_CONNECTED; - m_bindings[i].pad->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES; - last_connection_status[i] = true; - connected_devices++; - } - else - { - if (update_sticks) - { - for (usz j = 0; j < pad.m_sticks.size(); j++) - { - const f32 stick_lerp_factor = (j < 2) ? m_l_stick_lerp_factor : m_r_stick_lerp_factor; - - // we already applied the following values on keypress if we used factor 1 - if (stick_lerp_factor < 1.0f) - { - const f32 v0 = static_cast(pad.m_sticks[j].m_value); - const f32 v1 = static_cast(m_stick_val[j]); - const f32 res = get_lerped(v0, v1, stick_lerp_factor); - - pad.m_sticks[j].m_value = static_cast(res); - } - } - } - - if (update_buttons) - { - for (Button& button : pad.m_buttons) - { - if (button.m_analog) - { - // we already applied the following values on keypress if we used factor 1 - if (m_analog_lerp_factor < 1.0f) - { - const f32 v0 = static_cast(button.m_value); - const f32 v1 = static_cast(button.m_actual_value); - const f32 res = get_lerped(v0, v1, m_analog_lerp_factor); - - button.m_value = static_cast(res); - button.m_pressed = button.m_value > 0; - } - } - else if (button.m_trigger) - { - // we already applied the following values on keypress if we used factor 1 - if (m_trigger_lerp_factor < 1.0f) - { - const f32 v0 = static_cast(button.m_value); - const f32 v1 = static_cast(button.m_actual_value); - const f32 res = get_lerped(v0, v1, m_trigger_lerp_factor); - - button.m_value = static_cast(res); - button.m_pressed = button.m_value > 0; - } - } - } - } - } - } - - if (m_mouse_wheel_used) - { - // Releases the wheel buttons 0,1 sec after they've been triggered - // Next activation is set to distant future to avoid activating this on every proc - const auto update_threshold = now - std::chrono::milliseconds(100); - const auto distant_future = now + std::chrono::hours(24); - - if (update_threshold >= m_last_wheel_move_up) - { - Key(mouse::wheel_up, false); - m_last_wheel_move_up = distant_future; - } - if (update_threshold >= m_last_wheel_move_down) - { - Key(mouse::wheel_down, false); - m_last_wheel_move_down = distant_future; - } - if (update_threshold >= m_last_wheel_move_left) - { - Key(mouse::wheel_left, false); - m_last_wheel_move_left = distant_future; - } - if (update_threshold >= m_last_wheel_move_right) - { - Key(mouse::wheel_right, false); - m_last_wheel_move_right = distant_future; - } - } - - for (uint i = 0; i < m_bindings.size(); i++) - { - auto& pad = m_bindings[i].pad; - ensure(pad); - - const cfg_pad* cfg = &m_pad_configs[pad->m_player_id]; - ensure(cfg); - - const Pad& pad_internal = m_pads_internal[i]; - - // Normalize and apply pad squircling - // Copy sticks first. We don't want to modify the raw internal values - std::array squircled_sticks = pad_internal.m_sticks; - - // Apply squircling - if (cfg->lpadsquircling != 0) - { - u16& lx = squircled_sticks[0].m_value; - u16& ly = squircled_sticks[1].m_value; - - ConvertToSquirclePoint(lx, ly, cfg->lpadsquircling); - } - - if (cfg->rpadsquircling != 0) - { - u16& rx = squircled_sticks[2].m_value; - u16& ry = squircled_sticks[3].m_value; - - ConvertToSquirclePoint(rx, ry, cfg->rpadsquircling); - } - - pad->m_buttons = pad_internal.m_buttons; - pad->m_sticks = squircled_sticks; // Don't use std::move here. We assign values lockless, so std::move can lead to segfaults. - } -} diff --git a/rpcs3qt-legacy/Input/keyboard_pad_handler.h b/rpcs3qt-legacy/Input/keyboard_pad_handler.h deleted file mode 100644 index a00b22329..000000000 --- a/rpcs3qt-legacy/Input/keyboard_pad_handler.h +++ /dev/null @@ -1,154 +0,0 @@ -#pragma once - -#include "util/types.hpp" -#include "Emu/Io/PadHandler.h" - -#include -#include -#include -#include -#include - -enum mouse -{ - move_left = 0x05555550, - move_right = 0x05555551, - move_up = 0x05555552, - move_down = 0x05555553, - wheel_up = 0x05555554, - wheel_down = 0x05555555, - wheel_left = 0x05555556, - wheel_right = 0x05555557 -}; - -// Unique button names for the config files and our pad settings dialog -const std::unordered_map mouse_list = - { - {Qt::NoButton, ""}, - {Qt::LeftButton, "Mouse Left"}, - {Qt::RightButton, "Mouse Right"}, - {Qt::MiddleButton, "Mouse Middle"}, - {Qt::BackButton, "Mouse Back"}, - {Qt::ForwardButton, "Mouse Fwd"}, - {Qt::TaskButton, "Mouse Task"}, - {Qt::ExtraButton4, "Mouse 4"}, - {Qt::ExtraButton5, "Mouse 5"}, - {Qt::ExtraButton6, "Mouse 6"}, - {Qt::ExtraButton7, "Mouse 7"}, - {Qt::ExtraButton8, "Mouse 8"}, - {Qt::ExtraButton9, "Mouse 9"}, - {Qt::ExtraButton10, "Mouse 10"}, - {Qt::ExtraButton11, "Mouse 11"}, - {Qt::ExtraButton12, "Mouse 12"}, - {Qt::ExtraButton13, "Mouse 13"}, - {Qt::ExtraButton14, "Mouse 14"}, - {Qt::ExtraButton15, "Mouse 15"}, - {Qt::ExtraButton16, "Mouse 16"}, - {Qt::ExtraButton17, "Mouse 17"}, - {Qt::ExtraButton18, "Mouse 18"}, - {Qt::ExtraButton19, "Mouse 19"}, - {Qt::ExtraButton20, "Mouse 20"}, - {Qt::ExtraButton21, "Mouse 21"}, - {Qt::ExtraButton22, "Mouse 22"}, - {Qt::ExtraButton23, "Mouse 23"}, - {Qt::ExtraButton24, "Mouse 24"}, - - {mouse::move_left, "Mouse MLeft"}, - {mouse::move_right, "Mouse MRight"}, - {mouse::move_up, "Mouse MUp"}, - {mouse::move_down, "Mouse MDown"}, - - {mouse::wheel_up, "Wheel Up"}, - {mouse::wheel_down, "Wheel Down"}, - {mouse::wheel_left, "Wheel Left"}, - {mouse::wheel_right, "Wheel Right"}, -}; - -class keyboard_pad_handler final : public QObject, public PadHandlerBase -{ -public: - bool Init() override; - - keyboard_pad_handler(); - - void SetTargetWindow(QWindow* target); - void processKeyEvent(QKeyEvent* event, bool pressed); - void keyPressEvent(QKeyEvent* event); - void keyReleaseEvent(QKeyEvent* event); - void mousePressEvent(QMouseEvent* event); - void mouseReleaseEvent(QMouseEvent* event); - void mouseMoveEvent(QMouseEvent* event); - void mouseWheelEvent(QWheelEvent* event); - - bool eventFilter(QObject* target, QEvent* ev) override; - - void init_config(cfg_pad* cfg) override; - std::vector list_devices() override; - connection get_next_button_press(const std::string& /*padId*/, const pad_callback& /*callback*/, const pad_fail_callback& /*fail_callback*/, gui_call_type /*call_type*/, const std::vector& /*buttons*/) override - { - return connection::connected; - } - bool bindPadToDevice(std::shared_ptr pad) override; - void process() override; - - static std::string GetMouseName(const QMouseEvent* event); - static std::string GetMouseName(u32 button); - static QStringList GetKeyNames(const QKeyEvent* keyEvent); - static std::string GetKeyName(const QKeyEvent* keyEvent, bool with_modifiers); - static std::string GetKeyName(const u32& keyCode); - static std::set GetKeyCodes(const cfg::string& cfg_string); - static u32 GetKeyCode(const QString& keyName); - - static int native_scan_code_from_string(const std::string& key); - static std::string native_scan_code_to_string(int native_scan_code); - -protected: - void Key(const u32 code, bool pressed, u16 value = 255); - -private: - QWindow* m_target = nullptr; - mouse_movement_mode m_mouse_movement_mode = mouse_movement_mode::relative; - bool m_keys_released = false; - bool m_mouse_move_used = false; - bool m_mouse_wheel_used = false; - bool get_mouse_lock_state() const; - void release_all_keys(); - - std::vector m_pads_internal; // Accumulates input until the next poll. Only used for user input! - - // Button Movements - steady_clock::time_point m_button_time; - f32 m_analog_lerp_factor = 1.0f; - f32 m_trigger_lerp_factor = 1.0f; - bool m_analog_limiter_toggle_mode = false; - bool m_pressure_intensity_toggle_mode = false; - u32 m_pressure_intensity_deadzone = 0; - - // Stick Movements - steady_clock::time_point m_stick_time; - f32 m_l_stick_lerp_factor = 1.0f; - f32 m_r_stick_lerp_factor = 1.0f; - u32 m_l_stick_multiplier = 100; - u32 m_r_stick_multiplier = 100; - - static constexpr usz max_sticks = 4; - std::array m_stick_min{0, 0, 0, 0}; - std::array m_stick_max{128, 128, 128, 128}; - std::array m_stick_val{128, 128, 128, 128}; - - // Mouse Movements - steady_clock::time_point m_last_mouse_move_left; - steady_clock::time_point m_last_mouse_move_right; - steady_clock::time_point m_last_mouse_move_up; - steady_clock::time_point m_last_mouse_move_down; - int m_deadzone_x = 60; - int m_deadzone_y = 60; - double m_multi_x = 2; - double m_multi_y = 2.5; - - // Mousewheel - steady_clock::time_point m_last_wheel_move_up; - steady_clock::time_point m_last_wheel_move_down; - steady_clock::time_point m_last_wheel_move_left; - steady_clock::time_point m_last_wheel_move_right; -}; diff --git a/rpcs3qt-legacy/_discord_utils.cpp b/rpcs3qt-legacy/_discord_utils.cpp deleted file mode 100644 index 8c2d227f3..000000000 --- a/rpcs3qt-legacy/_discord_utils.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifdef WITH_DISCORD_RPC -#include "_discord_utils.h" -#include "discord_rpc.h" - -#include -#include - -namespace discord -{ - void initialize(const std::string& application_id) - { - DiscordEventHandlers handlers = {}; - Discord_Initialize(application_id.c_str(), &handlers, 1, nullptr); - } - - void shutdown() - { - Discord_Shutdown(); - } - - void update_presence(const std::string& state, const std::string& details, bool reset_timer) - { - DiscordRichPresence discordPresence = {}; - discordPresence.details = details.c_str(); - discordPresence.state = state.c_str(); - discordPresence.largeImageKey = "rpcs3_logo"; - discordPresence.largeImageText = "RPCS3 is the world's first PlayStation 3 emulator."; - - if (reset_timer) - { - discordPresence.startTimestamp = std::time(nullptr); - } - - Discord_UpdatePresence(&discordPresence); - } -} // namespace discord -#endif diff --git a/rpcs3qt-legacy/_discord_utils.h b/rpcs3qt-legacy/_discord_utils.h deleted file mode 100644 index 638c208e3..000000000 --- a/rpcs3qt-legacy/_discord_utils.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -namespace discord -{ - // Convenience function for initialization - void initialize(const std::string& application_id = "424004941485572097"); - - // Convenience function for shutdown - void shutdown(); - - // Convenience function for status updates. The default is set to idle. - void update_presence(const std::string& state = "", const std::string& details = "Idle", bool reset_timer = true); -} // namespace discord diff --git a/rpcs3qt-legacy/about_dialog.cpp b/rpcs3qt-legacy/about_dialog.cpp deleted file mode 100644 index 71952446a..000000000 --- a/rpcs3qt-legacy/about_dialog.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "about_dialog.h" -#include "ui_about_dialog.h" - -#include "rpcs3_version.h" -#include "qt_utils.h" - -#include -#include -#include - -about_dialog::about_dialog(QWidget* parent) : QDialog(parent), ui(new Ui::about_dialog) -{ - ui->setupUi(this); - setAttribute(Qt::WA_DeleteOnClose); - - ui->close->setDefault(true); - ui->icon->load(QStringLiteral(":/rpcs3.svg")); - ui->version->setText(tr("RPCS3 Version: %1").arg(QString::fromStdString(rpcs3::get_verbose_version()))); - ui->description->setText(gui::utils::make_paragraph(tr( - "RPCS3 is an open-source Sony PlayStation 3 emulator and debugger.\n" - "It is written in C++ for Windows, Linux, FreeBSD and MacOS, funded with %0.\n" - "Our developers and contributors are always working hard to ensure this project is the best that it can be.\n" - "There are still plenty of implementations to make and optimizations to do.") - .arg(gui::utils::make_link(tr("Patreon"), "https://rpcs3.net/patreon")))); - - // Events - connect(ui->gitHub, &QPushButton::clicked, [] - { - QDesktopServices::openUrl(QUrl("https://www.github.com/RPCS3")); - }); - connect(ui->website, &QPushButton::clicked, [] - { - QDesktopServices::openUrl(QUrl("https://rpcs3.net")); - }); - connect(ui->forum, &QPushButton::clicked, [] - { - QDesktopServices::openUrl(QUrl("https://forums.rpcs3.net")); - }); - connect(ui->patreon, &QPushButton::clicked, [] - { - QDesktopServices::openUrl(QUrl("https://rpcs3.net/patreon")); - }); - connect(ui->discord, &QPushButton::clicked, [] - { - QDesktopServices::openUrl(QUrl("https://discord.me/RPCS3")); - }); - connect(ui->wiki, &QPushButton::clicked, [] - { - QDesktopServices::openUrl(QUrl("https://wiki.rpcs3.net/index.php?title=Main_Page")); - }); - connect(ui->close, &QPushButton::clicked, this, &QWidget::close); -} - -about_dialog::~about_dialog() -{ -} diff --git a/rpcs3qt-legacy/about_dialog.h b/rpcs3qt-legacy/about_dialog.h deleted file mode 100644 index 91872ec6c..000000000 --- a/rpcs3qt-legacy/about_dialog.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include - -namespace Ui -{ - class about_dialog; -} - -class about_dialog : public QDialog -{ - Q_OBJECT - -public: - explicit about_dialog(QWidget* parent = nullptr); - ~about_dialog(); - -private: - std::unique_ptr ui; -}; diff --git a/rpcs3qt-legacy/about_dialog.ui b/rpcs3qt-legacy/about_dialog.ui deleted file mode 100644 index 67bb5762f..000000000 --- a/rpcs3qt-legacy/about_dialog.ui +++ /dev/null @@ -1,468 +0,0 @@ - - - about_dialog - - - - 0 - 0 - 805 - 555 - - - - About RPCS3 - - - - :/rpcs3.ico:/rpcs3.ico - - - - 6 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 50 - 50 - - - - - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::MinimumExpanding - - - - 20 - 0 - - - - - - - - - - - - - - Arial - 14 - false - - - - RPCS3 PlayStation 3 Emulator - - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - - - Qt::TextInteractionFlag::TextSelectableByMouse - - - - - - - - Arial - true - - - - Qt::TextFormat::RichText - - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - - - false - - - true - - - Qt::TextInteractionFlag::TextBrowserInteraction - - - - - - - - Arial - true - - - - - - - Qt::TextFormat::RichText - - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - - - true - - - Qt::TextInteractionFlag::TextBrowserInteraction - - - - - - - - - - - - - 0 - 0 - - - - - 0 - 200 - - - - QFrame::Shape::NoFrame - - - Qt::ScrollBarPolicy::ScrollBarAlwaysOn - - - QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents - - - true - - - - - 0 - 0 - 793 - 2638 - - - - - QLayout::SizeConstraint::SetMinimumSize - - - 24 - - - - - 10 - - - - - - true - - - - Developers: - - - - - - - <p>¬DH<br>¬AlexAltea<br>¬Hykem<br>¬Oil<br>Nekotekina<br>¬Bigpet<br>¬gopalsr83<br>¬tambry<br>¬vlj<br>kd-11<br>¬jarveson<br>¬raven02<br>AniLeo<br>¬cornytrace<br>¬ssshadow<br>¬Numan<br>hcorion<br>Megamouse<br>¬flash-fire<br>DAGINATSUKO<br>GalCiv<br>eladash</p> - - - false - - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - - - Qt::TextInteractionFlag::TextSelectableByMouse - - - - - - - - - 10 - - - - - - true - - - - Contributors: - - - - - - - <p>BlackDaemon<br>elisha464<br>Aishou<br>krofna<br>xsacha<br>danilaml<br>unknownbrackets<br>Zangetsu38<br>lioncash<br>achurch<br>darkf<br>Syphurith<br>Blaypeg<br>Survanium90<br>georgemoralis<br>ikki84<br>scribam<br>TGE<br>velocity<br>Farseer<br>Dangles<br>ruipin<br>jbeich<br>CookiePLMonster<br>Whatcookie<br>rajkosto<br>Admiral Thrawn<br>FlexBy<br>Dunastique<br>Jonathan44062<br>yurinator557<br>Satan<br>HoldTheMourning<br>illusion0001</p> - - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - - - Qt::TextInteractionFlag::TextSelectableByMouse - - - - - - - - - 10 - - - - - - true - - - - Supporters: - - - - - - - <p>Howard Garrison<br>EXPotemkin<br>Marko V.<br>danhp<br>Jake (5315825)<br>Ian Reid<br>Tad Sherlock<br>Tyler Friesen<br>Folzar<br>Payton Williams<br>RedPill Australia<br>yanghong<br>Mohammed El-Serougi<br>Дима ~Ximer13~ Кулин<br>James Reed<br>BaroqueSonata<br>Bonzay0<br>Henrijs Kons<br>eoiz<br>Lena Stöffler<br>Michael Holder<br>Max Bosse<br>Tyler Whisinnand<br>Gato Harvey<br>cain4355<br>Thomas Peltier<br>Loli Co.<br>MapleLoonie<br>Travis McEwen<br>Scott Singratsomboune<br>T.E<br>Lukas Rieger<br>Dane Madsen<br>JMS<br>Jonatan R<br>Luke Johnson<br>Thomas Zaorski<br>MyOwnFan<br>Alexandros Mandravillis<br>Socker Bopper<br>Faris Leonhart<br>Fabien Net<br>Raves<br>Barrowsx<br>kilsuton<br>Max Mason<br>Ethan Condon<br>jfidone<br>iaDRM<br>Kazer2.0<br>Bryce Quintin<br>Yuri Kunde Schlesner<br>Abdulla Altayer<br>Nicolas Jallamion<br>Vorvek<br>Ian Faddis<br>Leon<br>Mohammad Taleb<br>Jokez<br>crashX<br>Raveskirza<br>Grant Deacon<br>michael<br>David Zech<br>Ben Manoochehri<br>Adnan Kovacevic<br>Mighty J<br>Sam Shan Jiang<br>TheAnig<br>Rodney Coleman<br>FiniteAce<br>Kian Soon Alex Chiam<br>yukkuri<br>Justin Chadwick<br>toxic Itzi<br>Templerror<br>Myles Wesley Carlson<br>Max Bosse<br>Ethan Clark<br>LupineDream<br>CheatCodesOfLife<br>Jan Zykmund<br>Francesco Cinquemani<br>Andylg<br>Julia H de Camargos<br>Suvodip Mitra<br>Goh<br>Dmitry<br>Steel Brain<br>VarieZ<br>William Swango<br>Matthew Messersmith<br>Duane Locsin<br>Shuddertrix<br>Loweys Litsman<br>Shuddertrix<br>Mason Ferrie<br>Richard Kaplan<br>Hugues Valois<br>richard(lath..ch@)<br>Johnathan (Virtuous John)<br>eoiz<br>Dany Huguenin<br>doobieashtray<br>dean(mag..94@)<br>Pommier Jean-Philippe<br>Douglas Alan Albino<br>Ryan Mull<br>Thor-Erling Engen<br>Nick Carpenter<br>curryking3<br>Jared Tracton<br>alex(koo..oh@)<br>Jason O'Brien<br>Skeletal Charizard<br>Ace00<br>Brandon Corujo<br>HyperBitHero<br>佳文 李<br>sorryboi - <br>Johnson Bui - <br>itachi1986 - <br>Mortano - <br>Xythera - <br>Albert Quinteros - <br>Uzair Sheikh - <br>Ethan Hoppins - <br>optic - <br>Quill Slyver - <br>Averie - <br>StevenCarson - <br>YuriNator557 - <br>Deanmaxx - <br>linkQatar G - <br>Jack Collie - <br>TAL BERKOVITZ - <br>cjtk - <br>Comexzone - <br>mapleglass - <br>Liquidbings - <br>Dormant_Hero@0230 - <br>Theodore Raney - <br>Morito - <br>Chaining Ten - <br>Xeropel - <br>Marko Gatzouris - <br>Steven (...weller@gmail.com) - <br>Real Gamer - <br>Spencer Robinson - <br>naps - <br>Matthew Stevens - <br>Orion Clark - <br>William K. Leung - <br>Trent M - <br>Viktor Ni - <br>Marc Tönsing - <br>Amogus - <br>Scott Davis - <br>秉軒 侯 - <br>Wiktor Tkaczyński - <br>Vekkar - <br>Jackson Abney - </p> - - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - - - Qt::TextInteractionFlag::TextSelectableByMouse - - - - - - - - - Qt::Orientation::Horizontal - - - QSizePolicy::Policy::MinimumExpanding - - - - 0 - 20 - - - - - - - - - - - - 9 - - - 9 - - - 9 - - - 9 - - - - - GitHub - - - - - - - Website - - - - - - - Forum - - - - - - - Discord - - - - - - - Wiki - - - - - - - Patreon - - - - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - - - - - Close - - - - - - - - - - QSvgWidget - QWidget -

qsvgwidget.h
- 1 - - - - - - - diff --git a/rpcs3qt-legacy/auto_pause_settings_dialog.cpp b/rpcs3qt-legacy/auto_pause_settings_dialog.cpp deleted file mode 100644 index 2091381f9..000000000 --- a/rpcs3qt-legacy/auto_pause_settings_dialog.cpp +++ /dev/null @@ -1,299 +0,0 @@ -#include "auto_pause_settings_dialog.h" -#include "table_item_delegate.h" -#include "Emu/System.h" - -#include -#include -#include -#include -#include - -#include "util/logs.hpp" -#include "util/File.h" - -LOG_CHANNEL(autopause_log, "AutoPause"); - -auto_pause_settings_dialog::auto_pause_settings_dialog(QWidget* parent) : QDialog(parent) -{ - QLabel* description = new QLabel(tr("To use auto pause: enter the ID(s) of a function or a system call.\nRestart of the game is required to apply. You can enable/disable this in the settings."), this); - - m_pause_list = new QTableWidget(this); - m_pause_list->setColumnCount(2); - m_pause_list->setHorizontalHeaderLabels(QStringList() << tr("Call ID") << tr("Type")); - // m_pause_list->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); - m_pause_list->setSelectionBehavior(QAbstractItemView::SelectRows); - m_pause_list->setContextMenuPolicy(Qt::CustomContextMenu); - m_pause_list->setItemDelegate(new table_item_delegate(this)); - m_pause_list->setShowGrid(false); - - QPushButton* clearButton = new QPushButton(tr("Clear"), this); - QPushButton* reloadButton = new QPushButton(tr("Reload"), this); - QPushButton* saveButton = new QPushButton(tr("Save"), this); - QPushButton* cancelButton = new QPushButton(tr("Cancel"), this); - cancelButton->setDefault(true); - - QHBoxLayout* buttonsLayout = new QHBoxLayout(); - buttonsLayout->addWidget(clearButton); - buttonsLayout->addWidget(reloadButton); - buttonsLayout->addStretch(); - buttonsLayout->addWidget(saveButton); - buttonsLayout->addWidget(cancelButton); - - QVBoxLayout* mainLayout = new QVBoxLayout(this); - mainLayout->addWidget(description); - mainLayout->addWidget(m_pause_list); - mainLayout->addLayout(buttonsLayout); - setLayout(mainLayout); - - setMinimumSize(QSize(400, 360)); - setWindowTitle(tr("Auto Pause Manager")); - setObjectName("auto_pause_manager"); - - // Events - connect(m_pause_list, &QTableWidget::customContextMenuRequested, this, &auto_pause_settings_dialog::ShowContextMenu); - connect(clearButton, &QAbstractButton::clicked, [this]() - { - m_entries.clear(); - UpdateList(); - }); - connect(reloadButton, &QAbstractButton::clicked, [this]() - { - LoadEntries(); - UpdateList(); - }); - connect(saveButton, &QAbstractButton::clicked, [this]() - { - SaveEntries(); - autopause_log.success("File pause.bin was updated."); - }); - connect(cancelButton, &QAbstractButton::clicked, this, &QWidget::close); - - Emu.GracefulShutdown(false); - - LoadEntries(); - UpdateList(); - setFixedSize(sizeHint()); -} - -// Copied some from AutoPause. -void auto_pause_settings_dialog::LoadEntries() -{ - m_entries.clear(); - m_entries.reserve(16); - - const fs::file list(fs::get_config_dir() + "pause.bin"); - - if (list) - { - // System calls ID and Function calls ID are all u32 iirc. - u32 num; - const usz fmax = list.size(); - usz fcur = 0; - list.seek(0); - while (fcur <= fmax - sizeof(u32)) - { - list.read(&num, sizeof(u32)); - fcur += sizeof(u32); - if (num == 0xFFFFFFFF) - break; - - m_entries.emplace_back(num); - } - } -} - -// Copied some from AutoPause. -// Tip: This one doesn't check for the file is being read or not. -// This would always use a 0xFFFFFFFF as end of the pause.bin -void auto_pause_settings_dialog::SaveEntries() -{ - fs::file list(fs::get_config_dir() + "pause.bin", fs::rewrite); - // System calls ID and Function calls ID are all u32 iirc. - u32 num = 0; - list.seek(0); - for (usz i = 0; i < m_entries.size(); ++i) - { - if (num == 0xFFFFFFFF) - continue; - num = m_entries[i]; - list.write(&num, sizeof(u32)); - } - num = 0xFFFFFFFF; - list.write(&num, sizeof(u32)); -} - -void auto_pause_settings_dialog::UpdateList() -{ - const int entries_size = static_cast(m_entries.size()); - m_pause_list->clearContents(); - m_pause_list->setRowCount(entries_size); - for (int i = 0; i < entries_size; ++i) - { - QTableWidgetItem* callItem = new QTableWidgetItem; - QTableWidgetItem* typeItem = new QTableWidgetItem; - callItem->setFlags(callItem->flags() & ~Qt::ItemIsEditable); - typeItem->setFlags(typeItem->flags() & ~Qt::ItemIsEditable); - if (m_entries[i] != 0xFFFFFFFF) - { - callItem->setData(Qt::DisplayRole, QString::fromStdString(fmt::format("%08x", m_entries[i]))); - } - else - { - callItem->setData(Qt::DisplayRole, tr("Unset")); - } - - if (m_entries[i] < 1024) - { - typeItem->setData(Qt::DisplayRole, tr("System Call")); - } - else - { - typeItem->setData(Qt::DisplayRole, tr("Function Call")); - } - - m_pause_list->setItem(i, 0, callItem); - m_pause_list->setItem(i, 1, typeItem); - } -} - -void auto_pause_settings_dialog::ShowContextMenu(const QPoint& pos) -{ - const int row = m_pause_list->indexAt(pos).row(); - - QMenu myMenu; - - // Make Actions - QAction* add = myMenu.addAction(tr("&Add")); - QAction* remove = myMenu.addAction(tr("&Remove")); - myMenu.addSeparator(); - QAction* config = myMenu.addAction(tr("&Config")); - - if (row == -1) - { - remove->setEnabled(false); - config->setEnabled(false); - } - - auto OnEntryConfig = [this](int row, bool newEntry) - { - AutoPauseConfigDialog* config = new AutoPauseConfigDialog(this, this, newEntry, &m_entries[row]); - config->setModal(true); - config->exec(); - UpdateList(); - }; - - connect(add, &QAction::triggered, this, [=, this]() - { - m_entries.emplace_back(0xFFFFFFFF); - UpdateList(); - const int idx = static_cast(m_entries.size()) - 1; - m_pause_list->selectRow(idx); - OnEntryConfig(idx, true); - }); - connect(remove, &QAction::triggered, this, &auto_pause_settings_dialog::OnRemove); - connect(config, &QAction::triggered, this, [=, this]() - { - OnEntryConfig(row, false); - }); - - myMenu.exec(m_pause_list->viewport()->mapToGlobal(pos)); -} - -void auto_pause_settings_dialog::OnRemove() -{ - QModelIndexList selection = m_pause_list->selectionModel()->selectedRows(); - std::sort(selection.begin(), selection.end()); - for (int i = selection.count() - 1; i >= 0; i--) - { - m_entries.erase(m_entries.begin() + ::at32(selection, i).row()); - } - UpdateList(); -} - -void auto_pause_settings_dialog::keyPressEvent(QKeyEvent* event) -{ - if (event->isAutoRepeat()) - { - return; - } - - if (event->key() == Qt::Key_Delete) - { - OnRemove(); - } -} - -AutoPauseConfigDialog::AutoPauseConfigDialog(QWidget* parent, auto_pause_settings_dialog* apsd, bool newEntry, u32* entry) - : QDialog(parent), m_presult(entry), m_newEntry(newEntry), m_apsd(apsd) -{ - m_entry = *m_presult; - setMinimumSize(QSize(300, -1)); - - QPushButton* button_ok = new QPushButton(tr("&Ok"), this); - QPushButton* button_cancel = new QPushButton(tr("&Cancel"), this); - button_ok->setFixedWidth(50); - button_cancel->setFixedWidth(50); - - QLabel* description = new QLabel(tr("Specify ID of System Call or Function Call below. You need to use a Hexadecimal ID."), this); - description->setWordWrap(true); - - m_current_converted = new QLabel(tr("Currently it gets an id of \"Unset\"."), this); - m_current_converted->setWordWrap(true); - - m_id = new QLineEdit(this); - m_id->setText(QString::fromStdString(fmt::format("%08x", m_entry))); - m_id->setPlaceholderText("ffffffff"); - m_id->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); - m_id->setMaxLength(8); - m_id->setFixedWidth(65); - setWindowTitle("Auto Pause Setting: " + m_id->text()); - - connect(button_cancel, &QAbstractButton::clicked, this, &AutoPauseConfigDialog::OnCancel); - connect(button_ok, &QAbstractButton::clicked, this, &AutoPauseConfigDialog::OnOk); - connect(m_id, &QLineEdit::textChanged, this, &AutoPauseConfigDialog::OnUpdateValue); - - QHBoxLayout* configHBox = new QHBoxLayout(); - configHBox->addWidget(m_id); - configHBox->addWidget(button_ok); - configHBox->addWidget(button_cancel); - configHBox->setAlignment(Qt::AlignCenter); - - QVBoxLayout* mainLayout = new QVBoxLayout(this); - mainLayout->addWidget(description); - mainLayout->addLayout(configHBox); - mainLayout->addWidget(m_current_converted); - - setLayout(mainLayout); - setFixedSize(QSize(300, sizeHint().height())); - - OnUpdateValue(); -} - -void AutoPauseConfigDialog::OnOk() -{ - bool ok; - const ullong value = m_id->text().toULongLong(&ok, 16); - - m_entry = value; - *m_presult = m_entry; - - accept(); -} - -void AutoPauseConfigDialog::OnCancel() -{ - if (m_newEntry) - { - m_apsd->OnRemove(); - } - close(); -} - -void AutoPauseConfigDialog::OnUpdateValue() const -{ - bool ok; - const ullong value = m_id->text().toULongLong(&ok, 16); - const bool is_ok = ok && value <= u32{umax}; - - m_current_converted->setText(tr("Current value: %1 (%2)").arg(value, 8, 16).arg(is_ok ? tr("OK") : tr("Conversion failed"))); -} diff --git a/rpcs3qt-legacy/auto_pause_settings_dialog.h b/rpcs3qt-legacy/auto_pause_settings_dialog.h deleted file mode 100644 index 30e595e50..000000000 --- a/rpcs3qt-legacy/auto_pause_settings_dialog.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include "util/types.hpp" - -#include -#include -#include -#include -#include - -class auto_pause_settings_dialog : public QDialog -{ - Q_OBJECT - - enum - { - id_add, - id_remove, - id_config, - }; - - std::vector m_entries; - QTableWidget* m_pause_list; - -public: - explicit auto_pause_settings_dialog(QWidget* parent); - void UpdateList(); - void LoadEntries(); - void SaveEntries(); - -public Q_SLOTS: - void OnRemove(); -private Q_SLOTS: - void ShowContextMenu(const QPoint& pos); - void keyPressEvent(QKeyEvent* event) override; -}; - -class AutoPauseConfigDialog : public QDialog -{ - Q_OBJECT - - u32 m_entry; - u32* m_presult; - bool m_newEntry; - QLineEdit* m_id; - QLabel* m_current_converted; - auto_pause_settings_dialog* m_apsd; - -public: - explicit AutoPauseConfigDialog(QWidget* parent, auto_pause_settings_dialog* apsd, bool newEntry, u32* entry); - -private Q_SLOTS: - void OnOk(); - void OnCancel(); - void OnUpdateValue() const; -}; diff --git a/rpcs3qt-legacy/basic_mouse_settings_dialog.cpp b/rpcs3qt-legacy/basic_mouse_settings_dialog.cpp deleted file mode 100644 index c89d461c9..000000000 --- a/rpcs3qt-legacy/basic_mouse_settings_dialog.cpp +++ /dev/null @@ -1,298 +0,0 @@ -#include "stdafx.h" -#include "basic_mouse_settings_dialog.h" -#include "localized_emu.h" -#include "Input/basic_mouse_handler.h" -#include "Input/keyboard_pad_handler.h" -#include "Emu/Io/mouse_config.h" -#include "util/asm.hpp" - -#include -#include -#include - -LOG_CHANNEL(cfg_log, "CFG"); - -enum button_role -{ - button_name = Qt::UserRole, - button_code -}; - -basic_mouse_settings_dialog::basic_mouse_settings_dialog(QWidget* parent) - : QDialog(parent) -{ - setObjectName("basic_mouse_settings_dialog"); - setWindowTitle(tr("Configure Basic Mouse Handler")); - setAttribute(Qt::WA_DeleteOnClose); - setAttribute(Qt::WA_StyledBackground); - setModal(true); - - QVBoxLayout* v_layout = new QVBoxLayout(this); - - m_button_box = new QDialogButtonBox(this); - m_button_box->setStandardButtons(QDialogButtonBox::Apply | QDialogButtonBox::Cancel | QDialogButtonBox::Save | QDialogButtonBox::RestoreDefaults); - - connect(m_button_box, &QDialogButtonBox::clicked, this, [this](QAbstractButton* button) - { - if (button == m_button_box->button(QDialogButtonBox::Apply)) - { - g_cfg_mouse.save(); - } - else if (button == m_button_box->button(QDialogButtonBox::Save)) - { - g_cfg_mouse.save(); - accept(); - } - else if (button == m_button_box->button(QDialogButtonBox::RestoreDefaults)) - { - if (QMessageBox::question(this, tr("Confirm Reset"), tr("Reset all settings?")) != QMessageBox::Yes) - return; - reset_config(); - } - else if (button == m_button_box->button(QDialogButtonBox::Cancel)) - { - // Restore config - if (!g_cfg_mouse.load()) - { - cfg_log.notice("Could not restore mouse config. Using defaults."); - } - reject(); - } - }); - - if (!g_cfg_mouse.load()) - { - cfg_log.notice("Could not load basic mouse config. Using defaults."); - } - - m_buttons = new QButtonGroup(this); - - connect(m_buttons, &QButtonGroup::idClicked, this, &basic_mouse_settings_dialog::on_button_click); - - connect(&m_remap_timer, &QTimer::timeout, this, [this]() - { - auto button = m_buttons->button(m_button_id); - - if (--m_seconds <= 0) - { - if (button) - { - if (const int button_id = m_buttons->id(button)) - { - const std::string name = g_cfg_mouse.get_button(button_id).to_string(); - button->setText(name.empty() ? QStringLiteral("-") : QString::fromStdString(name)); - } - } - reactivate_buttons(); - return; - } - if (button) - { - button->setText(tr("[ Waiting %1 ]").arg(m_seconds)); - } - }); - - const auto insert_button = [this](int id, QPushButton* button) - { - m_buttons->addButton(button, id); - button->installEventFilter(this); - }; - - constexpr u32 button_count = 8; - constexpr u32 max_items_per_column = 4; - int rows = button_count; - - for (u32 cols = 1; utils::aligned_div(button_count, cols) > max_items_per_column;) - { - rows = utils::aligned_div(button_count, ++cols); - } - - QWidget* widget = new QWidget(this); - QGridLayout* grid_layout = new QGridLayout(this); - - for (int i = 0, row = 0, col = 0; i < static_cast(button_count); i++, row++) - { - const int cell_code = get_mouse_button_code(i); - const QString translated_cell_button = localized_emu::translated_mouse_button(cell_code); - - QHBoxLayout* h_layout = new QHBoxLayout(this); - QGroupBox* gb = new QGroupBox(translated_cell_button, this); - QPushButton* pb = new QPushButton(this); - - insert_button(cell_code, pb); - - const std::string saved_btn = g_cfg_mouse.get_button(cell_code); - - pb->setText(saved_btn.empty() ? QStringLiteral("-") : QString::fromStdString(saved_btn)); - - if (row >= rows) - { - row = 0; - col++; - } - - m_push_buttons[cell_code] = pb; - h_layout->addWidget(pb); - gb->setLayout(h_layout); - grid_layout->addWidget(gb, row, col); - } - - widget->setLayout(grid_layout); - - v_layout->addWidget(widget); - v_layout->addWidget(m_button_box); - setLayout(v_layout); - - m_palette = m_push_buttons[CELL_MOUSE_BUTTON_1]->palette(); // save normal palette -} - -void basic_mouse_settings_dialog::reset_config() -{ - g_cfg_mouse.from_default(); - - for (auto& [cell_code, pb] : m_push_buttons) - { - if (!pb) - continue; - - const QString text = QString::fromStdString(g_cfg_mouse.get_button(cell_code).def); - pb->setText(text.isEmpty() ? QStringLiteral("-") : text); - } -} - -void basic_mouse_settings_dialog::on_button_click(int id) -{ - if (id < 0) - { - return; - } - - for (auto but : m_buttons->buttons()) - { - but->setEnabled(false); - but->setFocusPolicy(Qt::ClickFocus); - } - - m_button_box->setEnabled(false); - for (auto but : m_button_box->buttons()) - { - but->setFocusPolicy(Qt::ClickFocus); - } - - m_button_id = id; - if (auto button = m_buttons->button(m_button_id)) - { - button->setText(tr("[ Waiting %1 ]").arg(MAX_SECONDS)); - button->setPalette(QPalette(Qt::blue)); - button->grabMouse(); - } - - m_remap_timer.start(1000); -} - -void basic_mouse_settings_dialog::keyPressEvent(QKeyEvent* event) -{ - if (m_button_id < 0) - { - // We are not remapping a button, so pass the event to the base class. - QDialog::keyPressEvent(event); - return; - } - - const std::string name = keyboard_pad_handler::GetKeyName(event, false); - g_cfg_mouse.get_button(m_button_id).from_string(name); - - if (auto button = m_buttons->button(m_button_id)) - { - button->setText(QString::fromStdString(name)); - } - - reactivate_buttons(); -} - -void basic_mouse_settings_dialog::mouseReleaseEvent(QMouseEvent* event) -{ - if (m_button_id < 0) - { - // We are not remapping a button, so pass the event to the base class. - QDialog::mouseReleaseEvent(event); - return; - } - - const std::string name = keyboard_pad_handler::GetMouseName(event); - g_cfg_mouse.get_button(m_button_id).from_string(name); - - if (auto button = m_buttons->button(m_button_id)) - { - button->setText(QString::fromStdString(name)); - } - - reactivate_buttons(); -} - -bool basic_mouse_settings_dialog::eventFilter(QObject* object, QEvent* event) -{ - switch (event->type()) - { - case QEvent::MouseButtonRelease: - { - // On right click clear binding if we are not remapping pad button - if (m_button_id < 0) - { - QMouseEvent* mouse_event = static_cast(event); - if (const auto button = qobject_cast(object); button && button->isEnabled() && mouse_event->button() == Qt::RightButton) - { - if (const int button_id = m_buttons->id(button)) - { - button->setText(QStringLiteral("-")); - g_cfg_mouse.get_button(button_id).from_string(""); - return true; - } - } - } - - // Disabled buttons should not absorb mouseclicks - event->ignore(); - break; - } - default: - { - break; - } - } - - return QDialog::eventFilter(object, event); -} - -void basic_mouse_settings_dialog::reactivate_buttons() -{ - m_remap_timer.stop(); - m_seconds = MAX_SECONDS; - - if (m_button_id < 0) - { - return; - } - - if (auto button = m_buttons->button(m_button_id)) - { - button->setPalette(m_palette); - button->releaseMouse(); - } - - m_button_id = -1; - - // Enable all buttons - m_button_box->setEnabled(true); - - for (auto but : m_button_box->buttons()) - { - but->setFocusPolicy(Qt::StrongFocus); - } - - for (auto but : m_buttons->buttons()) - { - but->setEnabled(true); - but->setFocusPolicy(Qt::StrongFocus); - } -} diff --git a/rpcs3qt-legacy/basic_mouse_settings_dialog.h b/rpcs3qt-legacy/basic_mouse_settings_dialog.h deleted file mode 100644 index 3de502997..000000000 --- a/rpcs3qt-legacy/basic_mouse_settings_dialog.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include - -class basic_mouse_settings_dialog : public QDialog -{ - Q_OBJECT - -public: - basic_mouse_settings_dialog(QWidget* parent = nullptr); - -private: - void reset_config(); - - void on_button_click(int id); - void reactivate_buttons(); - - // Buttons - QDialogButtonBox* m_button_box = nullptr; - QButtonGroup* m_buttons = nullptr; - std::unordered_map m_push_buttons; - int m_button_id = -1; - - // Backup for standard button palette - QPalette m_palette; - - // Remap Timer - static constexpr int MAX_SECONDS = 5; - int m_seconds = MAX_SECONDS; - QTimer m_remap_timer; - -protected: - void keyPressEvent(QKeyEvent* event) override; - void mouseReleaseEvent(QMouseEvent* event) override; - bool eventFilter(QObject* object, QEvent* event) override; -}; diff --git a/rpcs3qt-legacy/breakpoint_handler.cpp b/rpcs3qt-legacy/breakpoint_handler.cpp deleted file mode 100644 index d2c4c44ba..000000000 --- a/rpcs3qt-legacy/breakpoint_handler.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "breakpoint_handler.h" - -extern bool ppu_breakpoint(u32 loc, bool is_adding); - -bool breakpoint_handler::IsBreakOnBPM() const -{ - return m_break_on_bpm; -} - -void breakpoint_handler::SetBreakOnBPM(bool break_on_bpm) -{ - m_break_on_bpm = break_on_bpm; -} - -bool breakpoint_handler::HasBreakpoint(u32 loc, bs_t type) -{ - std::lock_guard lock(mutex_breakpoints); - - return m_breakpoints.contains(loc) && ((m_breakpoints.at(loc) & type) == type); -} - -bool breakpoint_handler::AddBreakpoint(u32 loc, bs_t type) -{ - std::lock_guard lock(mutex_breakpoints); - - if ((type & breakpoint_types::bp_exec) && !ppu_breakpoint(loc, true)) - { - return false; - } - - return m_breakpoints.insert({loc, type}).second; -} - -bool breakpoint_handler::RemoveBreakpoint(u32 loc) -{ - std::lock_guard lock(mutex_breakpoints); - - bs_t bp_type{}; - if (m_breakpoints.contains(loc)) - { - bp_type = m_breakpoints.at(loc); - } - - if (m_breakpoints.erase(loc) == 0) - { - return false; - } - - if (bp_type & breakpoint_types::bp_exec) - { - ensure(ppu_breakpoint(loc, false)); - } - return true; -} diff --git a/rpcs3qt-legacy/breakpoint_handler.h b/rpcs3qt-legacy/breakpoint_handler.h deleted file mode 100644 index 1d731b6e9..000000000 --- a/rpcs3qt-legacy/breakpoint_handler.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "util/types.hpp" -#include "util/bit_set.h" -#include -#include "util/mutex.h" - -enum class breakpoint_types -{ - bp_read = 0x1, - bp_write = 0x2, - bp_exec = 0x4, - __bitset_enum_max -}; - -/* - * This class acts as a layer between the UI and Emu for breakpoints. - */ -class breakpoint_handler -{ - -public: - breakpoint_handler() = default; - ~breakpoint_handler() = default; - - bool IsBreakOnBPM() const; - void SetBreakOnBPM(bool break_on_bpm); - - /** - * Returns true iff breakpoint exists at loc. - * TODO: Add arg for flags, gameid, and maybe even thread if it should be thread local breakpoint.... breakpoint struct is probably what'll happen - */ - bool HasBreakpoint(u32 loc, bs_t type); - - /** - * Returns true if added successfully. TODO: flags - */ - bool AddBreakpoint(u32 loc, bs_t type); - - /** - * Returns true if removed breakpoint at loc successfully. - */ - bool RemoveBreakpoint(u32 loc); - -private: - // TODO : generalize to hold multiple games and handle flags.Probably do : std::map>. - // Although, externally, they'll only be accessed by loc (I think) so a map of maps may also do? - shared_mutex mutex_breakpoints; - std::map> m_breakpoints; //! Holds all breakpoints. - bool m_break_on_bpm = false; -}; - -extern breakpoint_handler g_breakpoint_handler; diff --git a/rpcs3qt-legacy/breakpoint_list.cpp b/rpcs3qt-legacy/breakpoint_list.cpp deleted file mode 100644 index 12a4e44ef..000000000 --- a/rpcs3qt-legacy/breakpoint_list.cpp +++ /dev/null @@ -1,287 +0,0 @@ -#include "breakpoint_list.h" -#include "breakpoint_handler.h" - -#include "Emu/CPU/CPUDisAsm.h" -#include "Emu/Cell/PPUThread.h" -#include "Emu/Cell/SPUThread.h" -#include "debugger_add_bp_window.h" - -#include -#include -#include - -extern bool is_using_interpreter(thread_class t_class); - -breakpoint_list::breakpoint_list(QWidget* parent, breakpoint_handler* handler) : QListWidget(parent), m_ppu_breakpoint_handler(handler) -{ - setEditTriggers(QAbstractItemView::NoEditTriggers); - setContextMenuPolicy(Qt::CustomContextMenu); - setSelectionMode(QAbstractItemView::ExtendedSelection); - - connect(this, &QListWidget::itemDoubleClicked, this, &breakpoint_list::OnBreakpointListDoubleClicked); - connect(this, &QListWidget::customContextMenuRequested, this, &breakpoint_list::OnBreakpointListRightClicked); - - m_delete_action = new QAction(tr("&Delete"), this); - m_delete_action->setShortcut(Qt::Key_Delete); - m_delete_action->setShortcutContext(Qt::WidgetShortcut); - connect(m_delete_action, &QAction::triggered, this, &breakpoint_list::OnBreakpointListDelete); - addAction(m_delete_action); - - // Hide until used in order to allow as much space for registers panel as possible - hide(); -} - -/** - * It's unfortunate I need a method like this to sync these. Should ponder a cleaner way to do this. - */ -void breakpoint_list::UpdateCPUData(std::shared_ptr disasm) -{ - m_disasm = std::move(disasm); -} - -void breakpoint_list::ClearBreakpoints() -{ - while (count()) - { - auto* currentItem = takeItem(0); - const u32 loc = currentItem->data(Qt::UserRole).value(); - m_ppu_breakpoint_handler->RemoveBreakpoint(loc); - delete currentItem; - } - - hide(); -} - -void breakpoint_list::RemoveBreakpoint(u32 addr) -{ - m_ppu_breakpoint_handler->RemoveBreakpoint(addr); - - for (int i = 0; i < count(); i++) - { - QListWidgetItem* currentItem = item(i); - - if (currentItem->data(Qt::UserRole).value() == addr) - { - delete takeItem(i); - break; - } - } - - if (!count()) - { - hide(); - } -} - -bool breakpoint_list::AddBreakpoint(u32 pc, bs_t type) -{ - if (!m_ppu_breakpoint_handler->AddBreakpoint(pc, type)) - { - return false; - } - - QString breakpoint_item_text; - - if (type == breakpoint_types::bp_exec) - { - m_disasm->disasm(m_disasm->dump_pc = pc); - breakpoint_item_text = QString::fromStdString(m_disasm->last_opcode); - breakpoint_item_text.remove(10, 13); - } - else if (type == breakpoint_types::bp_read) - { - breakpoint_item_text = QString("BPMR: 0x%1").arg(pc, 8, 16, QChar('0')); - } - else if (type == breakpoint_types::bp_write) - { - breakpoint_item_text = QString("BPMW: 0x%1").arg(pc, 8, 16, QChar('0')); - } - else if (type == (breakpoint_types::bp_read + breakpoint_types::bp_write)) - { - breakpoint_item_text = QString("BPMRW: 0x%1").arg(pc, 8, 16, QChar('0')); - } - - QListWidgetItem* breakpoint_item = new QListWidgetItem(breakpoint_item_text); - breakpoint_item->setForeground(m_text_color_bp); - breakpoint_item->setBackground(m_color_bp); - breakpoint_item->setData(Qt::UserRole, pc); - addItem(breakpoint_item); - - show(); - - return true; -} - -/** - * If breakpoint exists, we remove it, else add new one. Yeah, it'd be nicer from a code logic to have it be set/reset. But, that logic has to happen somewhere anyhow. - */ -void breakpoint_list::HandleBreakpointRequest(u32 loc, bool only_add) -{ - const auto cpu = m_disasm ? m_disasm->get_cpu() : nullptr; - - if (!cpu || cpu->state & cpu_flag::exit) - { - return; - } - - if (!is_using_interpreter(cpu->get_class())) - { - QMessageBox::warning(this, tr("Interpreters-Only Feature!"), tr("Cannot set breakpoints on non-interpreter decoders.")); - return; - } - - switch (cpu->get_class()) - { - case thread_class::spu: - { - if (loc >= SPU_LS_SIZE || loc % 4) - { - QMessageBox::warning(this, tr("Invalid Memory For Breakpoints!"), tr("Cannot set breakpoints on non-SPU executable memory!")); - return; - } - - const auto spu = static_cast(cpu); - auto& list = spu->local_breakpoints; - const u32 pos_at = loc / 4; - const u32 pos_bit = 1u << (pos_at % 8); - - if (list[pos_at / 8].fetch_xor(pos_bit) & pos_bit) - { - if (std::none_of(list.begin(), list.end(), [](auto& val) - { - return val.load(); - })) - { - spu->has_active_local_bps = false; - } - } - else - { - if (!spu->has_active_local_bps.exchange(true)) - { - spu->state.atomic_op([](bs_t& flags) - { - if (flags & cpu_flag::pending) - { - flags += cpu_flag::pending_recheck; - } - else - { - flags += cpu_flag::pending; - } - }); - } - } - - return; - } - case thread_class::ppu: - break; - default: - QMessageBox::warning(this, tr("Unimplemented Breakpoints For Thread Type!"), tr("Cannot set breakpoints on a thread not an PPU/SPU currently, sorry.")); - return; - } - - if (!vm::check_addr(loc, vm::page_executable)) - { - QMessageBox::warning(this, tr("Invalid Memory For Breakpoints!"), tr("Cannot set breakpoints on non-executable memory!")); - return; - } - - if (m_ppu_breakpoint_handler->HasBreakpoint(loc, breakpoint_types::bp_exec)) - { - if (!only_add) - { - RemoveBreakpoint(loc); - } - } - else - { - if (!AddBreakpoint(loc, breakpoint_types::bp_exec)) - { - QMessageBox::warning(this, tr("Unknown error while setting breakpoint!"), tr("Failed to set breakpoints.")); - return; - } - } -} - -void breakpoint_list::OnBreakpointListDoubleClicked() -{ - if (QListWidgetItem* item = currentItem()) - { - const u32 address = item->data(Qt::UserRole).value(); - Q_EMIT RequestShowAddress(address); - } -} - -void breakpoint_list::OnBreakpointListRightClicked(const QPoint& pos) -{ - m_context_menu = new QMenu(); - - if (selectedItems().count() == 1) - { - QAction* rename_action = m_context_menu->addAction(tr("&Rename")); - connect(rename_action, &QAction::triggered, this, [this]() - { - QListWidgetItem* current_item = selectedItems().first(); - current_item->setFlags(current_item->flags() | Qt::ItemIsEditable); - editItem(current_item); - }); - m_context_menu->addSeparator(); - } - - if (selectedItems().count() >= 1) - { - m_context_menu->addAction(m_delete_action); - } - - QAction* m_addbp = new QAction(tr("Add Breakpoint"), this); - connect(m_addbp, &QAction::triggered, this, [this] - { - debugger_add_bp_window dlg(this, this); - dlg.exec(); - }); - m_context_menu->addAction(m_addbp); - -#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS - QAction* m_tglbpmbreak = new QAction(m_ppu_breakpoint_handler->IsBreakOnBPM() ? tr("Disable BPM") : tr("Enable BPM"), this); - connect(m_tglbpmbreak, &QAction::triggered, [this] - { - m_ppu_breakpoint_handler->SetBreakOnBPM(!m_ppu_breakpoint_handler->IsBreakOnBPM()); - }); - m_context_menu->addAction(m_tglbpmbreak); -#endif - - m_context_menu->exec(viewport()->mapToGlobal(pos)); - m_context_menu->deleteLater(); - m_context_menu = nullptr; -} - -void breakpoint_list::OnBreakpointListDelete() -{ - for (int i = selectedItems().count() - 1; i >= 0; i--) - { - RemoveBreakpoint(::at32(selectedItems(), i)->data(Qt::UserRole).value()); - } - - if (m_context_menu) - { - m_context_menu->close(); - } -} - -void breakpoint_list::mouseDoubleClickEvent(QMouseEvent* ev) -{ - if (!ev) - return; - - // Qt's itemDoubleClicked signal doesn't distinguish between mouse buttons and there is no simple way to get the pressed button. - // So we have to ignore this event when another button is pressed. - if (ev->button() != Qt::LeftButton) - { - ev->ignore(); - return; - } - - QListWidget::mouseDoubleClickEvent(ev); -} diff --git a/rpcs3qt-legacy/breakpoint_list.h b/rpcs3qt-legacy/breakpoint_list.h deleted file mode 100644 index 4367f1ba7..000000000 --- a/rpcs3qt-legacy/breakpoint_list.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "util/types.hpp" - -#include - -#include "breakpoint_handler.h" - -class CPUDisAsm; -class cpu_thread; - -class breakpoint_list : public QListWidget -{ - Q_OBJECT - -public: - breakpoint_list(QWidget* parent, breakpoint_handler* handler); - void UpdateCPUData(std::shared_ptr disasm); - void ClearBreakpoints(); - void RemoveBreakpoint(u32 addr); - bool AddBreakpoint(u32 pc, bs_t type); - - QColor m_text_color_bp; - QColor m_color_bp; - -protected: - void mouseDoubleClickEvent(QMouseEvent* ev) override; - -Q_SIGNALS: - void RequestShowAddress(u32 addr, bool select_addr = true, bool force = false); -public Q_SLOTS: - void HandleBreakpointRequest(u32 loc, bool add_only); - -private Q_SLOTS: - void OnBreakpointListDoubleClicked(); - void OnBreakpointListRightClicked(const QPoint& pos); - void OnBreakpointListDelete(); - -private: - breakpoint_handler* m_ppu_breakpoint_handler = nullptr; - QMenu* m_context_menu = nullptr; - QAction* m_delete_action; - std::shared_ptr m_disasm = nullptr; -}; diff --git a/rpcs3qt-legacy/call_stack_list.cpp b/rpcs3qt-legacy/call_stack_list.cpp deleted file mode 100644 index c2b8f343b..000000000 --- a/rpcs3qt-legacy/call_stack_list.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "call_stack_list.h" - -#include "util/StrFmt.h" - -#include -#include - -call_stack_list::call_stack_list(QWidget* parent) : QListWidget(parent) -{ - setEditTriggers(QAbstractItemView::NoEditTriggers); - setContextMenuPolicy(Qt::CustomContextMenu); - setSelectionMode(QAbstractItemView::ExtendedSelection); - - // connects - connect(this, &QListWidget::itemDoubleClicked, this, &call_stack_list::ShowItemAddress); - - // Hide until used in order to allow as much space for registers panel as possible - hide(); -} - -void call_stack_list::keyPressEvent(QKeyEvent* event) -{ - QListWidget::keyPressEvent(event); - event->ignore(); // Propagate the event to debugger_frame - - if (!event->modifiers() && event->key() == Qt::Key_Return) - { - ShowItemAddress(); - } -} - -void call_stack_list::HandleUpdate(const std::vector>& call_stack) -{ - clear(); - - for (const auto& addr : call_stack) - { - const QString text = QString::fromStdString(fmt::format("0x%08llx (sp=0x%08llx)", addr.first, addr.second)); - QListWidgetItem* call_stack_item = new QListWidgetItem(text); - call_stack_item->setData(Qt::UserRole, {addr.first}); - addItem(call_stack_item); - } - - setVisible(!call_stack.empty()); -} - -void call_stack_list::ShowItemAddress() -{ - if (QListWidgetItem* call_stack_item = currentItem()) - { - const u32 address = call_stack_item->data(Qt::UserRole).value(); - Q_EMIT RequestShowAddress(address); - } -} - -void call_stack_list::mouseDoubleClickEvent(QMouseEvent* ev) -{ - if (!ev) - return; - - // Qt's itemDoubleClicked signal doesn't distinguish between mouse buttons and there is no simple way to get the pressed button. - // So we have to ignore this event when another button is pressed. - if (ev->button() != Qt::LeftButton) - { - ev->ignore(); - return; - } - - QListWidget::mouseDoubleClickEvent(ev); -} diff --git a/rpcs3qt-legacy/call_stack_list.h b/rpcs3qt-legacy/call_stack_list.h deleted file mode 100644 index 48a71dbbb..000000000 --- a/rpcs3qt-legacy/call_stack_list.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "util/types.hpp" - -#include -#include - -class cpu_thread; -class CPUDisAsm; - -class call_stack_list : public QListWidget -{ - Q_OBJECT - -public: - explicit call_stack_list(QWidget* parent); - -protected: - void mouseDoubleClickEvent(QMouseEvent* ev) override; - -Q_SIGNALS: - void RequestShowAddress(u32 addr, bool select_addr = true, bool force = false); -public Q_SLOTS: - void HandleUpdate(const std::vector>& call_stack); - -private Q_SLOTS: - void ShowItemAddress(); - -private: - void keyPressEvent(QKeyEvent* event) override; -}; diff --git a/rpcs3qt-legacy/camera_settings_dialog.cpp b/rpcs3qt-legacy/camera_settings_dialog.cpp deleted file mode 100644 index 42a8b37f5..000000000 --- a/rpcs3qt-legacy/camera_settings_dialog.cpp +++ /dev/null @@ -1,284 +0,0 @@ -#include "stdafx.h" -#include "camera_settings_dialog.h" -#include "ui_camera_settings_dialog.h" -#include "permissions.h" -#include "Emu/Io/camera_config.h" - -#include -#include -#include -#include - -LOG_CHANNEL(camera_log, "Camera"); - -template <> -void fmt_class_string::format(std::string& out, u64 arg) -{ - format_enum(out, arg, [](QVideoFrameFormat::PixelFormat value) - { - switch (value) - { - case QVideoFrameFormat::Format_ARGB8888: return "ARGB8888"; - case QVideoFrameFormat::Format_ARGB8888_Premultiplied: return "ARGB8888_Premultiplied"; - case QVideoFrameFormat::Format_XRGB8888: return "XRGB8888"; - case QVideoFrameFormat::Format_BGRA8888: return "BGRA8888"; - case QVideoFrameFormat::Format_BGRA8888_Premultiplied: return "BGRA8888_Premultiplied"; - case QVideoFrameFormat::Format_BGRX8888: return "BGRX8888"; - case QVideoFrameFormat::Format_ABGR8888: return "ABGR8888"; - case QVideoFrameFormat::Format_XBGR8888: return "XBGR8888"; - case QVideoFrameFormat::Format_RGBA8888: return "RGBA8888"; - case QVideoFrameFormat::Format_RGBX8888: return "RGBX8888"; - case QVideoFrameFormat::Format_AYUV: return "AYUV"; - case QVideoFrameFormat::Format_AYUV_Premultiplied: return "AYUV_Premultiplied"; - case QVideoFrameFormat::Format_YUV420P: return "YUV420P"; - case QVideoFrameFormat::Format_YUV422P: return "YUV422P"; - case QVideoFrameFormat::Format_YV12: return "YV12"; - case QVideoFrameFormat::Format_UYVY: return "UYVY"; - case QVideoFrameFormat::Format_YUYV: return "YUYV"; - case QVideoFrameFormat::Format_NV12: return "NV12"; - case QVideoFrameFormat::Format_NV21: return "NV21"; - case QVideoFrameFormat::Format_IMC1: return "IMC1"; - case QVideoFrameFormat::Format_IMC2: return "IMC2"; - case QVideoFrameFormat::Format_IMC3: return "IMC3"; - case QVideoFrameFormat::Format_IMC4: return "IMC4"; - case QVideoFrameFormat::Format_Y8: return "Y8"; - case QVideoFrameFormat::Format_Y16: return "Y16"; - case QVideoFrameFormat::Format_P010: return "P010"; - case QVideoFrameFormat::Format_P016: return "P016"; - case QVideoFrameFormat::Format_SamplerExternalOES: return "SamplerExternalOES"; - case QVideoFrameFormat::Format_Jpeg: return "Jpeg"; - case QVideoFrameFormat::Format_SamplerRect: return "SamplerRect"; - default: return unknown; - } - }); -} - -Q_DECLARE_METATYPE(QCameraDevice); - -camera_settings_dialog::camera_settings_dialog(QWidget* parent) - : QDialog(parent), ui(new Ui::camera_settings_dialog) -{ - ui->setupUi(this); - - load_config(); - - for (const QCameraDevice& camera_info : QMediaDevices::videoInputs()) - { - if (camera_info.isNull()) - continue; - ui->combo_camera->addItem(camera_info.description(), QVariant::fromValue(camera_info)); - camera_log.notice("Found camera: '%s'", camera_info.description()); - } - - connect(ui->combo_camera, QOverload::of(&QComboBox::currentIndexChanged), this, &camera_settings_dialog::handle_camera_change); - connect(ui->combo_settings, QOverload::of(&QComboBox::currentIndexChanged), this, &camera_settings_dialog::handle_settings_change); - connect(ui->buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button) - { - if (button == ui->buttonBox->button(QDialogButtonBox::Save)) - { - save_config(); - accept(); - } - else if (button == ui->buttonBox->button(QDialogButtonBox::Apply)) - { - save_config(); - } - }); - - if (ui->combo_camera->count() == 0) - { - ui->combo_camera->setEnabled(false); - ui->combo_settings->setEnabled(false); - ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(false); - ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); - } - else - { - // TODO: show camera ID somewhere - ui->combo_camera->setCurrentIndex(0); - } -} - -camera_settings_dialog::~camera_settings_dialog() -{ -} - -void camera_settings_dialog::handle_camera_change(int index) -{ - if (index < 0 || !ui->combo_camera->itemData(index).canConvert()) - { - ui->combo_settings->clear(); - return; - } - - const QCameraDevice camera_info = ui->combo_camera->itemData(index).value(); - - if (camera_info.isNull()) - { - ui->combo_settings->clear(); - return; - } - - m_camera.reset(new QCamera(camera_info)); - m_media_capture_session.reset(new QMediaCaptureSession(nullptr)); - m_media_capture_session->setCamera(m_camera.get()); - m_media_capture_session->setVideoSink(ui->videoWidget->videoSink()); - - if (!m_camera->isAvailable()) - { - ui->combo_settings->clear(); - QMessageBox::warning(this, tr("Camera not available"), tr("The selected camera is not available.\nIt might be blocked by another application.")); - return; - } - - ui->combo_settings->blockSignals(true); - ui->combo_settings->clear(); - - QList settings = camera_info.videoFormats(); - std::sort(settings.begin(), settings.end(), [](const QCameraFormat& l, const QCameraFormat& r) -> bool - { - if (l.resolution().width() > r.resolution().width()) - return true; - if (l.resolution().width() < r.resolution().width()) - return false; - if (l.resolution().height() > r.resolution().height()) - return true; - if (l.resolution().height() < r.resolution().height()) - return false; - if (l.minFrameRate() > r.minFrameRate()) - return true; - if (l.minFrameRate() < r.minFrameRate()) - return false; - if (l.maxFrameRate() > r.maxFrameRate()) - return true; - if (l.maxFrameRate() < r.maxFrameRate()) - return false; - if (l.pixelFormat() > r.pixelFormat()) - return true; - if (l.pixelFormat() < r.pixelFormat()) - return false; - return false; - }); - - for (const QCameraFormat& setting : settings) - { - if (setting.isNull()) - continue; - const QString description = tr("%0x%1, %2-%3 FPS, Format=%4") - .arg(setting.resolution().width()) - .arg(setting.resolution().height()) - .arg(setting.minFrameRate()) - .arg(setting.maxFrameRate()) - .arg(QString::fromStdString(fmt::format("%s", setting.pixelFormat()))); - ui->combo_settings->addItem(description, QVariant::fromValue(setting)); - } - ui->combo_settings->blockSignals(false); - - if (ui->combo_settings->count() == 0) - { - ui->combo_settings->setEnabled(false); - } - else - { - // Load selected settings from config file - int index = 0; - bool success = false; - const std::string key = camera_info.id().toStdString(); - cfg_camera::camera_setting cfg_setting = g_cfg_camera.get_camera_setting(key, success); - - if (success) - { - camera_log.notice("Found config entry for camera \"%s\"", key); - - // Select matching drowdown entry - const double epsilon = 0.001; - - for (int i = 0; i < ui->combo_settings->count(); i++) - { - const QCameraFormat tmp = ui->combo_settings->itemData(i).value(); - - if (tmp.resolution().width() == cfg_setting.width && - tmp.resolution().height() == cfg_setting.height && - tmp.minFrameRate() >= (cfg_setting.min_fps - epsilon) && - tmp.minFrameRate() <= (cfg_setting.min_fps + epsilon) && - tmp.maxFrameRate() >= (cfg_setting.max_fps - epsilon) && - tmp.maxFrameRate() <= (cfg_setting.max_fps + epsilon) && - tmp.pixelFormat() == static_cast(cfg_setting.format)) - { - index = i; - break; - } - } - } - - ui->combo_settings->setCurrentIndex(std::max(0, index)); - ui->combo_settings->setEnabled(true); - - // Update config to match user interface outcome - const QCameraFormat setting = ui->combo_settings->currentData().value(); - cfg_setting.width = setting.resolution().width(); - cfg_setting.height = setting.resolution().height(); - cfg_setting.min_fps = setting.minFrameRate(); - cfg_setting.max_fps = setting.maxFrameRate(); - cfg_setting.format = static_cast(setting.pixelFormat()); - g_cfg_camera.set_camera_setting(key, cfg_setting); - } -} - -void camera_settings_dialog::handle_settings_change(int index) -{ - if (!m_camera) - { - return; - } - - if (!m_camera->isAvailable()) - { - QMessageBox::warning(this, tr("Camera not available"), tr("The selected camera is not available.\nIt might be blocked by another application.")); - return; - } - - if (!gui::utils::check_camera_permission(this, [this, index]() - { - handle_settings_change(index); - }, - [this]() - { - QMessageBox::warning(this, tr("Camera permissions denied!"), tr("RPCS3 has no permissions to access cameras on this device.")); - })) - { - return; - } - - if (index >= 0 && ui->combo_settings->itemData(index).canConvert() && ui->combo_camera->currentData().canConvert()) - { - const QCameraFormat setting = ui->combo_settings->itemData(index).value(); - if (!setting.isNull()) - { - m_camera->setCameraFormat(setting); - } - - cfg_camera::camera_setting cfg_setting; - cfg_setting.width = setting.resolution().width(); - cfg_setting.height = setting.resolution().height(); - cfg_setting.min_fps = setting.minFrameRate(); - cfg_setting.max_fps = setting.maxFrameRate(); - cfg_setting.format = static_cast(setting.pixelFormat()); - g_cfg_camera.set_camera_setting(ui->combo_camera->currentData().value().id().toStdString(), cfg_setting); - } - - m_camera->start(); -} - -void camera_settings_dialog::load_config() -{ - if (!g_cfg_camera.load()) - { - camera_log.notice("Could not load camera config. Using defaults."); - } -} - -void camera_settings_dialog::save_config() -{ - g_cfg_camera.save(); -} diff --git a/rpcs3qt-legacy/camera_settings_dialog.h b/rpcs3qt-legacy/camera_settings_dialog.h deleted file mode 100644 index da18f64cc..000000000 --- a/rpcs3qt-legacy/camera_settings_dialog.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace Ui -{ - class camera_settings_dialog; -} - -class camera_settings_dialog : public QDialog -{ - Q_OBJECT - -public: - camera_settings_dialog(QWidget* parent = nullptr); - virtual ~camera_settings_dialog(); - -private Q_SLOTS: - void handle_camera_change(int index); - void handle_settings_change(int index); - -private: - void load_config(); - void save_config(); - - std::unique_ptr ui; - std::unique_ptr m_camera; - std::unique_ptr m_media_capture_session; -}; diff --git a/rpcs3qt-legacy/camera_settings_dialog.ui b/rpcs3qt-legacy/camera_settings_dialog.ui deleted file mode 100644 index 8afe262f2..000000000 --- a/rpcs3qt-legacy/camera_settings_dialog.ui +++ /dev/null @@ -1,130 +0,0 @@ - - - camera_settings_dialog - - - - 0 - 0 - 356 - 380 - - - - Camera Settings - - - - - - - - Camera - - - - - - No cameras found - - - - - - - - - - Settings - - - - - - No settings found - - - - - - - - - - - - Preview - - - - - - - 64 - 48 - - - - true - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Save - - - - - - - - QVideoWidget - QWidget -
qvideowidget.h
- 1 -
-
- - - - buttonBox - accepted() - camera_settings_dialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - camera_settings_dialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - -
diff --git a/rpcs3qt-legacy/category.h b/rpcs3qt-legacy/category.h deleted file mode 100644 index 26771aa0e..000000000 --- a/rpcs3qt-legacy/category.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include -#include - -enum Category -{ - Disc_Game, - HDD_Game, - PS1_Game, - PS2_Game, - PSP_Game, - Home, - Media, - Data, - OS, - Unknown_Cat, - Others, -}; - -namespace cat -{ - const QString cat_app_music = "AM"; - const QString cat_app_photo = "AP"; - const QString cat_app_store = "AS"; - const QString cat_app_tv = "AT"; - const QString cat_app_video = "AV"; - const QString cat_bc_video = "BV"; - const QString cat_web_tv = "WT"; - const QString cat_home = "HM"; - const QString cat_network = "CB"; - const QString cat_store_fe = "SF"; - const QString cat_disc_game = "DG"; - const QString cat_hdd_game = "HG"; - const QString cat_ps2_game = "2P"; - const QString cat_ps2_inst = "2G"; - const QString cat_ps1_game = "1P"; - const QString cat_psp_game = "PP"; - const QString cat_psp_mini = "MN"; - const QString cat_psp_rema = "PE"; - const QString cat_ps3_data = "GD"; - const QString cat_ps2_data = "2D"; - const QString cat_ps3_save = "SD"; - const QString cat_psp_save = "MS"; - - const QString cat_ps3_os = "/OS"; - - const QString cat_unknown = "Unknown"; - - const QStringList ps2_games = {cat_ps2_game, cat_ps2_inst}; - const QStringList psp_games = {cat_psp_game, cat_psp_mini, cat_psp_rema}; - const QStringList media = {cat_app_photo, cat_app_video, cat_bc_video, cat_app_music, cat_app_store, cat_app_tv, cat_web_tv}; - const QStringList data = {cat_ps3_data, cat_ps2_data, cat_ps3_save, cat_psp_save}; - const QStringList os = {cat_ps3_os}; - const QStringList others = {cat_network, cat_store_fe}; -} // namespace cat diff --git a/rpcs3qt-legacy/cg_disasm_window.cpp b/rpcs3qt-legacy/cg_disasm_window.cpp deleted file mode 100644 index 633fbbcf2..000000000 --- a/rpcs3qt-legacy/cg_disasm_window.cpp +++ /dev/null @@ -1,172 +0,0 @@ -#include "cg_disasm_window.h" -#include "gui_settings.h" -#include "syntax_highlighter.h" - -#include -#include -#include -#include -#include -#include - -#include "Emu/RSX/Program/CgBinaryProgram.h" - -LOG_CHANNEL(gui_log, "GUI"); - -cg_disasm_window::cg_disasm_window(std::shared_ptr gui_settings) - : m_gui_settings(std::move(gui_settings)) -{ - setWindowTitle(tr("Cg Disasm")); - setObjectName("cg_disasm"); - setAttribute(Qt::WA_DeleteOnClose); - setAttribute(Qt::WA_StyledBackground); - setAcceptDrops(true); - setMinimumSize(QSize(200, 150)); // seems fine on win 10 - resize(QSize(620, 395)); - - m_path_last = m_gui_settings->GetValue(gui::fd_cg_disasm).toString(); - - m_disasm_text = new QTextEdit(this); - m_disasm_text->setReadOnly(true); - m_disasm_text->setWordWrapMode(QTextOption::NoWrap); - m_disasm_text->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); - - m_glsl_text = new QTextEdit(this); - m_glsl_text->setReadOnly(true); - m_glsl_text->setWordWrapMode(QTextOption::NoWrap); - m_glsl_text->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); - - // m_disasm_text syntax highlighter - sh_asm = new AsmHighlighter(m_disasm_text->document()); - - // m_glsl_text syntax highlighter - sh_glsl = new GlslHighlighter(m_glsl_text->document()); - - QSplitter* splitter = new QSplitter(); - splitter->addWidget(m_disasm_text); - splitter->addWidget(m_glsl_text); - - QHBoxLayout* layout = new QHBoxLayout(); - layout->addWidget(splitter); - - setLayout(layout); - - m_disasm_text->setContextMenuPolicy(Qt::CustomContextMenu); - m_glsl_text->setContextMenuPolicy(Qt::CustomContextMenu); - connect(m_disasm_text, &QWidget::customContextMenuRequested, this, &cg_disasm_window::ShowContextMenu); - connect(m_glsl_text, &QWidget::customContextMenuRequested, this, &cg_disasm_window::ShowContextMenu); - - ShowDisasm(); -} - -void cg_disasm_window::ShowContextMenu(const QPoint& pos) -{ - QMenu menu; - QAction* clear = new QAction(tr("&Clear")); - QAction* open = new QAction(tr("Open &Cg binary program")); - - menu.addAction(open); - menu.addSeparator(); - menu.addAction(clear); - - connect(clear, &QAction::triggered, [this]() - { - m_disasm_text->clear(); - m_glsl_text->clear(); - }); - - connect(open, &QAction::triggered, [this]() - { - const QString file_path = QFileDialog::getOpenFileName(this, tr("Select Cg program object"), m_path_last, tr("Cg program objects (*.fpo;*.vpo);;")); - if (file_path.isEmpty()) - return; - m_path_last = file_path; - ShowDisasm(); - }); - - const auto obj = qobject_cast(sender()); - - QPoint origin; - - if (obj == m_disasm_text) - { - origin = m_disasm_text->viewport()->mapToGlobal(pos); - } - else if (obj == m_glsl_text) - { - origin = m_glsl_text->viewport()->mapToGlobal(pos); - } - else - { - origin = mapToGlobal(pos); - } - - menu.exec(origin); -} - -void cg_disasm_window::ShowDisasm() const -{ - if (QFileInfo(m_path_last).isFile()) - { - CgBinaryDisasm disasm(m_path_last.toStdString()); - disasm.BuildShaderBody(); - m_disasm_text->setText(QString::fromStdString(disasm.GetArbShader())); - m_glsl_text->setText(QString::fromStdString(disasm.GetGlslShader())); - m_gui_settings->SetValue(gui::fd_cg_disasm, m_path_last); - } - else if (!m_path_last.isEmpty()) - { - gui_log.error("CgDisasm: Failed to open %s", m_path_last); - } -} - -bool cg_disasm_window::IsValidFile(const QMimeData& md, bool save) -{ - const QList urls = md.urls(); - - if (urls.count() > 1) - { - return false; - } - - const QString suff = QFileInfo(urls[0].fileName()).suffix().toLower(); - - if (suff == "fpo" || suff == "vpo") - { - if (save) - { - m_path_last = urls[0].toLocalFile(); - } - return true; - } - return false; -} - -void cg_disasm_window::dropEvent(QDropEvent* ev) -{ - if (IsValidFile(*ev->mimeData(), true)) - { - ShowDisasm(); - } -} - -void cg_disasm_window::dragEnterEvent(QDragEnterEvent* ev) -{ - if (IsValidFile(*ev->mimeData())) - { - ev->accept(); - } -} - -void cg_disasm_window::dragMoveEvent(QDragMoveEvent* ev) -{ - if (IsValidFile(*ev->mimeData())) - { - ev->accept(); - } -} - -void cg_disasm_window::dragLeaveEvent(QDragLeaveEvent* ev) -{ - ev->accept(); -} diff --git a/rpcs3qt-legacy/cg_disasm_window.h b/rpcs3qt-legacy/cg_disasm_window.h deleted file mode 100644 index 46e86da15..000000000 --- a/rpcs3qt-legacy/cg_disasm_window.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include -#include - -#include - -class AsmHighlighter; -class GlslHighlighter; -class gui_settings; - -class cg_disasm_window : public QWidget -{ - Q_OBJECT - -private Q_SLOTS: - void ShowContextMenu(const QPoint& pos); - -private: - void ShowDisasm() const; - bool IsValidFile(const QMimeData& md, bool save = false); - - QString m_path_last; - QTextEdit* m_disasm_text; - QTextEdit* m_glsl_text; - - std::shared_ptr m_gui_settings; - - AsmHighlighter* sh_asm; - GlslHighlighter* sh_glsl; - -public: - explicit cg_disasm_window(std::shared_ptr xSettings); - -protected: - void dropEvent(QDropEvent* ev) override; - void dragEnterEvent(QDragEnterEvent* ev) override; - void dragMoveEvent(QDragMoveEvent* ev) override; - void dragLeaveEvent(QDragLeaveEvent* ev) override; -}; diff --git a/rpcs3qt-legacy/cheat_manager.cpp b/rpcs3qt-legacy/cheat_manager.cpp deleted file mode 100644 index 2e6c756ba..000000000 --- a/rpcs3qt-legacy/cheat_manager.cpp +++ /dev/null @@ -1,1063 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "cheat_manager.h" - -#include "Emu/System.h" -#include "Emu/Memory/vm.h" -#include "Emu/CPU/CPUThread.h" - -#include "Emu/IdManager.h" -#include "Emu/Cell/PPUAnalyser.h" -#include "Emu/Cell/PPUInterpreter.h" -#include "Emu/Cell/lv2/sys_sync.h" - -#include "util/yaml.hpp" -#include "util/asm.hpp" -#include "util/logs.hpp" -#include "util/to_endian.hpp" -#include "util/File.h" -#include "util/StrUtil.h" -#include "util/bin_patch.h" // get_patches_path() - -LOG_CHANNEL(log_cheat, "Cheat"); - -cheat_manager_dialog* cheat_manager_dialog::inst = nullptr; - -YAML::Emitter& operator<<(YAML::Emitter& out, const cheat_info& rhs) -{ - std::string type_formatted; - fmt::append(type_formatted, "%s", rhs.type); - - out << YAML::BeginSeq << rhs.description << type_formatted << rhs.red_script << YAML::EndSeq; - - return out; -} - -cheat_engine::cheat_engine() -{ - const std::string patches_path = patch_engine::get_patches_path(); - - if (!fs::create_path(patches_path)) - { - log_cheat.fatal("Failed to create path: %s (%s)", patches_path, fs::g_tls_error); - return; - } - - const std::string path = patches_path + m_cheats_filename; - - if (fs::file cheat_file{path, fs::read + fs::create}) - { - auto [yml_cheats, error] = yaml_load(cheat_file.to_string()); - - if (!error.empty()) - { - log_cheat.error("Error parsing %s: %s", path, error); - return; - } - - for (const auto& yml_cheat : yml_cheats) - { - const std::string& game_name = yml_cheat.first.Scalar(); - - for (const auto& yml_offset : yml_cheat.second) - { - const u32 offset = get_yaml_node_value(yml_offset.first, error); - if (!error.empty()) - { - log_cheat.error("Error parsing %s: node key %s is not a u32 offset", path, yml_offset.first.Scalar()); - return; - } - - cheat_info cheat = get_yaml_node_value(yml_offset.second, error); - if (!error.empty()) - { - log_cheat.error("Error parsing %s: node %s is not a cheat_info node", path, yml_offset.first.Scalar()); - return; - } - - cheat.game = game_name; - cheat.offset = offset; - cheats[game_name][offset] = std::move(cheat); - } - } - } - else - { - log_cheat.error("Error loading %s", path); - } -} - -void cheat_engine::save() const -{ - const std::string patches_path = patch_engine::get_patches_path(); - - if (!fs::create_path(patches_path)) - { - log_cheat.fatal("Failed to create path: %s (%s)", patches_path, fs::g_tls_error); - return; - } - - const std::string path = patches_path + m_cheats_filename; - - fs::file cheat_file(path, fs::rewrite); - if (!cheat_file) - return; - - YAML::Emitter out; - - out << YAML::BeginMap; - for (const auto& game_entry : cheats) - { - out << game_entry.first; - out << YAML::BeginMap; - for (const auto& offset_entry : game_entry.second) - { - out << YAML::Hex << offset_entry.first; - out << offset_entry.second; - } - out << YAML::EndMap; - } - out << YAML::EndMap; - - cheat_file.write(out.c_str(), out.size()); -} - -void cheat_engine::import_cheats_from_str(const std::string& str_cheats) -{ - auto cheats_vec = fmt::split(str_cheats, {"^^^"}); - - for (auto& cheat_line : cheats_vec) - { - cheat_info new_cheat; - if (new_cheat.from_str(cheat_line)) - cheats[new_cheat.game][new_cheat.offset] = new_cheat; - } -} - -std::string cheat_engine::export_cheats_to_str() const -{ - std::string cheats_str; - - for (const auto& game : cheats) - { - for (const auto& offset : ::at32(cheats, game.first)) - { - cheats_str += offset.second.to_str(); - cheats_str += "^^^"; - } - } - - return cheats_str; -} - -bool cheat_engine::exist(const std::string& game, const u32 offset) const -{ - return cheats.contains(game) && ::at32(cheats, game).contains(offset); -} - -void cheat_engine::add(const std::string& game, const std::string& description, const cheat_type type, const u32 offset, const std::string& red_script) -{ - cheats[game][offset] = cheat_info{game, description, type, offset, red_script}; -} - -cheat_info* cheat_engine::get(const std::string& game, const u32 offset) -{ - if (!exist(game, offset)) - return nullptr; - - return &cheats[game][offset]; -} - -bool cheat_engine::erase(const std::string& game, const u32 offset) -{ - if (!exist(game, offset)) - return false; - - cheats[game].erase(offset); - return true; -} - -bool cheat_engine::resolve_script(u32& final_offset, const u32 offset, const std::string& red_script) -{ - enum operand - { - operand_equal, - operand_add, - operand_sub - }; - - auto do_operation = [](const operand op, u32& param1, const u32 param2) -> u32 - { - switch (op) - { - case operand_equal: return param1 = param2; - case operand_add: return param1 += param2; - case operand_sub: return param1 -= param2; - } - - return ensure(0); - }; - - operand cur_op = operand_equal; - u32 index = 0; - - while (index < red_script.size()) - { - if (std::isdigit(static_cast(red_script[index]))) - { - std::string num_string; - for (; index < red_script.size(); index++) - { - if (!std::isdigit(static_cast(red_script[index]))) - break; - - num_string += red_script[index]; - } - - const u32 num_value = std::stoul(num_string); - do_operation(cur_op, final_offset, num_value); - } - else - { - switch (red_script[index]) - { - case '$': - { - do_operation(cur_op, final_offset, offset); - index++; - break; - } - case '[': - { - // find corresponding ] - s32 found_close = 1; - std::string sub_script; - for (index++; index < red_script.size(); index++) - { - if (found_close == 0) - break; - - if (red_script[index] == ']') - found_close--; - else if (red_script[index] == '[') - found_close++; - - if (found_close != 0) - sub_script += red_script[index]; - } - - if (found_close) - return false; - - // Resolves content of [] - u32 res_addr = 0; - if (!resolve_script(res_addr, offset, sub_script)) - return false; - - // Tries to get value at resolved address - bool success; - const u32 res_value = get_value(res_addr, success); - - if (!success) - return false; - - do_operation(cur_op, final_offset, res_value); - break; - } - case '+': - cur_op = operand_add; - index++; - break; - case '-': - cur_op = operand_sub; - index++; - break; - case ' ': index++; break; - default: log_cheat.fatal("invalid character in redirection script"); return false; - } - } - } - - return true; -} - -template -std::vector cheat_engine::search(const T value, const std::vector& to_filter) -{ - std::vector results; - - to_be_t value_swapped = value; - - if (Emu.IsStopped()) - return {}; - - cpu_thread::suspend_all(nullptr, {}, [&] - { - if (!to_filter.empty()) - { - for (const auto& off : to_filter) - { - if (vm::check_addr(off)) - { - if (*vm::get_super_ptr(off) == value_swapped) - results.push_back(off); - } - } - } - else - { - // Looks through mapped memory - for (u32 page_start = 0x10000; page_start < 0xF0000000; page_start += 4096) - { - if (vm::check_addr(page_start)) - { - // Assumes the values are aligned - for (u32 index = 0; index < 4096; index += sizeof(T)) - { - if (*vm::get_super_ptr(page_start + index) == value_swapped) - results.push_back(page_start + index); - } - } - } - } - }); - - return results; -} - -template -T cheat_engine::get_value(const u32 offset, bool& success) -{ - if (Emu.IsStopped()) - { - success = false; - return 0; - } - - return cpu_thread::suspend_all(nullptr, {}, [&]() -> T - { - if (!vm::check_addr(offset)) - { - success = false; - return 0; - } - - success = true; - return *vm::get_super_ptr(offset); - }); -} - -template -bool cheat_engine::set_value(const u32 offset, const T value) -{ - if (Emu.IsStopped()) - return false; - - if (!vm::check_addr(offset)) - { - return false; - } - - return cpu_thread::suspend_all(nullptr, {}, [&] - { - if (!vm::check_addr(offset)) - { - return false; - } - - *vm::get_super_ptr(offset) = value; - - const bool exec_code_at_start = vm::check_addr(offset, vm::page_executable); - const bool exec_code_at_end = [&]() - { - if constexpr (sizeof(T) == 1) - { - return exec_code_at_start; - } - else - { - return vm::check_addr(offset + sizeof(T) - 1, vm::page_executable); - } - }(); - - if (exec_code_at_end || exec_code_at_start) - { - extern void ppu_register_function_at(u32, u32, ppu_intrp_func_t); - - u32 addr = offset, size = sizeof(T); - - if (exec_code_at_end && exec_code_at_start) - { - size = utils::align(addr + size, 4) - (addr & -4); - addr &= -4; - } - else if (exec_code_at_end) - { - size -= utils::align(size - 4096 + (addr & 4095), 4); - addr = utils::align(addr, 4096); - } - else if (exec_code_at_start) - { - size = utils::align(4096 - (addr & 4095), 4); - addr &= -4; - } - - // Reinitialize executable code - ppu_register_function_at(addr, size, nullptr); - } - - return true; - }); -} - -bool cheat_engine::is_addr_safe(const u32 offset) -{ - if (Emu.IsStopped()) - return false; - - const auto ppum = g_fxo->try_get>(); - - if (!ppum) - { - log_cheat.fatal("Failed to get ppu_module"); - return false; - } - - std::vector> segs; - - for (const auto& seg : ppum->segs) - { - if ((seg.flags & 3)) - { - segs.emplace_back(seg.addr, seg.size); - } - } - - if (segs.empty()) - { - log_cheat.fatal("Couldn't find a +rw-x section"); - return false; - } - - for (const auto& seg : segs) - { - if (offset >= seg.first && offset < (seg.first + seg.second)) - return true; - } - - return false; -} - -u32 cheat_engine::reverse_lookup(const u32 addr, const u32 max_offset, const u32 max_depth, const u32 cur_depth) -{ - for (u32 index = 0; index <= max_offset; index += 4) - { - std::vector ptrs = search(addr - index, {}); - - log_cheat.fatal("Found %d pointer(s) for addr 0x%x [offset: %d cur_depth:%d]", ptrs.size(), addr, index, cur_depth); - - for (const auto& ptr : ptrs) - { - if (is_addr_safe(ptr)) - return ptr; - } - - // If depth has not been reached dig deeper - if (!ptrs.empty() && cur_depth < max_depth) - { - for (const auto& ptr : ptrs) - { - const u32 result = reverse_lookup(ptr, max_offset, max_depth, cur_depth + 1); - if (result) - return result; - } - } - } - - return 0; -} - -enum cheat_table_columns : int -{ - title = 0, - description, - type, - offset, - script -}; - -cheat_manager_dialog::cheat_manager_dialog(QWidget* parent) - : QDialog(parent) -{ - setWindowTitle(tr("Cheat Manager")); - setObjectName("cheat_manager"); - setMinimumSize(QSize(800, 400)); - - QVBoxLayout* main_layout = new QVBoxLayout(); - - tbl_cheats = new QTableWidget(this); - tbl_cheats->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection); - tbl_cheats->setSelectionBehavior(QAbstractItemView::SelectRows); - tbl_cheats->setContextMenuPolicy(Qt::CustomContextMenu); - tbl_cheats->setColumnCount(5); - tbl_cheats->setHorizontalHeaderLabels(QStringList() << tr("Game") << tr("Description") << tr("Type") << tr("Offset") << tr("Script")); - main_layout->addWidget(tbl_cheats); - - QHBoxLayout* btn_layout = new QHBoxLayout(); - QLabel* lbl_value_final = new QLabel(tr("Current Value:")); - edt_value_final = new QLineEdit(); - btn_apply = new QPushButton(tr("Apply"), this); - btn_apply->setEnabled(false); - btn_layout->addWidget(lbl_value_final); - btn_layout->addWidget(edt_value_final); - btn_layout->addWidget(btn_apply); - main_layout->addLayout(btn_layout); - - QGroupBox* grp_add_cheat = new QGroupBox(tr("Cheat Search")); - QVBoxLayout* grp_add_cheat_layout = new QVBoxLayout(); - QHBoxLayout* grp_add_cheat_sub_layout = new QHBoxLayout(); - QPushButton* btn_new_search = new QPushButton(tr("New Search")); - btn_new_search->setEnabled(false); - btn_filter_results = new QPushButton(tr("Filter Results")); - btn_filter_results->setEnabled(false); - edt_cheat_search_value = new QLineEdit(); - cbx_cheat_search_type = new QComboBox(); - - for (u64 i = 0; i < cheat_type_max; i++) - { - const QString item_text = get_localized_cheat_type(static_cast(i)); - cbx_cheat_search_type->addItem(item_text); - } - cbx_cheat_search_type->setCurrentIndex(static_cast(cheat_type::signed_32_cheat)); - grp_add_cheat_sub_layout->addWidget(btn_new_search); - grp_add_cheat_sub_layout->addWidget(btn_filter_results); - grp_add_cheat_sub_layout->addWidget(edt_cheat_search_value); - grp_add_cheat_sub_layout->addWidget(cbx_cheat_search_type); - grp_add_cheat_layout->addLayout(grp_add_cheat_sub_layout); - lst_search = new QListWidget(this); - lst_search->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection); - lst_search->setSelectionBehavior(QAbstractItemView::SelectRows); - lst_search->setContextMenuPolicy(Qt::CustomContextMenu); - grp_add_cheat_layout->addWidget(lst_search); - grp_add_cheat->setLayout(grp_add_cheat_layout); - main_layout->addWidget(grp_add_cheat); - - setLayout(main_layout); - - // Edit/Manage UI - connect(tbl_cheats, &QTableWidget::itemClicked, [this](QTableWidgetItem* item) - { - if (!item) - return; - - const int row = item->row(); - - if (row == -1) - return; - - cheat_info* cheat = g_cheat.get(tbl_cheats->item(row, cheat_table_columns::title)->text().toStdString(), tbl_cheats->item(row, cheat_table_columns::offset)->data(Qt::UserRole).toUInt()); - if (!cheat) - { - log_cheat.fatal("Failed to retrieve cheat selected from internal cheat_engine"); - return; - } - - u32 final_offset; - if (!cheat->red_script.empty()) - { - final_offset = 0; - if (!cheat_engine::resolve_script(final_offset, cheat->offset, cheat->red_script)) - { - btn_apply->setEnabled(false); - edt_value_final->setText(tr("Failed to resolve redirection script")); - return; - } - } - else - { - final_offset = cheat->offset; - } - - if (Emu.IsStopped()) - { - btn_apply->setEnabled(false); - edt_value_final->setText(tr("This Application is not running")); - return; - } - - bool success = false; - u64 result_value{}; - f64 result_value_f{}; - - switch (cheat->type) - { - case cheat_type::unsigned_8_cheat: result_value = cheat_engine::get_value(final_offset, success); break; - case cheat_type::unsigned_16_cheat: result_value = cheat_engine::get_value(final_offset, success); break; - case cheat_type::unsigned_32_cheat: result_value = cheat_engine::get_value(final_offset, success); break; - case cheat_type::unsigned_64_cheat: result_value = cheat_engine::get_value(final_offset, success); break; - case cheat_type::signed_8_cheat: result_value = cheat_engine::get_value(final_offset, success); break; - case cheat_type::signed_16_cheat: result_value = cheat_engine::get_value(final_offset, success); break; - case cheat_type::signed_32_cheat: result_value = cheat_engine::get_value(final_offset, success); break; - case cheat_type::signed_64_cheat: result_value = cheat_engine::get_value(final_offset, success); break; - case cheat_type::float_32_cheat: result_value_f = cheat_engine::get_value(final_offset, success); break; - default: log_cheat.fatal("Unsupported cheat type"); return; - } - - if (success) - { - if (cheat->type >= cheat_type::signed_8_cheat && cheat->type <= cheat_type::signed_64_cheat) - edt_value_final->setText(tr("%1").arg(static_cast(result_value))); - else if (cheat->type == cheat_type::float_32_cheat) - edt_value_final->setText(tr("%1").arg(result_value_f)); - else - edt_value_final->setText(tr("%1").arg(result_value)); - } - else - { - edt_value_final->setText(tr("Failed to get the value from memory")); - } - - btn_apply->setEnabled(success); - }); - - connect(tbl_cheats, &QTableWidget::cellChanged, [this](int row, int column) - { - QTableWidgetItem* item = tbl_cheats->item(row, column); - if (!item) - { - return; - } - - if (column != cheat_table_columns::description && column != cheat_table_columns::script) - { - log_cheat.fatal("A column other than description and script was edited"); - return; - } - - cheat_info* cheat = g_cheat.get(tbl_cheats->item(row, cheat_table_columns::title)->text().toStdString(), tbl_cheats->item(row, cheat_table_columns::offset)->data(Qt::UserRole).toUInt()); - if (!cheat) - { - log_cheat.fatal("Failed to retrieve cheat edited from internal cheat_engine"); - return; - } - - switch (column) - { - case cheat_table_columns::description: cheat->description = item->text().toStdString(); break; - case cheat_table_columns::script: cheat->red_script = item->text().toStdString(); break; - default: break; - } - - g_cheat.save(); - }); - - connect(tbl_cheats, &QTableWidget::customContextMenuRequested, [this](const QPoint& loc) - { - const QPoint globalPos = tbl_cheats->mapToGlobal(loc); - QMenu* menu = new QMenu(); - QAction* delete_cheats = new QAction(tr("Delete"), menu); - QAction* import_cheats = new QAction(tr("Import Cheats")); - QAction* export_cheats = new QAction(tr("Export Cheats")); - QAction* reverse_cheat = new QAction(tr("Reverse-Lookup Cheat")); - - connect(delete_cheats, &QAction::triggered, [this]() - { - const auto selected = tbl_cheats->selectedItems(); - - std::set rows; - - for (const auto& sel : selected) - { - const int row = sel->row(); - - if (rows.count(row)) - continue; - - g_cheat.erase(tbl_cheats->item(row, cheat_table_columns::title)->text().toStdString(), tbl_cheats->item(row, cheat_table_columns::offset)->data(Qt::UserRole).toUInt()); - rows.insert(row); - } - - update_cheat_list(); - }); - - connect(import_cheats, &QAction::triggered, [this]() - { - QClipboard* clipboard = QGuiApplication::clipboard(); - g_cheat.import_cheats_from_str(clipboard->text().toStdString()); - update_cheat_list(); - }); - - connect(export_cheats, &QAction::triggered, [this]() - { - const auto selected = tbl_cheats->selectedItems(); - - std::set rows; - std::string export_string; - - for (const auto& sel : selected) - { - const int row = sel->row(); - - if (rows.count(row)) - continue; - - cheat_info* cheat = g_cheat.get(tbl_cheats->item(row, cheat_table_columns::title)->text().toStdString(), tbl_cheats->item(row, cheat_table_columns::offset)->data(Qt::UserRole).toUInt()); - if (cheat) - export_string += cheat->to_str() + "^^^"; - - rows.insert(row); - } - - QClipboard* clipboard = QGuiApplication::clipboard(); - clipboard->setText(QString::fromStdString(export_string)); - }); - - connect(reverse_cheat, &QAction::triggered, [this]() - { - QTableWidgetItem* item = tbl_cheats->item(tbl_cheats->currentRow(), cheat_table_columns::offset); - if (item) - { - const u32 offset = item->data(Qt::UserRole).toUInt(); - const u32 result = cheat_engine::reverse_lookup(offset, 32, 12); - - log_cheat.fatal("Result is 0x%x", result); - } - }); - - menu->addAction(delete_cheats); - menu->addSeparator(); - // menu->addAction(reverse_cheat); - // menu->addSeparator(); - menu->addAction(import_cheats); - menu->addAction(export_cheats); - menu->exec(globalPos); - }); - - connect(btn_apply, &QPushButton::clicked, [this](bool /*checked*/) - { - const int row = tbl_cheats->currentRow(); - cheat_info* cheat = g_cheat.get(tbl_cheats->item(row, cheat_table_columns::title)->text().toStdString(), tbl_cheats->item(row, cheat_table_columns::offset)->data(Qt::UserRole).toUInt()); - - if (!cheat) - { - log_cheat.fatal("Failed to retrieve cheat selected from internal cheat_engine"); - return; - } - - std::pair results; - - u32 final_offset; - if (!cheat->red_script.empty()) - { - final_offset = 0; - if (!g_cheat.resolve_script(final_offset, cheat->offset, cheat->red_script)) - { - btn_apply->setEnabled(false); - edt_value_final->setText(tr("Failed to resolve redirection script")); - } - } - else - { - final_offset = cheat->offset; - } - - // TODO: better way to do this? - switch (static_cast(cbx_cheat_search_type->currentIndex())) - { - case cheat_type::unsigned_8_cheat: results = convert_and_set(final_offset); break; - case cheat_type::unsigned_16_cheat: results = convert_and_set(final_offset); break; - case cheat_type::unsigned_32_cheat: results = convert_and_set(final_offset); break; - case cheat_type::unsigned_64_cheat: results = convert_and_set(final_offset); break; - case cheat_type::signed_8_cheat: results = convert_and_set(final_offset); break; - case cheat_type::signed_16_cheat: results = convert_and_set(final_offset); break; - case cheat_type::signed_32_cheat: results = convert_and_set(final_offset); break; - case cheat_type::signed_64_cheat: results = convert_and_set(final_offset); break; - case cheat_type::float_32_cheat: results = convert_and_set(final_offset); break; - default: log_cheat.fatal("Unsupported cheat type"); return; - } - - if (!results.first) - { - QMessageBox::warning(this, tr("Error converting value"), tr("Couldn't convert the value you typed to the integer type of that cheat"), QMessageBox::Ok); - return; - } - - if (!results.second) - { - QMessageBox::warning(this, tr("Error applying value"), tr("Couldn't patch memory"), QMessageBox::Ok); - return; - } - }); - - // Search UI - connect(btn_new_search, &QPushButton::clicked, [this](bool /*checked*/) - { - offsets_found.clear(); - do_the_search(); - }); - - connect(edt_cheat_search_value, &QLineEdit::textChanged, this, [btn_new_search, this](const QString& text) - { - if (btn_new_search) - { - btn_new_search->setEnabled(!text.isEmpty()); - } - if (btn_filter_results) - { - btn_filter_results->setEnabled(!text.isEmpty() && !offsets_found.empty()); - } - }); - - connect(btn_filter_results, &QPushButton::clicked, [this](bool /*checked*/) - { - do_the_search(); - }); - - connect(lst_search, &QListWidget::customContextMenuRequested, [this](const QPoint& loc) - { - const QPoint globalPos = lst_search->mapToGlobal(loc); - const int current_row = lst_search->currentRow(); - QListWidgetItem* item = lst_search->item(current_row); - - // Skip if the item was a placeholder - if (!item || item->data(Qt::UserRole).toBool()) - return; - - QMenu* menu = new QMenu(); - - QAction* add_to_cheat_list = new QAction(tr("Add to cheat list"), menu); - - const u32 offset = offsets_found[current_row]; - const cheat_type type = static_cast(cbx_cheat_search_type->currentIndex()); - - connect(add_to_cheat_list, &QAction::triggered, [name = Emu.GetTitle(), offset, type, this]() - { - if (g_cheat.exist(name, offset)) - { - if (QMessageBox::question(this, tr("Cheat already exists"), tr("Do you want to overwrite the existing cheat?"), QMessageBox::Ok | QMessageBox::Cancel) != QMessageBox::Ok) - return; - } - - std::string comment; - if (!cheat_engine::is_addr_safe(offset)) - comment = "Unsafe"; - - g_cheat.add(name, comment, type, offset, ""); - update_cheat_list(); - }); - - menu->addAction(add_to_cheat_list); - menu->exec(globalPos); - }); - - update_cheat_list(); -} - -cheat_manager_dialog::~cheat_manager_dialog() -{ - inst = nullptr; -} - -cheat_manager_dialog* cheat_manager_dialog::get_dlg(QWidget* parent) -{ - if (inst == nullptr) - inst = new cheat_manager_dialog(parent); - - return inst; -} - -template -T cheat_manager_dialog::convert_from_QString(const QString& str, bool& success) -{ - if constexpr (std::is_same_v) - { - const u16 result_16 = str.toUShort(&success); - - if (result_16 > 0xFF) - success = false; - - return static_cast(result_16); - } - - if constexpr (std::is_same_v) - return str.toUShort(&success); - - if constexpr (std::is_same_v) - return str.toUInt(&success); - - if constexpr (std::is_same_v) - return str.toULongLong(&success); - - if constexpr (std::is_same_v) - { - const s16 result_16 = str.toShort(&success); - if (result_16 < -128 || result_16 > 127) - success = false; - - return static_cast(result_16); - } - - if constexpr (std::is_same_v) - return str.toShort(&success); - - if constexpr (std::is_same_v) - return str.toInt(&success); - - if constexpr (std::is_same_v) - return str.toLongLong(&success); - - if constexpr (std::is_same_v) - return str.toFloat(&success); - - return {}; -} - -template -bool cheat_manager_dialog::convert_and_search() -{ - bool res_conv = false; - const QString to_search = edt_cheat_search_value->text(); - - const T value = convert_from_QString(to_search, res_conv); - - if (!res_conv) - return false; - - offsets_found = cheat_engine::search(value, offsets_found); - return true; -} - -template -std::pair cheat_manager_dialog::convert_and_set(u32 offset) -{ - bool res_conv = false; - const QString to_set = edt_value_final->text(); - - const T value = convert_from_QString(to_set, res_conv); - - if (!res_conv) - return {false, false}; - - return {true, cheat_engine::set_value(offset, value)}; -} - -void cheat_manager_dialog::do_the_search() -{ - bool res_conv = false; - - // TODO: better way to do this? - switch (static_cast(cbx_cheat_search_type->currentIndex())) - { - case cheat_type::unsigned_8_cheat: res_conv = convert_and_search(); break; - case cheat_type::unsigned_16_cheat: res_conv = convert_and_search(); break; - case cheat_type::unsigned_32_cheat: res_conv = convert_and_search(); break; - case cheat_type::unsigned_64_cheat: res_conv = convert_and_search(); break; - case cheat_type::signed_8_cheat: res_conv = convert_and_search(); break; - case cheat_type::signed_16_cheat: res_conv = convert_and_search(); break; - case cheat_type::signed_32_cheat: res_conv = convert_and_search(); break; - case cheat_type::signed_64_cheat: res_conv = convert_and_search(); break; - case cheat_type::float_32_cheat: res_conv = convert_and_search(); break; - default: log_cheat.fatal("Unsupported cheat type"); break; - } - - if (!res_conv) - { - QMessageBox::warning(this, tr("Error converting value"), tr("Couldn't convert the search value you typed to the integer type you selected"), QMessageBox::Ok); - return; - } - - lst_search->clear(); - - const usz size = offsets_found.size(); - - if (size == 0) - { - QListWidgetItem* item = new QListWidgetItem(tr("Nothing found")); - item->setData(Qt::UserRole, true); - lst_search->insertItem(0, item); - } - else if (size > 10000) - { - // Only show entries below a fixed amount. Too many entries can take forever to render and fill up memory quickly. - QListWidgetItem* item = new QListWidgetItem(tr("Too many entries to display (%0)").arg(size)); - item->setData(Qt::UserRole, true); - lst_search->insertItem(0, item); - } - else - { - for (u32 row = 0; row < size; row++) - { - lst_search->insertItem(row, QString("0x%0").arg(offsets_found[row], 1, 16).toUpper()); - } - } - - btn_filter_results->setEnabled(!offsets_found.empty() && edt_cheat_search_value && !edt_cheat_search_value->text().isEmpty()); -} - -void cheat_manager_dialog::update_cheat_list() -{ - usz num_rows = 0; - for (const auto& name : g_cheat.cheats) - num_rows += name.second.size(); - - tbl_cheats->setRowCount(::narrow(num_rows)); - - u32 row = 0; - { - const QSignalBlocker blocker(tbl_cheats); - for (const auto& game : g_cheat.cheats) - { - for (const auto& offset : game.second) - { - QTableWidgetItem* item_game = new QTableWidgetItem(QString::fromStdString(offset.second.game)); - item_game->setFlags(item_game->flags() & ~Qt::ItemIsEditable); - tbl_cheats->setItem(row, cheat_table_columns::title, item_game); - - tbl_cheats->setItem(row, cheat_table_columns::description, new QTableWidgetItem(QString::fromStdString(offset.second.description))); - - std::string type_formatted; - fmt::append(type_formatted, "%s", offset.second.type); - QTableWidgetItem* item_type = new QTableWidgetItem(QString::fromStdString(type_formatted)); - item_type->setFlags(item_type->flags() & ~Qt::ItemIsEditable); - tbl_cheats->setItem(row, cheat_table_columns::type, item_type); - - QTableWidgetItem* item_offset = new QTableWidgetItem(QString("0x%0").arg(offset.second.offset, 1, 16).toUpper()); - item_offset->setData(Qt::UserRole, QVariant(offset.second.offset)); - item_offset->setFlags(item_offset->flags() & ~Qt::ItemIsEditable); - tbl_cheats->setItem(row, cheat_table_columns::offset, item_offset); - - tbl_cheats->setItem(row, cheat_table_columns::script, new QTableWidgetItem(QString::fromStdString(offset.second.red_script))); - - row++; - } - } - } - - g_cheat.save(); -} - -QString cheat_manager_dialog::get_localized_cheat_type(cheat_type type) -{ - switch (type) - { - case cheat_type::unsigned_8_cheat: return tr("Unsigned 8 bits"); - case cheat_type::unsigned_16_cheat: return tr("Unsigned 16 bits"); - case cheat_type::unsigned_32_cheat: return tr("Unsigned 32 bits"); - case cheat_type::unsigned_64_cheat: return tr("Unsigned 64 bits"); - case cheat_type::signed_8_cheat: return tr("Signed 8 bits"); - case cheat_type::signed_16_cheat: return tr("Signed 16 bits"); - case cheat_type::signed_32_cheat: return tr("Signed 32 bits"); - case cheat_type::signed_64_cheat: return tr("Signed 64 bits"); - case cheat_type::float_32_cheat: return tr("Float 32 bits"); - case cheat_type::max: break; - } - std::string type_formatted; - fmt::append(type_formatted, "%s", type); - return QString::fromStdString(type_formatted); -} diff --git a/rpcs3qt-legacy/cheat_manager.h b/rpcs3qt-legacy/cheat_manager.h deleted file mode 100644 index a6447787c..000000000 --- a/rpcs3qt-legacy/cheat_manager.h +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -#include "util/types.hpp" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "util/cheat_info.h" - -class cheat_engine -{ -public: - cheat_engine(); - - bool exist(const std::string& game, const u32 offset) const; - void add(const std::string& game, const std::string& description, const cheat_type type, const u32 offset, const std::string& red_script); - cheat_info* get(const std::string& game, const u32 offset); - bool erase(const std::string& game, const u32 offset); - - void import_cheats_from_str(const std::string& str_cheats); - std::string export_cheats_to_str() const; - void save() const; - - // Static functions to find/get/set values in ps3 memory - static bool resolve_script(u32& final_offset, const u32 offset, const std::string& red_script); - - template - static std::vector search(const T value, const std::vector& to_filter); - - template - static T get_value(const u32 offset, bool& success); - template - static bool set_value(const u32 offset, const T value); - - static bool is_addr_safe(const u32 offset); - static u32 reverse_lookup(const u32 addr, const u32 max_offset, const u32 max_depth, const u32 cur_depth = 0); - - std::map> cheats; - -private: - const std::string m_cheats_filename = "cheats.yml"; -}; - -class cheat_manager_dialog : public QDialog -{ - Q_OBJECT -public: - cheat_manager_dialog(QWidget* parent = nullptr); - ~cheat_manager_dialog(); - static cheat_manager_dialog* get_dlg(QWidget* parent = nullptr); - - cheat_manager_dialog(cheat_manager_dialog const&) = delete; - void operator=(cheat_manager_dialog const&) = delete; - -protected: - void update_cheat_list(); - void do_the_search(); - - template - T convert_from_QString(const QString& str, bool& success); - - template - bool convert_and_search(); - template - std::pair convert_and_set(u32 offset); - -protected: - QTableWidget* tbl_cheats = nullptr; - QListWidget* lst_search = nullptr; - - QLineEdit* edt_value_final = nullptr; - QPushButton* btn_apply = nullptr; - - QLineEdit* edt_cheat_search_value = nullptr; - QComboBox* cbx_cheat_search_type = nullptr; - - QPushButton* btn_filter_results = nullptr; - - u32 current_offset{}; - std::vector offsets_found; - - cheat_engine g_cheat; - -private: - static cheat_manager_dialog* inst; - - QString get_localized_cheat_type(cheat_type type); -}; diff --git a/rpcs3qt-legacy/config_adapter.cpp b/rpcs3qt-legacy/config_adapter.cpp deleted file mode 100644 index 516c2fc3e..000000000 --- a/rpcs3qt-legacy/config_adapter.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "config_adapter.h" -#include "Emu/system_config.h" - -LOG_CHANNEL(cfg_log, "CFG"); - -// Helper methods to interact with YAML and the config settings. -namespace cfg_adapter -{ - static cfg::_base& get_cfg(const cfg::_base& root, const std::string& name) - { - if (root.get_type() == cfg::type::node) - { - for (const auto& node : static_cast(root).get_nodes()) - { - if (node->get_name() == name) - { - return *node; - } - } - } - - fmt::throw_exception("Node not found: %s", name); - } - - static cfg::_base& get_cfg(cfg::_base& root, const cfg_location::const_iterator begin, const cfg_location::const_iterator end) - { - return begin == end ? root : get_cfg(get_cfg(root, *begin), begin + 1, end); - } - - YAML::Node get_node(const YAML::Node& node, const cfg_location::const_iterator begin, const cfg_location::const_iterator end) - { - if (begin == end) - { - return node; - } - - if (!node || !node.IsMap()) - { - cfg_log.fatal("Node error. A cfg_location does not match its cfg::node (location: %s)", get_yaml_node_location(node)); - return YAML::Node(); - } - - return get_node(node[*begin], begin + 1, end); // TODO - } - - YAML::Node get_node(const YAML::Node& node, const cfg_location& location) - { - return get_node(node, location.cbegin(), location.cend()); - } - - std::vector get_options(const cfg_location& location) - { - return cfg_adapter::get_cfg(g_cfg, location.cbegin(), location.cend()).to_list(); - } - - static bool get_is_dynamic(const cfg_location& location) - { - return cfg_adapter::get_cfg(g_cfg, location.cbegin(), location.cend()).get_is_dynamic(); - } - - bool get_is_dynamic(emu_settings_type type) - { - return get_is_dynamic(::at32(settings_location, type)); - } - - std::string get_setting_name(emu_settings_type type) - { - const cfg_location& loc = ::at32(settings_location, type); - return ::at32(loc, loc.size() - 1); - } -} // namespace cfg_adapter diff --git a/rpcs3qt-legacy/config_adapter.h b/rpcs3qt-legacy/config_adapter.h deleted file mode 100644 index f34e1e661..000000000 --- a/rpcs3qt-legacy/config_adapter.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "emu_settings_type.h" -#include "util/yaml.hpp" - -// Helper methods to interact with YAML and the config settings. -namespace cfg_adapter -{ - YAML::Node get_node(const YAML::Node& node, const cfg_location::const_iterator begin, const cfg_location::const_iterator end); - - /** Syntactic sugar to get a setting at a given config location. */ - YAML::Node get_node(const YAML::Node& node, const cfg_location& location); - - /** Returns possible options for values for some particular setting.*/ - std::vector get_options(const cfg_location& location); - - /** Returns dynamic property for some particular setting.*/ - bool get_is_dynamic(emu_settings_type type); - - /** Returns the string for a given setting.*/ - std::string get_setting_name(emu_settings_type type); -} // namespace cfg_adapter diff --git a/rpcs3qt-legacy/config_checker.cpp b/rpcs3qt-legacy/config_checker.cpp deleted file mode 100644 index accab6366..000000000 --- a/rpcs3qt-legacy/config_checker.cpp +++ /dev/null @@ -1,214 +0,0 @@ -#include "stdafx.h" -#include "config_checker.h" -#include "Emu/system_config.h" - -#include -#include -#include -#include -#include -#include - -LOG_CHANNEL(gui_log, "GUI"); - -config_checker::config_checker(QWidget* parent, const QString& content, bool is_log) : QDialog(parent) -{ - setObjectName("config_checker"); - setAttribute(Qt::WA_DeleteOnClose); - - QVBoxLayout* layout = new QVBoxLayout(); - QLabel* label = new QLabel(this); - layout->addWidget(label); - - QString result; - - if (check_config(content, result, is_log)) - { - setWindowTitle(tr("Interesting!")); - - if (result.isEmpty()) - { - label->setText(tr("Found config.\nIt seems to match the default config.")); - } - else - { - label->setText(tr("Found config.\nSome settings seem to deviate from the default config:")); - - QTextEdit* text_box = new QTextEdit(); - text_box->setReadOnly(true); - text_box->setHtml(result); - layout->addWidget(text_box); - - resize(400, 600); - } - } - else - { - setWindowTitle(tr("Ooops!")); - label->setText(result); - } - - QDialogButtonBox* box = new QDialogButtonBox(QDialogButtonBox::Close); - connect(box, &QDialogButtonBox::rejected, this, &QDialog::reject); - layout->addWidget(box); - - setLayout(layout); -} - -bool config_checker::check_config(QString content, QString& result, bool is_log) -{ - cfg_root config{}; - - if (is_log) - { - const QString start_token = "SYS: Used configuration:\n"; - const QString end_token = "\n·"; - - qsizetype start = content.indexOf(start_token); - qsizetype end = -1; - - if (start >= 0) - { - start += start_token.size(); - end = content.indexOf(end_token, start); - } - - if (end < 0) - { - result = tr("Cannot find any config!"); - return false; - } - - content = content.mid(start, end - start); - } - - if (!config.from_string(content.toStdString())) - { - gui_log.error("log_viewer: Failed to parse config:\n%s", content); - result = tr("Cannot find any config!"); - return false; - } - - std::function print_diff_recursive; - print_diff_recursive = [&print_diff_recursive](const cfg::_base* base, std::string& diff, int indentation) -> void - { - if (!base) - { - return; - } - - const auto indent = [](std::string& str, int indentation) - { - for (int i = 0; i < indentation * 2; i++) - { - str += " "; - } - }; - - switch (base->get_type()) - { - case cfg::type::node: - { - if (const auto& node = static_cast(base)) - { - std::string diff_tmp; - - for (const auto& n : node->get_nodes()) - { - print_diff_recursive(n, diff_tmp, indentation + 1); - } - - if (!diff_tmp.empty()) - { - indent(diff, indentation); - - if (!base->get_name().empty()) - { - fmt::append(diff, "%s:
", base->get_name()); - } - - fmt::append(diff, "%s", diff_tmp); - } - } - break; - } - case cfg::type::_bool: - case cfg::type::_enum: - case cfg::type::_int: - case cfg::type::uint: - case cfg::type::string: - { - const std::string val = base->to_string(); - const std::string def = base->def_to_string(); - - if (val != def) - { - indent(diff, indentation); - - if (def.empty()) - { - fmt::append(diff, "%s: %s
", base->get_name(), val); - } - else - { - fmt::append(diff, "%s: %s default: %s
", base->get_name(), val, def); - } - } - break; - } - case cfg::type::set: - { - if (const auto& node = static_cast(base)) - { - const std::vector set_entries = node->to_list(); - - if (!set_entries.empty()) - { - indent(diff, indentation); - fmt::append(diff, "%s:
", base->get_name()); - - for (const std::string& entry : set_entries) - { - indent(diff, indentation + 1); - fmt::append(diff, "- %s
", entry); - } - } - } - break; - } - case cfg::type::log: - { - if (const auto& node = static_cast(base)) - { - const auto& log_entries = node->get_map(); - - if (!log_entries.empty()) - { - indent(diff, indentation); - fmt::append(diff, "%s:
", base->get_name()); - - for (const auto& entry : log_entries) - { - indent(diff, indentation + 1); - fmt::append(diff, "%s: %s
", entry.first, entry.second); - } - } - } - break; - } - case cfg::type::map: - case cfg::type::node_map: - case cfg::type::device: - { - // Ignored - break; - } - } - }; - - std::string diff; - print_diff_recursive(&config, diff, 0); - result = QString::fromStdString(diff); - - return true; -} diff --git a/rpcs3qt-legacy/config_checker.h b/rpcs3qt-legacy/config_checker.h deleted file mode 100644 index 900fb3035..000000000 --- a/rpcs3qt-legacy/config_checker.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -class config_checker : public QDialog -{ - Q_OBJECT - -public: - config_checker(QWidget* parent, const QString& path, bool is_log); - - bool check_config(QString content, QString& result, bool is_log); -}; diff --git a/rpcs3qt-legacy/curl_handle.cpp b/rpcs3qt-legacy/curl_handle.cpp deleted file mode 100644 index dbb393915..000000000 --- a/rpcs3qt-legacy/curl_handle.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "stdafx.h" -#include "curl_handle.h" -#include "util/logs.hpp" - -#ifdef _WIN32 -#include "util/StrUtil.h" -#endif - -LOG_CHANNEL(network_log, "NET"); - -namespace rpcs3::curl -{ - - curl_handle::curl_handle() - { - reset_error_buffer(); - - m_curl = curl_easy_init(); - - CURLcode err = curl_easy_setopt(m_curl, CURLOPT_ERRORBUFFER, m_error_buffer.data()); - if (err != CURLE_OK) - network_log.error("curl_easy_setopt(CURLOPT_ERRORBUFFER): %s", curl_easy_strerror(err)); - - m_uses_error_buffer = err == CURLE_OK; - - err = curl_easy_setopt(m_curl, CURLOPT_VERBOSE, g_curl_verbose); - if (err != CURLE_OK) - network_log.error("curl_easy_setopt(CURLOPT_VERBOSE, %d): %s", g_curl_verbose, curl_easy_strerror(err)); - -#ifdef _WIN32 - // Tell curl to use the native CA store for certificate verification - err = curl_easy_setopt(m_curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); - if (err != CURLE_OK) - network_log.error("curl_easy_setopt(CURLOPT_SSL_OPTIONS): %s", curl_easy_strerror(err)); -#endif - } - - curl_handle::~curl_handle() - { - curl_easy_cleanup(m_curl); - } - - CURL* curl_handle::get_curl() const - { - return m_curl; - } - - void curl_handle::reset_error_buffer() - { - ensure(m_error_buffer.size() == CURL_ERROR_SIZE); - m_error_buffer[0] = 0; - } - - std::string curl_handle::get_verbose_error(CURLcode code) const - { - if (m_uses_error_buffer) - { - ensure(m_error_buffer.size() == CURL_ERROR_SIZE); - if (m_error_buffer[0]) - { - return fmt::format("Curl error (%d): %s\nDetails: %s", static_cast(code), curl_easy_strerror(code), m_error_buffer.data()); - } - } - - return fmt::format("Curl error (%d): %s", static_cast(code), curl_easy_strerror(code)); - } - -} // namespace rpcs3::curl - -#ifdef _WIN32 -// Functions exported from our user_settings.h in WolfSSL, implemented in RPCS3 -extern "C" -{ - - FILE* wolfSSL_fopen_utf8(const char* name, const char* mode) - { - return _wfopen(utf8_to_wchar(name).c_str(), utf8_to_wchar(mode).c_str()); - } - - int wolfSSL_stat_utf8(const char* path, struct _stat* buffer) - { - return _wstat(utf8_to_wchar(path).c_str(), buffer); - } -} -#endif diff --git a/rpcs3qt-legacy/curl_handle.h b/rpcs3qt-legacy/curl_handle.h deleted file mode 100644 index c4abbf146..000000000 --- a/rpcs3qt-legacy/curl_handle.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include - -#ifndef CURL_STATICLIB -#define CURL_STATICLIB -#endif -#include - -namespace rpcs3::curl -{ - inline bool g_curl_verbose = false; - - class curl_handle - { - public: - explicit curl_handle(); - ~curl_handle(); - - CURL* get_curl() const; - - operator CURL*() const - { - return get_curl(); - } - - void reset_error_buffer(); - std::string get_verbose_error(CURLcode code) const; - - private: - CURL* m_curl = nullptr; - bool m_uses_error_buffer = false; - std::array m_error_buffer; - }; - -} // namespace rpcs3::curl diff --git a/rpcs3qt-legacy/custom_dialog.cpp b/rpcs3qt-legacy/custom_dialog.cpp deleted file mode 100644 index add3c328a..000000000 --- a/rpcs3qt-legacy/custom_dialog.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "custom_dialog.h" - -custom_dialog::custom_dialog(bool disableCancel, QWidget* parent) - : QDialog(parent), m_disable_cancel(disableCancel) -{ - if (m_disable_cancel) - { - setWindowFlags(windowFlags() & ~Qt::WindowCloseButtonHint); - } -} - -void custom_dialog::keyPressEvent(QKeyEvent* event) -{ - // this won't work with Alt+F4, the window still closes - if (m_disable_cancel && event->key() == Qt::Key_Escape) - { - event->ignore(); - } - else - { - QDialog::keyPressEvent(event); - } -} - -void custom_dialog::closeEvent(QCloseEvent* event) -{ - // spontaneous: don't close on external system level events like Alt+F4 - if (m_disable_cancel && event->spontaneous()) - { - event->ignore(); - } - else - { - QDialog::closeEvent(event); - } -} diff --git a/rpcs3qt-legacy/custom_dialog.h b/rpcs3qt-legacy/custom_dialog.h deleted file mode 100644 index 48531384d..000000000 --- a/rpcs3qt-legacy/custom_dialog.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include - -class custom_dialog : public QDialog -{ - Q_OBJECT - -public: - explicit custom_dialog(bool disableCancel, QWidget* parent = nullptr); - bool m_disable_cancel; - -private: - void keyPressEvent(QKeyEvent* event) override; - void closeEvent(QCloseEvent* event) override; -}; diff --git a/rpcs3qt-legacy/custom_dock_widget.h b/rpcs3qt-legacy/custom_dock_widget.h deleted file mode 100644 index 4899e63d6..000000000 --- a/rpcs3qt-legacy/custom_dock_widget.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include -#include -#include - -class custom_dock_widget : public QDockWidget -{ -private: - std::shared_ptr m_title_bar_widget; - bool m_is_title_bar_visible = true; - -public: - explicit custom_dock_widget(const QString& title, QWidget* parent = Q_NULLPTR, Qt::WindowFlags flags = Qt::WindowFlags()) - : QDockWidget(title, parent, flags) - { - m_title_bar_widget.reset(titleBarWidget()); - - connect(this, &QDockWidget::topLevelChanged, [this](bool /* topLevel*/) - { - SetTitleBarVisible(m_is_title_bar_visible); - style()->unpolish(this); - style()->polish(this); - }); - } - - void SetTitleBarVisible(bool visible) - { - if (visible || isFloating()) - { - if (m_title_bar_widget.get() != titleBarWidget()) - { - setTitleBarWidget(m_title_bar_widget.get()); - QMargins margins = widget()->contentsMargins(); - margins.setTop(0); - widget()->setContentsMargins(margins); - } - } - else - { - setTitleBarWidget(new QWidget()); - QMargins margins = widget()->contentsMargins(); - margins.setTop(1); - widget()->setContentsMargins(margins); - } - - m_is_title_bar_visible = visible; - } - -protected: - void paintEvent(QPaintEvent* event) override - { - // We need to repaint the dock widgets as plain widgets in floating mode. - // Source: https://stackoverflow.com/questions/10272091/cannot-add-a-background-image-to-a-qdockwidget - if (isFloating()) - { - QStyleOption opt; - opt.initFrom(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); - return; - } - - // Use inherited method for docked mode because otherwise the dock would lose the title etc. - QDockWidget::paintEvent(event); - } -}; diff --git a/rpcs3qt-legacy/custom_table_widget_item.cpp b/rpcs3qt-legacy/custom_table_widget_item.cpp deleted file mode 100644 index 4c17da7a4..000000000 --- a/rpcs3qt-legacy/custom_table_widget_item.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "custom_table_widget_item.h" -#include "util/StrFmt.h" - -#include - -custom_table_widget_item::custom_table_widget_item(const std::string& text, int sort_role, const QVariant& sort_value) - : movie_item(QString::fromStdString(text).simplified()) // simplified() forces single line text -{ - if (sort_role != Qt::DisplayRole) - { - setData(sort_role, sort_value, true); - } -} - -custom_table_widget_item::custom_table_widget_item(const QString& text, int sort_role, const QVariant& sort_value) - : movie_item(text.simplified()) // simplified() forces single line text -{ - if (sort_role != Qt::DisplayRole) - { - setData(sort_role, sort_value, true); - } -} - -bool custom_table_widget_item::operator<(const QTableWidgetItem& other) const -{ - if (m_sort_role == Qt::DisplayRole) - { - return QTableWidgetItem::operator<(other); - } - - const QVariant data_l = data(m_sort_role); - const QVariant data_r = other.data(m_sort_role); - const int type_l = data_l.metaType().id(); - const int type_r = data_r.metaType().id(); - - ensure(type_l == type_r); - - switch (type_l) - { - case QMetaType::Type::Bool: - case QMetaType::Type::Int: - return data_l.toInt() < data_r.toInt(); - case QMetaType::Type::UInt: - return data_l.toUInt() < data_r.toUInt(); - case QMetaType::Type::LongLong: - return data_l.toLongLong() < data_r.toLongLong(); - case QMetaType::Type::ULongLong: - return data_l.toULongLong() < data_r.toULongLong(); - case QMetaType::Type::Double: - return data_l.toDouble() < data_r.toDouble(); - case QMetaType::Type::QDate: - return data_l.toDate() < data_r.toDate(); - case QMetaType::Type::QTime: - return data_l.toTime() < data_r.toTime(); - case QMetaType::Type::QDateTime: - return data_l.toDateTime() < data_r.toDateTime(); - case QMetaType::Type::Char: - case QMetaType::Type::QString: - return data_l.toString() < data_r.toString(); - default: - fmt::throw_exception("Unimplemented type %s", QMetaType(type_l).name()); - } -} - -void custom_table_widget_item::setData(int role, const QVariant& value, bool assign_sort_role) -{ - if (assign_sort_role) - { - m_sort_role = role; - } - QTableWidgetItem::setData(role, value); -} diff --git a/rpcs3qt-legacy/custom_table_widget_item.h b/rpcs3qt-legacy/custom_table_widget_item.h deleted file mode 100644 index 7e87e35f5..000000000 --- a/rpcs3qt-legacy/custom_table_widget_item.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "movie_item.h" - -class custom_table_widget_item : public movie_item -{ -private: - int m_sort_role = Qt::DisplayRole; - -public: - using QTableWidgetItem::setData; - - custom_table_widget_item() = default; - custom_table_widget_item(const std::string& text, int sort_role = Qt::DisplayRole, const QVariant& sort_value = 0); - custom_table_widget_item(const QString& text, int sort_role = Qt::DisplayRole, const QVariant& sort_value = 0); - - bool operator<(const QTableWidgetItem& other) const override; - - void setData(int role, const QVariant& value, bool assign_sort_role); -}; diff --git a/rpcs3qt-legacy/debugger_add_bp_window.cpp b/rpcs3qt-legacy/debugger_add_bp_window.cpp deleted file mode 100644 index 710757fac..000000000 --- a/rpcs3qt-legacy/debugger_add_bp_window.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "debugger_add_bp_window.h" -#include "util/StrFmt.h" -#include "util/StrUtil.h" -#include "breakpoint_handler.h" -#include "util/types.hpp" - -#include -#include -#include -#include -#include -#include -#include - -debugger_add_bp_window::debugger_add_bp_window(breakpoint_list* bp_list, QWidget* parent) - : QDialog(parent) -{ - ensure(bp_list); - - setWindowTitle(tr("Add a breakpoint")); - setModal(true); - - QVBoxLayout* vbox_panel = new QVBoxLayout(); - - QHBoxLayout* hbox_top = new QHBoxLayout(); - QLabel* l_address = new QLabel(tr("Address")); - QLineEdit* t_address = new QLineEdit(); - t_address->setPlaceholderText(tr("Address here")); - t_address->setFocus(); - - hbox_top->addWidget(l_address); - hbox_top->addWidget(t_address); - vbox_panel->addLayout(hbox_top); - - QHBoxLayout* hbox_bot = new QHBoxLayout(); - QComboBox* co_bptype = new QComboBox(this); - QStringList qstr_breakpoint_types; - - qstr_breakpoint_types -#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS - << tr("Memory Read") - << tr("Memory Write") - << tr("Memory Read&Write") -#endif - << tr("Execution"); - - co_bptype->addItems(qstr_breakpoint_types); - - hbox_bot->addWidget(co_bptype); - vbox_panel->addLayout(hbox_bot); - - QHBoxLayout* hbox_buttons = new QHBoxLayout(); - QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - button_box->button(QDialogButtonBox::Ok)->setText(tr("Add")); - - hbox_buttons->addWidget(button_box); - vbox_panel->addLayout(hbox_buttons); - - setLayout(vbox_panel); - - connect(button_box, &QDialogButtonBox::accepted, this, [=, this] - { - const std::string str_address = t_address->text().toStdString(); - - if (str_address.empty()) - { - QMessageBox::warning(this, tr("Add BP error"), tr("Address is empty!")); - return; - } - - // We always want hex - const std::string parsed_string = (!str_address.starts_with("0x") && !str_address.starts_with("0X")) ? fmt::format("0x%s", str_address) : str_address; - u64 parsed_address = 0; - - // We don't accept 0 - if (!try_to_uint64(&parsed_address, parsed_string, 1, 0xFF'FF'FF'FF)) - { - QMessageBox::warning(this, tr("Add BP error"), tr("Address is invalid!")); - return; - } - - const u32 address = static_cast(parsed_address); - bs_t bp_t{}; - -#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS - switch (co_bptype->currentIndex()) - { - case 0: - bp_t = breakpoint_types::bp_read; - break; - case 1: - bp_t = breakpoint_types::bp_write; - break; - case 2: - bp_t = breakpoint_types::bp_read + breakpoint_types::bp_write; - break; - case 3: - bp_t = breakpoint_types::bp_exec; - break; - default: - break; - } -#else - bp_t = breakpoint_types::bp_exec; -#endif - - if (bp_t) - bp_list->AddBreakpoint(address, bp_t); - - QDialog::accept(); - }); - - connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); - - move(QCursor::pos()); -} diff --git a/rpcs3qt-legacy/debugger_add_bp_window.h b/rpcs3qt-legacy/debugger_add_bp_window.h deleted file mode 100644 index c57c6873d..000000000 --- a/rpcs3qt-legacy/debugger_add_bp_window.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "breakpoint_list.h" - -#include - -class debugger_add_bp_window : public QDialog -{ - Q_OBJECT - -public: - explicit debugger_add_bp_window(breakpoint_list* bp_list, QWidget* parent = nullptr); -}; diff --git a/rpcs3qt-legacy/debugger_frame.cpp b/rpcs3qt-legacy/debugger_frame.cpp deleted file mode 100644 index 4305a6e75..000000000 --- a/rpcs3qt-legacy/debugger_frame.cpp +++ /dev/null @@ -1,1740 +0,0 @@ -#include "debugger_frame.h" -#include "register_editor_dialog.h" -#include "instruction_editor_dialog.h" -#include "memory_viewer_panel.h" -#include "elf_memory_dumping_dialog.h" -#include "gui_settings.h" -#include "debugger_list.h" -#include "breakpoint_list.h" -#include "breakpoint_handler.h" -#include "call_stack_list.h" -#include "input_dialog.h" -#include "qt_utils.h" - -#include "Emu/System.h" -#include "Emu/IdManager.h" -#include "Emu/RSX/RSXThread.h" -#include "Emu/RSX/RSXDisAsm.h" -#include "Emu/Cell/PPUAnalyser.h" -#include "Emu/Cell/PPUDisAsm.h" -#include "Emu/Cell/PPUThread.h" -#include "Emu/Cell/SPUDisAsm.h" -#include "Emu/Cell/SPUThread.h" -#include "Emu/CPU/CPUThread.h" -#include "Emu/CPU/CPUDisAsm.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "debugger_add_bp_window.h" -#include "util/asm.hpp" - -constexpr auto qstr = QString::fromStdString; - -constexpr auto s_pause_flags = cpu_flag::dbg_pause + cpu_flag::dbg_global_pause; - -extern atomic_t g_debugger_pause_all_threads_on_bp; - -extern const ppu_decoder g_ppu_itype; -#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS -breakpoint_handler g_breakpoint_handler = breakpoint_handler(); -#endif - -extern bool is_using_interpreter(thread_class t_class); - -extern std::shared_ptr make_disasm(const cpu_thread* cpu, shared_ptr handle); - -debugger_frame::debugger_frame(std::shared_ptr gui_settings, QWidget* parent) - : custom_dock_widget(tr("Debugger [Press F1 for Help]"), parent), m_gui_settings(std::move(gui_settings)) -{ - setContentsMargins(0, 0, 0, 0); - - m_update = new QTimer(this); - connect(m_update, &QTimer::timeout, this, &debugger_frame::UpdateUI); - - m_mono = QFontDatabase::systemFont(QFontDatabase::FixedFont); - m_mono.setPointSize(9); - - QVBoxLayout* vbox_p_main = new QVBoxLayout(); - vbox_p_main->setContentsMargins(5, 5, 5, 5); - - QHBoxLayout* hbox_b_main = new QHBoxLayout(); - hbox_b_main->setContentsMargins(0, 0, 0, 0); - -#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS - m_ppu_breakpoint_handler = &g_breakpoint_handler; -#else - m_ppu_breakpoint_handler = new breakpoint_handler(); -#endif - - m_breakpoint_list = new breakpoint_list(this, m_ppu_breakpoint_handler); - - m_debugger_list = new debugger_list(this, m_gui_settings, m_ppu_breakpoint_handler); - m_debugger_list->installEventFilter(this); - - m_call_stack_list = new call_stack_list(this); - - m_choice_units = new QComboBox(this); - m_choice_units->setSizeAdjustPolicy(QComboBox::AdjustToContents); - m_choice_units->setMaxVisibleItems(30); - m_choice_units->setMaximumWidth(500); - m_choice_units->setEditable(true); - m_choice_units->setInsertPolicy(QComboBox::NoInsert); - m_choice_units->lineEdit()->setPlaceholderText(tr("Choose a thread")); - m_choice_units->completer()->setCompletionMode(QCompleter::PopupCompletion); - m_choice_units->completer()->setMaxVisibleItems(30); - m_choice_units->completer()->setFilterMode(Qt::MatchContains); - m_choice_units->installEventFilter(this); - - m_go_to_addr = new QPushButton(tr("Go To Address"), this); - m_go_to_pc = new QPushButton(tr("Go To PC"), this); - m_btn_step = new QPushButton(tr("Step"), this); - m_btn_step_over = new QPushButton(tr("Step Over"), this); - m_btn_add_bp = new QPushButton(tr("Add BP"), this); - m_btn_run = new QPushButton(RunString, this); - - EnableButtons(false); - - ChangeColors(); - - hbox_b_main->addWidget(m_go_to_addr); - hbox_b_main->addWidget(m_go_to_pc); - hbox_b_main->addWidget(m_btn_step); - hbox_b_main->addWidget(m_btn_step_over); - hbox_b_main->addWidget(m_btn_add_bp); - hbox_b_main->addWidget(m_btn_run); - hbox_b_main->addWidget(m_choice_units); - hbox_b_main->addStretch(); - - // Misc state - m_misc_state = new QPlainTextEdit(this); - m_misc_state->setLineWrapMode(QPlainTextEdit::NoWrap); - m_misc_state->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); - - // Registers - m_regs = new QPlainTextEdit(this); - m_regs->setLineWrapMode(QPlainTextEdit::NoWrap); - m_regs->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); - - m_debugger_list->setFont(m_mono); - m_misc_state->setFont(m_mono); - m_regs->setFont(m_mono); - m_call_stack_list->setFont(m_mono); - - m_right_splitter = new QSplitter(this); - m_right_splitter->setOrientation(Qt::Vertical); - m_right_splitter->addWidget(m_misc_state); - m_right_splitter->addWidget(m_regs); - m_right_splitter->addWidget(m_call_stack_list); - m_right_splitter->addWidget(m_breakpoint_list); - - // Set relative sizes for widgets - m_right_splitter->setStretchFactor(0, 2); // misc state - m_right_splitter->setStretchFactor(1, 8); // registers - m_right_splitter->setStretchFactor(2, 3); // call stack - m_right_splitter->setStretchFactor(3, 1); // breakpoint list - - m_splitter = new QSplitter(this); - m_splitter->addWidget(m_debugger_list); - m_splitter->addWidget(m_right_splitter); - - QHBoxLayout* hbox_w_list = new QHBoxLayout(); - hbox_w_list->addWidget(m_splitter); - - vbox_p_main->addLayout(hbox_b_main); - vbox_p_main->addLayout(hbox_w_list); - - QWidget* body = new QWidget(this); - body->setLayout(vbox_p_main); - setWidget(body); - - connect(m_go_to_addr, &QAbstractButton::clicked, this, &debugger_frame::ShowGotoAddressDialog); - connect(m_go_to_pc, &QAbstractButton::clicked, this, [this]() - { - ShowPC(true); - }); - - connect(m_btn_step, &QAbstractButton::clicked, this, &debugger_frame::DoStep); - connect(m_btn_step_over, &QAbstractButton::clicked, [this]() - { - DoStep(true); - }); - - connect(m_btn_add_bp, &QAbstractButton::clicked, this, [this] - { - debugger_add_bp_window dlg(m_breakpoint_list, this); - dlg.exec(); - }); - - connect(m_btn_run, &QAbstractButton::clicked, this, &debugger_frame::RunBtnPress); - - connect(m_choice_units->lineEdit(), &QLineEdit::editingFinished, [&] - { - m_choice_units->clearFocus(); - }); - - connect(m_choice_units, QOverload::of(&QComboBox::currentIndexChanged), this, [&]() - { - m_is_spu_disasm_mode = false; - OnSelectUnit(); - }); - connect(this, &QDockWidget::visibilityChanged, this, &debugger_frame::EnableUpdateTimer); - - connect(m_debugger_list, &debugger_list::BreakpointRequested, m_breakpoint_list, &breakpoint_list::HandleBreakpointRequest); - connect(m_breakpoint_list, &breakpoint_list::RequestShowAddress, m_debugger_list, &debugger_list::ShowAddress); - - connect(this, &debugger_frame::CallStackUpdateRequested, m_call_stack_list, &call_stack_list::HandleUpdate); - connect(m_call_stack_list, &call_stack_list::RequestShowAddress, m_debugger_list, &debugger_list::ShowAddress); - - m_debugger_list->RefreshView(); - - m_choice_units->clear(); - m_choice_units->addItem(NoThreadString); -} - -void debugger_frame::SaveSettings() const -{ - m_gui_settings->SetValue(gui::d_splitterState, m_splitter->saveState()); -} - -void debugger_frame::ChangeColors() const -{ - if (m_debugger_list) - { - const QColor color = gui::utils::get_foreground_color(); - - m_debugger_list->m_color_bp = m_breakpoint_list->m_color_bp = gui::utils::get_label_color("debugger_frame_breakpoint", Qt::yellow, Qt::darkYellow, QPalette::Window); - m_debugger_list->m_color_pc = gui::utils::get_label_color("debugger_frame_pc", Qt::green, Qt::darkGreen, QPalette::Window); - m_debugger_list->m_text_color_bp = m_breakpoint_list->m_text_color_bp = gui::utils::get_label_color("debugger_frame_breakpoint", color, color); - m_debugger_list->m_text_color_pc = gui::utils::get_label_color("debugger_frame_pc", color, color); - } -} - -bool debugger_frame::eventFilter(QObject* object, QEvent* event) -{ - // There's no overlap between keys so returning true wouldn't matter. - if (object == m_debugger_list && event->type() == QEvent::KeyPress) - { - keyPressEvent(static_cast(event)); - event->accept(); // Restore accepted state - return false; - } - - if (object == m_choice_units && event->type() == QEvent::FocusOut) - { - if (int index = m_choice_units->currentIndex(); index >= 0) - { - // Restore item text automatically on focus-out after search - m_choice_units->setCurrentText(m_choice_units->itemText(index)); - } - } - - return false; -} - -void debugger_frame::closeEvent(QCloseEvent* event) -{ - SaveSettings(); - - QDockWidget::closeEvent(event); - Q_EMIT DebugFrameClosed(); -} - -void debugger_frame::showEvent(QShowEvent* event) -{ - // resize splitter widgets - if (!m_splitter->restoreState(m_gui_settings->GetValue(gui::d_splitterState).toByteArray())) - { - const int width_right = width() / 3; - const int width_left = width() - width_right; - m_splitter->setSizes({width_left, width_right}); - } - - QDockWidget::showEvent(event); -} - -void debugger_frame::hideEvent(QHideEvent* event) -{ - // save splitter state or it will resume its initial state on next show - m_gui_settings->SetValue(gui::d_splitterState, m_splitter->saveState()); - QDockWidget::hideEvent(event); -} - -void debugger_frame::open_breakpoints_settings() -{ - QDialog* dlg = new QDialog(this); - dlg->setWindowTitle(tr("Breakpoint Settings")); - dlg->setModal(true); - - QCheckBox* check_box = new QCheckBox(tr("Pause All Threads On Hit"), dlg); - check_box->setCheckable(true); - check_box->setChecked(g_debugger_pause_all_threads_on_bp.load()); - check_box->setToolTip(tr("When set: a breakpoint hit will pause the emulation instead of the current thread." - "\nApplies on all breakpoints in all threads regardless if set before or after changing this setting.")); - - connect(check_box, &QCheckBox::clicked, dlg, [](bool checked) - { - g_debugger_pause_all_threads_on_bp = checked; - }); - - QPushButton* button_ok = new QPushButton(tr("OK"), dlg); - connect(button_ok, &QAbstractButton::clicked, dlg, &QDialog::accept); - - QHBoxLayout* hbox_layout = new QHBoxLayout(dlg); - hbox_layout->addWidget(check_box); - hbox_layout->addWidget(button_ok); - dlg->setLayout(hbox_layout); - dlg->setAttribute(Qt::WA_DeleteOnClose); - dlg->open(); -} - -void debugger_frame::keyPressEvent(QKeyEvent* event) -{ - if (!isActiveWindow()) - { - event->ignore(); - return; - } - - const auto cpu = get_cpu(); - const int row = m_debugger_list->currentRow(); - - switch (event->key()) - { - case Qt::Key_F1: - { - if (event->isAutoRepeat()) - { - event->ignore(); - return; - } - - QDialog* dlg = new QDialog(this); - dlg->setAttribute(Qt::WA_DeleteOnClose); - dlg->setWindowTitle(tr("Debugger Guide & Shortcuts")); - - QLabel* l = new QLabel(tr( - "Keys Ctrl+G: Go to typed address." - "\nKeys Ctrl+B: Open breakpoints settings." - "\nKeys Ctrl+C: Copy instruction contents." - "\nKeys Ctrl+F: Find thread." - "\nKeys Alt+S: Capture SPU images of selected SPU or generalized form when used from PPU." - "\nKeys Alt+S: Launch a memory viewer pointed to the current RSX semaphores location when used from RSX." - "\nKeys Alt+R: Load last saved SPU state capture." - "\nKeys Alt+F5: Show the SPU disassmebler dialog." - "\nKey D: SPU MFC commands logger, MFC debug setting must be enabled." - "\nKey D: Also PPU calling history logger, interpreter and non-zero call history size must be used." - "\nKey E: Instruction Editor: click on the instruction you want to modify, then press E." - "\nKey F: Dedicated floating point mode switch for SPU threads." - "\nKey R: Registers Editor for selected thread." - "\nKey N: Show next instruction the thread will execute after marked instruction, does nothing if target is not predictable." - "\nKey M: Show the Memory Viewer with initial address pointing to the marked instruction." - "\nKey I: Show RSX method detail." - "\nKey F10: Perform step-over on instructions. (skip function calls)" - "\nKey F11: Perform single-stepping on instructions." - "\nKey F1: Show this help dialog." - "\nKey Up: Scroll one instruction upwards. (address is decremented)" - "\nKey Down: Scroll one instruction downwards. (address is incremented)" - "\nKey Page-Up: Scroll upwards with steps count equal to the viewed instruction count." - "\nKey Page-Down: Scroll downwards with steps count equal to the viewed instruction count." - "\nDouble-click: Set breakpoints.")); - - gui::utils::set_font_size(*l, 9); - - QVBoxLayout* layout = new QVBoxLayout(); - layout->addWidget(l); - dlg->setLayout(layout); - dlg->setFixedSize(dlg->sizeHint()); - dlg->move(QCursor::pos()); - dlg->open(); - return; - } - default: break; - } - - if (event->modifiers() == Qt::ControlModifier) - { - switch (const auto key = event->key()) - { - case Qt::Key_PageUp: - case Qt::Key_PageDown: - { - if (event->isAutoRepeat()) - { - event->ignore(); - break; - } - - const int count = m_choice_units->count(); - const int cur_index = m_choice_units->currentIndex(); - - if (count && cur_index >= 0) - { - // Wrap around - // Adding count so the result would not be negative, that would alter the remainder operation - m_choice_units->setCurrentIndex((cur_index + count + (key == Qt::Key_PageUp ? -1 : 1)) % count); - } - - return; - } - case Qt::Key_F: - { - m_choice_units->clearEditText(); - m_choice_units->setFocus(); - return; - } - default: break; - } - } - - if (!cpu) - { - event->ignore(); - return; - } - - const u32 address_limits = (cpu->get_class() == thread_class::spu ? 0x3fffc : ~3); - const u32 pc = (m_debugger_list->m_pc & address_limits); - const u32 selected = (m_debugger_list->m_showing_selected_instruction ? m_debugger_list->m_selected_instruction : cpu->get_pc()) & address_limits; - - const auto modifiers = event->modifiers(); - - if (modifiers == Qt::ControlModifier) - { - if (event->isAutoRepeat()) - { - event->ignore(); - return; - } - - switch (event->key()) - { - case Qt::Key_G: - { - ShowGotoAddressDialog(); - return; - } - case Qt::Key_B: - { - open_breakpoints_settings(); - return; - } - default: break; - } - } - else - { - switch (event->key()) - { - case Qt::Key_D: - { - if (event->isAutoRepeat()) - { - break; - } - - auto get_max_allowed = [&](QString title, QString description, u32 limit) -> u32 - { - input_dialog dlg(4, "", title, description.arg(limit), QString::number(limit), this); - - QFont mono = QFontDatabase::systemFont(QFontDatabase::FixedFont); - mono.setPointSize(8); - dlg.set_input_font(mono, false); - dlg.set_clear_button_enabled(false); - dlg.set_button_enabled(QDialogButtonBox::StandardButton::Ok, false); - dlg.set_validator(new QRegularExpressionValidator(QRegularExpression("^[1-9][0-9]*$"), &dlg)); - - u32 max = 0; - - connect(&dlg, &input_dialog::text_changed, [&](const QString& changed) - { - bool ok = false; - const u32 dummy = changed.toUInt(&ok, 10); - ok = ok && dummy && dummy <= limit; - dlg.set_button_enabled(QDialogButtonBox::StandardButton::Ok, ok); - - if (ok) - { - max = dummy; - } - }); - - if (dlg.exec() != QDialog::Accepted) - { - max = 0; - } - - return max; - }; - - auto copy_overlapping_list = [&](u64& index, u64 max, const std::vector& in, std::vector& out, bool& emptied) - { - max = std::min(max, in.size()); - - const u64 current_pos = index % in.size(); - const u64 last_elements = std::min(current_pos, max); - const u64 overlapped_old_elements = std::min(index, max) - last_elements; - - out.resize(overlapped_old_elements + last_elements); - - // Save list contents (only the relavant parts) - std::copy(in.end() - overlapped_old_elements, in.end(), out.begin()); - std::copy_n(in.begin() + current_pos - last_elements, last_elements, out.begin() + overlapped_old_elements); - - // Check if max elements to log is larger/equal to current list size - if ((emptied = index && max >= index)) - { - // Empty list when possible (further calls' history logging will not log any call before this) - index = 0; - } - }; - - if (cpu->get_class() == thread_class::spu && g_cfg.core.mfc_debug) - { - const u32 max = get_max_allowed(tr("Max MFC cmds logged"), tr("Decimal only, max allowed is %0."), spu_thread::max_mfc_dump_idx); - - // Preallocate in order to save execution time when inside suspend_all. - std::vector copy(max); - - bool emptied = false; - - cpu_thread::suspend_all(nullptr, {}, [&] - { - const auto spu = static_cast(cpu); - copy_overlapping_list(spu->mfc_dump_idx, max, spu->mfc_history, copy, emptied); - }); - - std::string ret; - - u32 i = 0; - for (auto it = copy.rbegin(); it != copy.rend(); it++, i++) - { - auto& dump = *it; - - const u32 pc = std::exchange(dump.cmd.eah, 0); - fmt::append(ret, "\n(%d) PC 0x%05x: [%s] (%s)", i, pc, dump.cmd, spu_block_hash{dump.block_hash}); - - if (dump.cmd.cmd == MFC_PUTLLC_CMD) - { - fmt::append(ret, " %s", dump.cmd.tag == MFC_PUTLLC_SUCCESS ? "(passed)" : "(failed)"); - } - - auto load = [&](usz index) - { - be_t data{}; - std::memcpy(&data, dump.data + index * sizeof(data), sizeof(data)); - return data; - }; - - for (usz i = 0; i < utils::aligned_div(std::min(dump.cmd.size, 128), 4); i += 4) - { - fmt::append(ret, "\n[0x%02x] %08x %08x %08x %08x", i * sizeof(be_t), load(i + 0), load(i + 1), load(i + 2), load(i + 3)); - } - } - - if (ret.empty()) - { - ret = "No MFC commands have been logged"; - } - - if (emptied) - { - ret += "\nPrevious MFC history has been emptied!"; - } - - spu_log.success("SPU MFC dump of '%s': %s", cpu->get_name(), ret); - } - else if (cpu->get_class() == thread_class::ppu && g_cfg.core.ppu_call_history) - { - const u32 max = get_max_allowed(tr("Max PPU calls logged"), tr("Decimal only, max allowed is %0."), ppu_thread::call_history_max_size); - - // Preallocate in order to save execution time when inside suspend_all. - std::vector copy(max); - std::vector sys_copy(ppu_thread::syscall_history_max_size); - - std::array emptied{}; - - cpu_thread::suspend_all(nullptr, {}, [&] - { - auto& list = static_cast(cpu)->call_history; - auto& sys_list = static_cast(cpu)->syscall_history; - copy_overlapping_list(list.index, max, list.data, copy, emptied[0]); - copy_overlapping_list(sys_list.index, max, sys_list.data, sys_copy, emptied[1]); - }); - - std::string ret; - - PPUDisAsm dis_asm(cpu_disasm_mode::normal, vm::g_sudo_addr); - u32 i = 0; - - for (auto it = copy.rbegin(); it != copy.rend(); it++, i++) - { - dis_asm.disasm(*it); - fmt::append(ret, "\n(%u) 0x%08x: %s", i, *it, dis_asm.last_opcode); - } - - i = 0; - for (auto it = sys_copy.rbegin(); it != sys_copy.rend(); it++, i++) - { - fmt::append(ret, "\n(%u) 0x%08x: %s, 0x%x, r3=0x%x, r4=0x%x, r5=0x%x, r6=0x%x", i, it->cia, it->func_name, it->error, it->args[0], it->args[1], it->args[2], it->args[3]); - } - - if (ret.empty()) - { - ret = "No PPU calls have been logged"; - } - - if (emptied[0]) - { - ret += "\nPrevious call history has been emptied!"; - } - - if (emptied[1]) - { - ret += "\nPrevious HLE call history has been emptied!"; - } - - ppu_log.success("PPU calling history dump of '%s': %s", cpu->get_name(), ret); - } - - return; - } - case Qt::Key_E: - { - if (event->isAutoRepeat()) - { - break; - } - - if (cpu->get_class() == thread_class::ppu || cpu->get_class() == thread_class::spu) - { - if (!m_inst_editor) - { - m_inst_editor = new instruction_editor_dialog(this, selected, m_disasm.get(), make_check_cpu(cpu)); - connect(m_inst_editor, &QDialog::finished, this, [this]() - { - m_inst_editor = nullptr; - }); - m_inst_editor->show(); - } - } - - return; - } - case Qt::Key_F: - { - if (event->isAutoRepeat()) - { - break; - } - - if (cpu->get_class() == thread_class::ppu) - { - static_cast(cpu)->debugger_mode.atomic_op([](ppu_debugger_mode& mode) - { - mode = static_cast((static_cast(mode) + 1) % static_cast(ppu_debugger_mode::max_mode)); - }); - - return; - } - if (cpu->get_class() == thread_class::spu) - { - static_cast(cpu)->debugger_mode.atomic_op([](spu_debugger_mode& mode) - { - mode = static_cast((static_cast(mode) + 1) % static_cast(spu_debugger_mode::max_mode)); - }); - - return; - } - - break; - } - case Qt::Key_R: - { - if (event->isAutoRepeat()) - { - break; - } - - if (cpu->get_class() == thread_class::ppu || cpu->get_class() == thread_class::spu) - { - if (cpu->get_class() == thread_class::spu && modifiers & Qt::AltModifier) - { - static_cast(cpu)->try_load_debug_capture(); - return; - } - - if (!m_reg_editor) - { - m_reg_editor = new register_editor_dialog(this, m_disasm.get(), make_check_cpu(cpu)); - connect(m_reg_editor, &QDialog::finished, this, [this]() - { - m_reg_editor = nullptr; - }); - m_reg_editor->show(); - } - } - - return; - } - case Qt::Key_S: - { - if (event->isAutoRepeat()) - { - break; - } - - if (modifiers & Qt::AltModifier) - { - if (cpu->get_class() == thread_class::rsx) - { - if (u32 addr = static_cast(cpu)->label_addr) - { - // Memory viewer pointing to RSX semaphores - idm::make(this, m_disasm, addr, make_check_cpu(nullptr)); - } - - return; - } - - if (cpu->get_class() == thread_class::ppu) - { - new elf_memory_dumping_dialog(pc, m_gui_settings, this); - return; - } - - if (cpu->get_class() != thread_class::spu) - { - return; - } - - if (!cpu->state.all_of(cpu_flag::wait + cpu_flag::dbg_pause)) - { - QMessageBox::warning(this, QObject::tr("Pause the SPU Thread!"), QObject::tr("Cannot perform SPU capture due to the thread needing manual pausing!")); - return; - } - - static_cast(cpu)->capture_state(); - return; - } - - break; - } - case Qt::Key_N: - { - // Next instruction according to code flow - // Known branch targets are selected over next PC for conditional branches - // Indirect branches (unknown targets, such as function return) do not proceed to any instruction - std::array res{umax, umax}; - - const u32 selected = (m_debugger_list->m_showing_selected_instruction ? m_debugger_list->m_selected_instruction : cpu->get_pc()) & address_limits; - - switch (cpu->get_class()) - { - case thread_class::spu: - { - res = op_branch_targets(selected, spu_opcode_t{static_cast(cpu)->_ref(selected)}); - break; - } - case thread_class::ppu: - { - be_t op{}; - - if (vm::check_addr(selected, vm::page_executable) && vm::try_access(selected, &op, 4, false)) - res = op_branch_targets(selected, op); - - break; - } - default: break; - } - - if (auto it = std::find_if(res.rbegin(), res.rend(), FN(x != umax)); it != res.rend()) - m_debugger_list->ShowAddress(*it - std::max(row, 0) * 4, true); - - return; - } - case Qt::Key_M: - { - if (event->isAutoRepeat()) - { - break; - } - - if (m_disasm && cpu->get_class() == thread_class::spu) - { - // Save shared pointer to shared memory handle, ensure the destructor will not be called until the SPUDisAsm is destroyed - static_cast(m_disasm.get())->set_shm(static_cast(cpu)->shm); - } - - // Memory viewer - idm::make(this, m_disasm, pc, make_check_cpu(cpu)); - return; - } - case Qt::Key_F10: - { - DoStep(true); - return; - } - case Qt::Key_F11: - { - DoStep(false); - return; - } - case Qt::Key_F5: - { - if (modifiers & Qt::AltModifier) - { - OnSelectSPUDisassembler(); - return; - } - - break; - } - default: break; - } - } - - event->ignore(); -} - -cpu_thread* debugger_frame::get_cpu() -{ - if (m_emu_state == system_state::stopped) - { - m_rsx = nullptr; - m_cpu.reset(); - return nullptr; - } - - // Wait flag is raised by the thread itself, acknowledging exit - if (m_cpu) - { - if (m_cpu->state.all_of(cpu_flag::wait + cpu_flag::exit)) - { - m_cpu.reset(); - return nullptr; - } - - return m_cpu.get(); - } - - // m_rsx is raw pointer, when emulation is stopped it won't be cleared - // Therefore need to do invalidation checks manually - - if (m_rsx) - { - if (g_fxo->try_get() != m_rsx || !m_rsx->ctrl || m_rsx->state.all_of(cpu_flag::wait + cpu_flag::exit)) - { - m_rsx = nullptr; - return nullptr; - } - } - - return m_rsx; -} - -std::function debugger_frame::make_check_cpu(cpu_thread* cpu, bool unlocked) -{ - constexpr cpu_thread* null_cpu = nullptr; - - if (Emu.IsStopped()) - { - return []() - { - return null_cpu; - }; - } - - const auto type = cpu ? cpu->get_class() : thread_class::general; - - shared_ptr shared; - - if (g_fxo->is_init>>() && g_fxo->is_init>>()) - { - if (unlocked) - { - if (type == thread_class::ppu) - { - shared = idm::get_unlocked>(cpu->id); - } - else if (type == thread_class::spu) - { - shared = idm::get_unlocked>(cpu->id); - } - } - else - { - if (type == thread_class::ppu) - { - shared = idm::get_unlocked>(cpu->id); - } - else if (type == thread_class::spu) - { - shared = idm::get_unlocked>(cpu->id); - } - } - } - - if (type == thread_class::rsx) - { - if (g_fxo->try_get() != cpu) - { - return []() - { - return null_cpu; - }; - } - } - else if (!shared || shared.get() != cpu) - { - return []() - { - return null_cpu; - }; - } - - return [cpu, type, shared = std::move(shared), emulation_id = Emu.GetEmulationIdentifier()]() mutable -> cpu_thread* - { - if (emulation_id != Emu.GetEmulationIdentifier() || Emu.IsStopped()) - { - // Invalidate all data after Emu.Kill() - shared.reset(); - cpu = nullptr; - return nullptr; - } - - if (type == thread_class::ppu || type == thread_class::spu) - { - // SPU and PPU - if (!shared || shared->state.all_of(cpu_flag::exit + cpu_flag::wait)) - { - shared.reset(); - return nullptr; - } - - return shared.get(); - } - - // RSX - const auto rsx = g_fxo->try_get(); - - if (cpu) - { - if (rsx != cpu || !rsx->ctrl || rsx->state.all_of(cpu_flag::wait + cpu_flag::exit)) - { - cpu = nullptr; - return nullptr; - } - } - - return cpu; - }; -} - -void debugger_frame::UpdateUI() -{ - const auto cpu = get_cpu(); - - // Refresh at a high rate during initialization (looks weird otherwise) - if (m_ui_update_ctr % (cpu || m_ui_update_ctr < 200 || m_debugger_list->m_dirty_flag ? 5 : 50) == 0) - { - // If no change to instruction position happened, update instruction list at 20hz - ShowPC(); - } - - if (m_ui_update_ctr % 20 == 0 && !m_thread_list_pending_update) - { - // Update threads list at 5hz (low priority) - UpdateUnitList(); - } - - if (!cpu) - { - if (m_last_pc != umax || !m_last_query_state.empty()) - { - if (m_ui_update_ctr % 20 && !m_thread_list_pending_update) - { - // Update threads list (thread exited) - UpdateUnitList(); - } - - ShowPC(); - m_last_query_state.clear(); - m_last_pc = -1; - DoUpdate(); - } - } - else if (m_ui_update_ctr % 5 == 0 || m_ui_update_ctr < m_ui_fast_update_permission_deadline) - { - const auto cia = cpu->get_pc(); - const auto size_context = cpu->get_class() == thread_class::ppu ? sizeof(ppu_thread) : - cpu->get_class() == thread_class::spu ? sizeof(spu_thread) : - sizeof(cpu_thread); - - if (m_last_pc != cia || m_last_query_state.size() != size_context || std::memcmp(m_last_query_state.data(), static_cast(cpu), size_context)) - { - // Copy thread data - m_last_query_state.resize(size_context); - std::memcpy(m_last_query_state.data(), static_cast(cpu), size_context); - - m_last_pc = cia; - DoUpdate(); - - const bool paused = !!(cpu->state & s_pause_flags); - - if (paused) - { - m_btn_run->setText(RunString); - } - else - { - m_btn_run->setText(PauseString); - } - - if (m_ui_update_ctr % 5) - { - // Call if it hasn't been called before - ShowPC(); - } - - if (is_using_interpreter(cpu->get_class())) - { - m_btn_step->setEnabled(paused); - m_btn_step_over->setEnabled(paused); - } - } - } - - m_ui_update_ctr++; -} - -void debugger_frame::UpdateUnitList() -{ - const u64 emulation_id = static_cast>(Emu.GetEmulationIdentifier()); - const u64 threads_created = cpu_thread::g_threads_created; - const u64 threads_deleted = cpu_thread::g_threads_deleted; - const system_state emu_state = Emu.GetStatus(); - - std::unique_lock lock{id_manager::g_mutex, std::defer_lock}; - - if (emulation_id == m_emulation_id && threads_created == m_threads_created && threads_deleted == m_threads_deleted && emu_state == m_emu_state) - { - // Nothing to do - m_thread_list_pending_update = false; - return; - } - - const cpu_thread* old_cpu_ptr = get_cpu(); - - if (!lock.try_lock()) - { - m_thread_list_pending_update = true; - QTimer::singleShot(5, [this]() - { - UpdateUnitList(); - }); - return; - } - - std::vector>> cpu_list; - cpu_list.reserve(threads_created >= threads_deleted ? 0 : threads_created - threads_deleted); - - usz reselected_index = umax; - - const auto on_select = [&](u32 id, cpu_thread& cpu) - { - std::function func_cpu = make_check_cpu(std::addressof(cpu), true); - - // Space at the end is to pad a gap on the right - cpu_list.emplace_back(qstr((id >> 24 == 0x55 ? "RSX[0x55555555]" : cpu.get_name()) + ' '), std::move(func_cpu)); - - if (old_cpu_ptr == std::addressof(cpu)) - { - reselected_index = cpu_list.size() - 1; - } - }; - - if (emu_state != system_state::stopped) - { - if (g_fxo->is_init>>()) - { - idm::select>(on_select, idm::unlocked); - } - - if (g_fxo->is_init>>()) - { - idm::select>(on_select, idm::unlocked); - } - - if (const auto render = g_fxo->try_get(); render && render->ctrl) - { - on_select(render->id, *render); - } - } - - lock.unlock(); - - m_emulation_id = emulation_id; - m_threads_created = threads_created; - m_threads_deleted = threads_deleted; - m_emu_state = emu_state; - m_thread_list_pending_update = false; - - { - const QSignalBlocker blocker(m_choice_units); - - m_threads_info.clear(); - m_choice_units->clear(); - - m_threads_info.emplace_back(make_check_cpu(nullptr)); - m_choice_units->addItem(NoThreadString); - - for (auto&& [thread_name, func_cpu] : cpu_list) - { - m_threads_info.emplace_back(std::move(func_cpu)); - m_choice_units->addItem(std::move(thread_name)); - } - - if (reselected_index != umax) - { - // Include no-thread at index 0 - m_choice_units->setCurrentIndex(::narrow(reselected_index + 1)); - } - } - - // Close dialogs which are tied to the specific thread selected - if (reselected_index == umax) - { - if (m_reg_editor) - m_reg_editor->close(); - if (m_inst_editor) - m_inst_editor->close(); - if (m_goto_dialog) - m_goto_dialog->close(); - } - - if (emu_state == system_state::stopped) - { - ClearBreakpoints(); - ClearCallStack(); - } - - OnSelectUnit(); - - m_choice_units->update(); -} - -void debugger_frame::OnSelectUnit() -{ - if (m_is_spu_disasm_mode) - { - return; - } - - cpu_thread* selected = nullptr; - - if (m_emu_state != system_state::stopped) - { - if (int index = m_choice_units->currentIndex(); index >= 0 && index + 0u < m_threads_info.size()) - { - selected = ::at32(m_threads_info, index)(); - } - - if (selected && m_cpu.get() == selected) - { - // They match, nothing to do. - return; - } - - if (selected && m_rsx == selected) - { - return; - } - - if (!selected && !m_rsx && !m_cpu) - { - return; - } - } - - m_disasm.reset(); - m_cpu.reset(); - m_rsx = nullptr; - m_spu_disasm_memory.reset(); - - if (selected) - { - const u32 cpu_id = selected->id; - - switch (cpu_id >> 24) - { - case 1: - { - m_cpu = idm::get_unlocked>(cpu_id); - - if (selected == m_cpu.get()) - { - m_disasm = make_disasm(selected, m_cpu); - } - - break; - } - case 2: - { - m_cpu = idm::get_unlocked>(cpu_id); - - if (selected == m_cpu.get()) - { - m_disasm = make_disasm(selected, m_cpu); - } - - break; - } - case 0x55: - { - m_rsx = static_cast(selected); - - if (get_cpu()) - { - m_disasm = make_disasm(m_rsx, null_ptr); - } - - break; - } - default: break; - } - } - - if (!m_disasm) - { - m_cpu.reset(); - m_rsx = nullptr; - } - - EnableButtons(true); - - m_debugger_list->UpdateCPUData(m_disasm); - m_breakpoint_list->UpdateCPUData(m_disasm); - - ShowPC(true); - DoUpdate(); - UpdateUI(); -} - -void debugger_frame::OnSelectSPUDisassembler() -{ - if (m_spu_disasm_dialog) - { - m_spu_disasm_dialog->move(QCursor::pos()); - m_spu_disasm_dialog->show(); - m_spu_disasm_dialog->setFocus(); - return; - } - - m_spu_disasm_dialog = new QDialog(this); - m_spu_disasm_dialog->setWindowTitle(tr("SPU Disassembler Properties")); - - // Panels - QVBoxLayout* vbox_panel(new QVBoxLayout()); - QHBoxLayout* hbox_expression_input_panel = new QHBoxLayout(); - QHBoxLayout* hbox_button_panel(new QHBoxLayout()); - - // Address expression input - QLineEdit* source_eal(new QLineEdit(m_spu_disasm_dialog)); - QLineEdit* start_pc(new QLineEdit(m_spu_disasm_dialog)); - source_eal->setFont(m_mono); - source_eal->setMaxLength(12); - source_eal->setValidator(new QRegularExpressionValidator(QRegularExpression("^(0[xX])?0*[a-fA-F0-9]{0,8}$"), this)); - start_pc->setFont(m_mono); - start_pc->setMaxLength(7); - start_pc->setValidator(new QRegularExpressionValidator(QRegularExpression("^(0[xX])?0*[a-fA-F0-9]{0,5}$"), this)); - - // Ok/Cancel - QPushButton* button_ok = new QPushButton(tr("OK")); - QPushButton* button_cancel = new QPushButton(tr("Cancel")); - - hbox_expression_input_panel->addWidget(new QLabel(tr("Source Address: "))); - hbox_expression_input_panel->addWidget(source_eal); - hbox_expression_input_panel->addSpacing(10); - hbox_expression_input_panel->addWidget(new QLabel(tr("Load PC: "))); - hbox_expression_input_panel->addWidget(start_pc); - - hbox_button_panel->addWidget(button_ok); - hbox_button_panel->addWidget(button_cancel); - - vbox_panel->addLayout(hbox_expression_input_panel); - vbox_panel->addSpacing(8); - vbox_panel->addLayout(hbox_button_panel); - - m_spu_disasm_dialog->setLayout(vbox_panel); - - const QFont font = source_eal->font(); - - source_eal->setPlaceholderText(QString::fromStdString(fmt::format("0x%08x", 0))); - start_pc->setPlaceholderText(QString::fromStdString(fmt::format("0x%05x", 0))); - - source_eal->setFixedWidth(gui::utils::get_label_width(source_eal->placeholderText(), &font) + 5); - start_pc->setFixedWidth(gui::utils::get_label_width(start_pc->placeholderText(), &font) + 5); - - if (m_spu_disasm_origin_eal) - { - source_eal->setText(QString::fromStdString(fmt::format("0x%08x", m_spu_disasm_origin_eal))); - start_pc->setText(QString::fromStdString(fmt::format("0x%05x", m_spu_disasm_pc))); - } - - connect(button_ok, &QAbstractButton::clicked, m_spu_disasm_dialog, &QDialog::accept); - connect(button_cancel, &QAbstractButton::clicked, m_spu_disasm_dialog, &QDialog::reject); - - m_spu_disasm_dialog->move(QCursor::pos()); - m_spu_disasm_dialog->setAttribute(Qt::WA_DeleteOnClose); - - connect(m_spu_disasm_dialog, &QDialog::finished, this, [this, source_eal, start_pc](int result) - { - m_spu_disasm_dialog = nullptr; - - if (result != QDialog::Accepted) - { - return; - } - const u64 spu_base = EvaluateExpression(start_pc->text()); - - if (spu_base > SPU_LS_SIZE - 4 || spu_base % 4) - { - return; - } - - const u64 spu_addr = EvaluateExpression(source_eal->text()); - - if (spu_addr == umax || !spu_addr) - { - return; - } - - // Try to load as much memory as possible until SPU local memory ends - // Because I don't think there is a need for a size argument - // The user probably does not know the exact size of the SPU code either - u32 spu_size = SPU_LS_SIZE - spu_base; - - for (u32 passed = spu_base; passed < SPU_LS_SIZE; passed += 4096) - { - if (!vm::check_addr(spu_addr + passed)) - { - if (passed == spu_base) - { - return; - } - - spu_size = passed - spu_base - (spu_addr + passed) % 4096; - break; - } - - if (4096 > ~(spu_addr + passed)) - { - // For overflow - spu_size = std::min(SPU_LS_SIZE, 0 - spu_addr); - break; - } - } - - m_disasm.reset(); - m_cpu.reset(); - m_rsx = nullptr; - - m_spu_disasm_memory = std::make_shared(SPU_LS_SIZE); - m_spu_disasm_memory->map_self(); - m_is_spu_disasm_mode = true; - - std::memset(m_spu_disasm_memory->get(), 0, spu_base); - std::memcpy(m_spu_disasm_memory->get() + spu_base, vm::get_super_ptr(spu_addr), spu_size); - std::memset(m_spu_disasm_memory->get() + spu_base + spu_size, 0, SPU_LS_SIZE - (spu_base + spu_size)); - - m_spu_disasm_pc = spu_base; - m_spu_disasm_origin_eal = spu_addr; - - m_disasm = std::make_shared(cpu_disasm_mode::interpreter, m_spu_disasm_memory->get()); - - EnableButtons(true); - - m_debugger_list->UpdateCPUData(m_disasm); - m_breakpoint_list->UpdateCPUData(m_disasm); - ShowPC(true); - DoUpdate(); - UpdateUI(); - }); - - m_spu_disasm_dialog->show(); -} - -void debugger_frame::DoUpdate() -{ - // Check if we need to disable a step over bp - if (const auto cpu0 = get_cpu(); cpu0 && m_last_step_over_breakpoint != umax && cpu0->get_pc() == m_last_step_over_breakpoint) - { - m_ppu_breakpoint_handler->RemoveBreakpoint(m_last_step_over_breakpoint); - m_last_step_over_breakpoint = -1; - } - - WritePanels(); -} - -void debugger_frame::WritePanels() -{ - const auto cpu = get_cpu(); - - if (!cpu) - { - m_misc_state->clear(); - m_regs->clear(); - ClearCallStack(); - return; - } - - int loc = m_misc_state->verticalScrollBar()->value(); - int hloc = m_misc_state->horizontalScrollBar()->value(); - m_misc_state->clear(); - m_misc_state->setPlainText(qstr(cpu->dump_misc())); - m_misc_state->verticalScrollBar()->setValue(loc); - m_misc_state->horizontalScrollBar()->setValue(hloc); - - loc = m_regs->verticalScrollBar()->value(); - hloc = m_regs->horizontalScrollBar()->value(); - m_regs->clear(); - m_last_reg_state.clear(); - cpu->dump_regs(m_last_reg_state, m_dump_reg_func_data); - m_regs->setPlainText(qstr(m_last_reg_state)); - m_regs->verticalScrollBar()->setValue(loc); - m_regs->horizontalScrollBar()->setValue(hloc); - - Q_EMIT CallStackUpdateRequested(cpu->dump_callstack_list()); -} - -void debugger_frame::ShowGotoAddressDialog() -{ - if (m_goto_dialog) - { - m_goto_dialog->move(QCursor::pos()); - m_goto_dialog->show(); - m_goto_dialog->setFocus(); - return; - } - - m_goto_dialog = new QDialog(this); - m_goto_dialog->setWindowTitle(tr("Go To Address")); - - // Panels - QVBoxLayout* vbox_panel(new QVBoxLayout()); - QHBoxLayout* hbox_expression_input_panel = new QHBoxLayout(); - QHBoxLayout* hbox_button_panel(new QHBoxLayout()); - - // Address expression input - QLineEdit* expression_input(new QLineEdit(m_goto_dialog)); - expression_input->setFont(m_mono); - expression_input->setMaxLength(18); - - if (const auto thread = get_cpu(); !thread || thread->get_class() != thread_class::spu) - { - expression_input->setValidator(new QRegularExpressionValidator(QRegularExpression("^(0[xX])?0*[a-fA-F0-9]{0,8}$"), this)); - } - else - { - expression_input->setValidator(new QRegularExpressionValidator(QRegularExpression("^(0[xX])?0*[a-fA-F0-9]{0,5}$"), this)); - } - - // Ok/Cancel - QPushButton* button_ok = new QPushButton(tr("OK")); - QPushButton* button_cancel = new QPushButton(tr("Cancel")); - - hbox_expression_input_panel->addWidget(expression_input); - - hbox_button_panel->addWidget(button_ok); - hbox_button_panel->addWidget(button_cancel); - - vbox_panel->addLayout(hbox_expression_input_panel); - vbox_panel->addSpacing(8); - vbox_panel->addLayout(hbox_button_panel); - - m_goto_dialog->setLayout(vbox_panel); - - const auto cpu_check = make_check_cpu(get_cpu()); - const auto cpu = cpu_check(); - const QFont font = expression_input->font(); - - // -1 from get_pc() turns into 0 - const u32 pc = cpu ? utils::align(cpu->get_pc(), 4) : 0; - expression_input->setPlaceholderText(QString("0x%1").arg(pc, 16, 16, QChar('0'))); - expression_input->setFixedWidth(gui::utils::get_label_width(expression_input->placeholderText(), &font) + 5); - - connect(button_ok, &QAbstractButton::clicked, m_goto_dialog, &QDialog::accept); - connect(button_cancel, &QAbstractButton::clicked, m_goto_dialog, &QDialog::reject); - - m_goto_dialog->move(QCursor::pos()); - m_goto_dialog->setAttribute(Qt::WA_DeleteOnClose); - - connect(m_goto_dialog, &QDialog::finished, this, [this, cpu, cpu_check, expression_input](int result) - { - // Check if the thread has not been destroyed and is still the focused since - // This also works if no thread is selected and has been selected before - if (result == QDialog::Accepted && cpu == get_cpu() && cpu == cpu_check()) - { - PerformGoToRequest(expression_input->text()); - } - - m_goto_dialog = nullptr; - }); - - m_goto_dialog->show(); -} - -void debugger_frame::PerformGoToRequest(const QString& text_argument) -{ - const bool asterisk_prefixed = text_argument.startsWith(QChar('*')); - const u64 address = EvaluateExpression(asterisk_prefixed ? text_argument.sliced(1, text_argument.size() - 1) : text_argument); - - if (address != umax) - { - // Try to read from OPD entry if prefixed by asterisk - if (asterisk_prefixed) - { - if (auto cpu = get_cpu()) - { - if (cpu->try_get()) - { - const vm::ptr func_ptr = vm::cast(static_cast(address)); - - be_t code_addr{}; - - if (func_ptr.try_read(code_addr)) - { - m_debugger_list->ShowAddress(code_addr, true); - } - } - } - - return; - } - - m_debugger_list->ShowAddress(static_cast(address), true); - } -} - -void debugger_frame::PerformGoToThreadRequest(const QString& text_argument) -{ - const u64 thread_id = EvaluateExpression(text_argument); - - if (thread_id != umax) - { - for (usz i = 0; i < m_threads_info.size(); i++) - { - if (cpu_thread* ptr = m_threads_info[i](); ptr && ptr->id == thread_id) - { - // Success - m_choice_units->setCurrentIndex(::narrow(i)); - return; - } - } - } -} - -void debugger_frame::PerformAddBreakpointRequest(u32 addr) -{ - m_debugger_list->BreakpointRequested(addr, true); -} - -u64 debugger_frame::EvaluateExpression(const QString& expression) -{ - bool ok = false; - - // Parse expression (or at least used to, was nuked to remove the need for QtJsEngine) - const QString fixed_expression = QRegularExpression(QRegularExpression::anchoredPattern("a .*|^[A-Fa-f0-9]+$")).match(expression).hasMatch() ? "0x" + expression : expression; - const u64 res = static_cast(fixed_expression.toULong(&ok, 16)); - - if (ok) - return res; - if (const auto thread = get_cpu(); thread && expression.isEmpty()) - return thread->get_pc(); - return umax; -} - -void debugger_frame::ClearBreakpoints() const -{ - m_breakpoint_list->ClearBreakpoints(); -} - -void debugger_frame::ClearCallStack() -{ - Q_EMIT CallStackUpdateRequested({}); -} - -void debugger_frame::ShowPC(bool user_requested) -{ - const auto cpu0 = get_cpu(); - - const u32 pc = (cpu0 ? cpu0->get_pc() : (m_is_spu_disasm_mode ? m_spu_disasm_pc : 0)); - - if (user_requested) - { - m_debugger_list->EnableThreadFollowing(); - } - - m_debugger_list->ShowAddress(pc, false); -} - -void debugger_frame::DoStep(bool step_over) -{ - if (const auto cpu = get_cpu()) - { - bool should_step_over = step_over && cpu->get_class() == thread_class::ppu; - - // If stepping over, lay at the same spot and wait for the thread to finish the call - // If not, fixate on the current pointed instruction - m_debugger_list->EnableThreadFollowing(!should_step_over); - - if (should_step_over) - { - m_debugger_list->ShowAddress(cpu->get_pc() + 4, false); - } - - if (step_over && cpu->get_class() == thread_class::rsx) - { - const bool was_paused = cpu->is_paused(); - static_cast(cpu)->pause_on_draw = true; - - if (was_paused) - { - RunBtnPress(); - m_debugger_list->EnableThreadFollowing(true); - } - - return; - } - - if (const auto _state = +cpu->state; _state & s_pause_flags && _state & cpu_flag::wait && !(_state & cpu_flag::dbg_step)) - { - if (should_step_over && cpu->get_class() == thread_class::ppu) - { - const u32 current_instruction_pc = cpu->get_pc(); - - vm::ptr inst_ptr = vm::cast(current_instruction_pc); - be_t result{}; - - if (inst_ptr.try_read(result)) - { - ppu_opcode_t ppu_op{result}; - const ppu_itype::type itype = g_ppu_itype.decode(ppu_op.opcode); - - should_step_over = (itype & ppu_itype::branch && ppu_op.lk); - } - } - - if (should_step_over) - { - const u32 current_instruction_pc = cpu->get_pc(); - - // Set breakpoint on next instruction - const u32 next_instruction_pc = current_instruction_pc + 4; - m_ppu_breakpoint_handler->AddBreakpoint(next_instruction_pc, breakpoint_types::bp_exec); - - // Undefine previous step over breakpoint if it hasn't been already - // This can happen when the user steps over a branch that doesn't return to itself - if (m_last_step_over_breakpoint != umax) - { - m_ppu_breakpoint_handler->RemoveBreakpoint(next_instruction_pc); - } - - m_last_step_over_breakpoint = next_instruction_pc; - } - - cpu->state.atomic_op([&](bs_t& state) - { - state -= s_pause_flags; - - if (!should_step_over) - { - if (u32* ptr = cpu->get_pc2()) - { - state += cpu_flag::dbg_step; - *ptr = cpu->get_pc(); - } - } - }); - - cpu->state.notify_one(); - } - } - - // Tighten up, put the debugger on a wary watch over any thread info changes if there aren't any - // This allows responsive debugger interaction - m_ui_fast_update_permission_deadline = m_ui_update_ctr + 5; -} - -void debugger_frame::EnableUpdateTimer(bool enable) const -{ - if (m_update->isActive() == enable) - { - return; - } - - enable ? m_update->start(10) : m_update->stop(); -} - -void debugger_frame::RunBtnPress() -{ - if (const auto cpu = get_cpu()) - { - // If paused, unpause. - // If not paused, add dbg_pause. - const auto old = cpu->state.fetch_op([](bs_t& state) - { - if (state & s_pause_flags) - { - state -= s_pause_flags; - } - else - { - state += cpu_flag::dbg_pause; - } - }); - - // Notify only if no pause flags are set after this change - if (old & s_pause_flags) - { - if (g_debugger_pause_all_threads_on_bp && Emu.IsPaused() && (old & s_pause_flags) == s_pause_flags) - { - // Resume all threads were paused by this breakpoint - Emu.Resume(); - } - - cpu->state.notify_one(); - m_debugger_list->EnableThreadFollowing(); - } - } - - // Tighten up, put the debugger on a wary watch over any thread info changes if there aren't any - // This allows responsive debugger interaction - m_ui_fast_update_permission_deadline = m_ui_update_ctr + 5; -} - -void debugger_frame::EnableButtons(bool enable) -{ - const auto cpu = get_cpu(); - - if (!cpu) - enable = false; - - const bool step = enable && is_using_interpreter(cpu->get_class()); - - m_go_to_addr->setEnabled(enable); - m_go_to_pc->setEnabled(enable); - m_btn_add_bp->setEnabled(enable); - m_btn_step->setEnabled(step); - m_btn_step_over->setEnabled(step); - m_btn_run->setEnabled(enable); -} diff --git a/rpcs3qt-legacy/debugger_frame.h b/rpcs3qt-legacy/debugger_frame.h deleted file mode 100644 index 96a3ece2f..000000000 --- a/rpcs3qt-legacy/debugger_frame.h +++ /dev/null @@ -1,146 +0,0 @@ -#pragma once - -#include "util/types.hpp" -#include "util/shared_ptr.hpp" - -#include "custom_dock_widget.h" - -#include -#include -#include -#include - -#include -#include -#include -#include - -class CPUDisAsm; -class cpu_thread; -class gui_settings; -class debugger_list; -class breakpoint_list; -class breakpoint_handler; -class call_stack_list; - -namespace rsx -{ - class thread; -} - -namespace utils -{ - class shm; -} - -enum class system_state : u32; - -class instruction_editor_dialog; -class register_editor_dialog; - -class debugger_frame : public custom_dock_widget -{ - Q_OBJECT - - const QString NoThreadString = tr("No Thread"); - const QString RunString = tr("Run"); - const QString PauseString = tr("Pause"); - - debugger_list* m_debugger_list = nullptr; - QSplitter* m_right_splitter = nullptr; - QFont m_mono; - QPlainTextEdit* m_misc_state = nullptr; - QPlainTextEdit* m_regs = nullptr; - QPushButton* m_go_to_addr = nullptr; - QPushButton* m_go_to_pc = nullptr; - QPushButton* m_btn_step = nullptr; - QPushButton* m_btn_step_over = nullptr; - QPushButton* m_btn_add_bp = nullptr; - QPushButton* m_btn_run = nullptr; - - QComboBox* m_choice_units = nullptr; - QTimer* m_update = nullptr; - QSplitter* m_splitter = nullptr; - - u64 m_threads_created = -1; - u64 m_threads_deleted = -1; - system_state m_emu_state{}; - u64 m_emulation_id{}; - u32 m_last_pc = -1; - std::vector m_last_query_state; - std::string m_last_reg_state; - std::any m_dump_reg_func_data; - std::vector> m_threads_info; - u32 m_last_step_over_breakpoint = -1; - u64 m_ui_update_ctr = 0; - u64 m_ui_fast_update_permission_deadline = 0; - bool m_thread_list_pending_update = false; - - std::shared_ptr m_disasm; // Only shared to allow base/derived functionality - shared_ptr m_cpu; - rsx::thread* m_rsx = nullptr; - std::shared_ptr m_spu_disasm_memory; - u32 m_spu_disasm_origin_eal = 0; - u32 m_spu_disasm_pc = 0; - bool m_is_spu_disasm_mode = false; - - breakpoint_list* m_breakpoint_list = nullptr; - breakpoint_handler* m_ppu_breakpoint_handler = nullptr; - call_stack_list* m_call_stack_list = nullptr; - instruction_editor_dialog* m_inst_editor = nullptr; - register_editor_dialog* m_reg_editor = nullptr; - QDialog* m_goto_dialog = nullptr; - QDialog* m_spu_disasm_dialog = nullptr; - - std::shared_ptr m_gui_settings; - - cpu_thread* get_cpu(); - std::function make_check_cpu(cpu_thread* cpu, bool unlocked = false); - void open_breakpoints_settings(); - -public: - explicit debugger_frame(std::shared_ptr settings, QWidget* parent = nullptr); - - void SaveSettings() const; - void ChangeColors() const; - - void UpdateUI(); - void UpdateUnitList(); - - void DoUpdate(); - void WritePanels(); - void EnableButtons(bool enable); - void ShowGotoAddressDialog(); - void PerformGoToRequest(const QString& text_argument); - void PerformGoToThreadRequest(const QString& text_argument); - void PerformAddBreakpointRequest(u32 addr); - u64 EvaluateExpression(const QString& expression); - void ClearBreakpoints() const; // Fallthrough method into breakpoint_list. - void ClearCallStack(); - - /** Needed so key press events work when other objects are selected in debugger_frame. */ - bool eventFilter(QObject* object, QEvent* event) override; - -protected: - /** Override inherited method from Qt to allow signalling when close happened.*/ - void closeEvent(QCloseEvent* event) override; - void showEvent(QShowEvent* event) override; - void hideEvent(QHideEvent* event) override; - void keyPressEvent(QKeyEvent* event) override; - -Q_SIGNALS: - void DebugFrameClosed(); - void CallStackUpdateRequested(const std::vector>& call_stack); - -public Q_SLOTS: - void DoStep(bool step_over = false); - -private Q_SLOTS: - void OnSelectUnit(); - void OnSelectSPUDisassembler(); - void ShowPC(bool user_requested = false); - void EnableUpdateTimer(bool enable) const; - void RunBtnPress(); -}; - -Q_DECLARE_METATYPE(u32) diff --git a/rpcs3qt-legacy/debugger_list.cpp b/rpcs3qt-legacy/debugger_list.cpp deleted file mode 100644 index 5012d27a4..000000000 --- a/rpcs3qt-legacy/debugger_list.cpp +++ /dev/null @@ -1,497 +0,0 @@ -#include "debugger_list.h" -#include "gui_settings.h" -#include "qt_utils.h" -#include "breakpoint_handler.h" - -#include "Emu/Cell/SPUThread.h" -#include "Emu/CPU/CPUDisAsm.h" -#include "Emu/CPU/CPUThread.h" -#include "Emu/RSX/RSXDisAsm.h" -#include "Emu/RSX/RSXThread.h" -#include "Emu/System.h" - -#include "util/asm.hpp" - -#include -#include -#include -#include - -#include - -constexpr auto qstr = QString::fromStdString; - -debugger_list::debugger_list(QWidget* parent, std::shared_ptr gui_settings, breakpoint_handler* handler) - : QListWidget(parent), m_gui_settings(std::move(gui_settings)), m_ppu_breakpoint_handler(handler) -{ - setWindowTitle(tr("ASM")); - - for (uint i = 0; i < m_item_count; ++i) - { - insertItem(i, new QListWidgetItem("")); - } - - setSizeAdjustPolicy(QListWidget::AdjustToContents); - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - - connect(this, &QListWidget::currentRowChanged, this, [this](int row) - { - if (row < 0) - { - m_selected_instruction = -1; - m_showing_selected_instruction = false; - return; - } - - u32 pc = m_start_addr; - - const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr; - - for (; cpu && cpu->get_class() == thread_class::rsx && row; row--) - { - // If scrolling forwards (downwards), we can skip entire commands - pc += std::max(m_disasm->disasm(pc), 4); - } - - m_selected_instruction = pc + row * 4; - }); -} - -void debugger_list::UpdateCPUData(std::shared_ptr disasm) -{ - if ((!m_disasm) != (!disasm) || (m_disasm && disasm->get_cpu() != m_disasm->get_cpu())) - { - m_selected_instruction = -1; - m_showing_selected_instruction = false; - } - - m_disasm = std::move(disasm); -} - -u32 debugger_list::GetStartAddress(u32 address) -{ - const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr; - - const u32 steps = m_item_count / 3; - const u32 inst_count_jump_on_step = std::min(steps, 4); - - const bool is_spu = IsSpu(); - const u32 address_mask = (is_spu ? 0x3fffc : ~3); - - u32 result = address & address_mask; - - if (cpu && cpu->get_class() == thread_class::rsx) - { - if (auto [count, res] = static_cast(cpu)->try_get_pc_of_x_cmds_backwards(steps, address); count == steps) - { - result = res; - } - } - else - { - result = (address - (steps * 4)) & address_mask; - } - - u32 upper_bound = (m_start_addr + (steps * 4)) & address_mask; - - if (cpu && cpu->get_class() == thread_class::rsx) - { - if (auto [count, res] = static_cast(cpu)->try_get_pc_of_x_cmds_backwards(0 - steps, m_start_addr); count == steps) - { - upper_bound = res; - } - } - - bool goto_addr = false; - - if (upper_bound > m_start_addr) - { - goto_addr = address < m_start_addr || address >= upper_bound; - } - else - { - // Overflowing bounds case - goto_addr = address < m_start_addr && address >= upper_bound; - } - - if (goto_addr) - { - m_pc = address; - - if (address > upper_bound && address - upper_bound < inst_count_jump_on_step * 4) - { - m_start_addr = result + inst_count_jump_on_step * 4; - } - else - { - m_start_addr = result; - } - } - - return m_start_addr; -} - -bool debugger_list::IsSpu() const -{ - const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr; - - return (cpu && cpu->get_class() == thread_class::spu) || (m_disasm && !cpu); -} - -void debugger_list::ShowAddress(u32 addr, bool select_addr, bool direct) -{ - const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr; - - const decltype(spu_thread::local_breakpoints)* spu_bps_list{}; - - if (cpu && cpu->get_class() == thread_class::spu) - { - spu_bps_list = &static_cast(cpu)->local_breakpoints; - } - - auto IsBreakpoint = [&](u32 pc) - { - switch (cpu ? cpu->get_class() : thread_class::general) - { - case thread_class::ppu: - { - return m_ppu_breakpoint_handler->HasBreakpoint(pc, breakpoint_types::bp_exec); - } - case thread_class::spu: - { - const u32 pos_at = pc / 4; - const u32 pos_bit = 1u << (pos_at % 8); - - return !!((*spu_bps_list)[pos_at / 8] & pos_bit); - } - default: return false; - } - }; - - if (select_addr || direct) - { - // The user wants to survey a specific memory location, do not interfere from this point forth - m_follow_thread = false; - } - - m_dirty_flag = false; - - u32 pc = m_start_addr; - - if (!direct && (m_follow_thread || select_addr)) - { - pc = GetStartAddress(addr); - } - - const auto& default_foreground = palette().color(foregroundRole()); - const auto& default_background = palette().color(backgroundRole()); - - m_showing_selected_instruction = false; - - if (select_addr) - { - m_selected_instruction = addr; - } - - for (uint i = 0; i < m_item_count; ++i) - { - if (auto list_item = item(i); list_item->isSelected()) - { - list_item->setSelected(false); - } - } - - if (!m_disasm || (cpu && cpu->state.all_of(cpu_flag::exit + cpu_flag::wait))) - { - for (uint i = 0; i < m_item_count; ++i) - { - QListWidgetItem* list_item = item(i); - list_item->setText(qstr(fmt::format(" [%08x] ?? ?? ?? ??:", 0))); - list_item->setForeground(default_foreground); - list_item->setBackground(default_background); - } - } - else - { - const bool is_spu = IsSpu(); - const u32 address_limits = (is_spu ? 0x3fffc : ~3); - const u32 current_pc = (cpu ? cpu->get_pc() : 0); - m_start_addr &= address_limits; - pc = m_start_addr; - - for (uint i = 0, count = 4; i < m_item_count; ++i, pc = (pc + count) & address_limits) - { - QListWidgetItem* list_item = item(i); - - if (pc == current_pc) - { - list_item->setForeground(m_text_color_pc); - list_item->setBackground(m_color_pc); - } - else if (pc == m_selected_instruction) - { - // setSelected may invoke a resize event which causes stack overflow, terminate recursion - if (!list_item->isSelected()) - { - list_item->setSelected(true); - } - - m_showing_selected_instruction = true; - } - else if (IsBreakpoint(pc)) - { - list_item->setForeground(m_text_color_bp); - list_item->setBackground(m_color_bp); - } - else - { - list_item->setForeground(default_foreground); - list_item->setBackground(default_background); - } - - if (cpu && cpu->get_class() == thread_class::ppu && !vm::check_addr(pc, 0)) - { - list_item->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(fmt::format("[%08x] ?? ?? ?? ??:", pc))); - count = 4; - continue; - } - - if (cpu && cpu->get_class() == thread_class::ppu && !vm::check_addr(pc, vm::page_executable)) - { - const u32 data = *vm::get_super_ptr>(pc); - list_item->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(fmt::format("[%08x] %02x %02x %02x %02x:", pc, - static_cast(data >> 24), - static_cast(data >> 16), - static_cast(data >> 8), - static_cast(data >> 0)))); - count = 4; - continue; - } - - count = m_disasm->disasm(pc); - - if (!count) - { - list_item->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(fmt::format("[%08x] ??? ?? ??", pc))); - count = 4; - continue; - } - - list_item->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(m_disasm->last_opcode)); - } - } - - setLineWidth(-1); -} - -void debugger_list::RefreshView() -{ - const bool old = std::exchange(m_follow_thread, false); - ShowAddress(0, false); - m_follow_thread = old; -} - -void debugger_list::EnableThreadFollowing(bool enable) -{ - m_follow_thread = enable; -} - -void debugger_list::scroll(s32 steps) -{ - const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr; - - for (; cpu && cpu->get_class() == thread_class::rsx && steps > 0; steps--) - { - // If scrolling forwards (downwards), we can skip entire commands - m_start_addr += std::max(m_disasm->disasm(m_start_addr), 4); - } - - if (cpu && cpu->get_class() == thread_class::rsx && steps < 0) - { - // If scrolling backwards (upwards), try to obtain the start of commands tail - if (auto [count, res] = static_cast(cpu)->try_get_pc_of_x_cmds_backwards(-steps, m_start_addr); count == 0u - steps) - { - steps = 0; - m_start_addr = res; - } - } - - EnableThreadFollowing(false); - m_start_addr += steps * 4; - ShowAddress(0, false, true); -} - -void debugger_list::keyPressEvent(QKeyEvent* event) -{ - const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr; - - // Always accept event (so it would not bubble upwards, debugger_frame already sees it) - struct accept_event_t - { - QKeyEvent* event; - - ~accept_event_t() noexcept - { - event->accept(); - } - } accept_event{event}; - - if (!isActiveWindow()) - { - QListWidget::keyPressEvent(event); - return; - } - - if (event->modifiers()) - { - QListWidget::keyPressEvent(event); - return; - } - - switch (event->key()) - { - case Qt::Key_PageUp: scroll(0 - m_item_count); return; - case Qt::Key_PageDown: scroll(m_item_count); return; - case Qt::Key_Up: scroll(-1); return; - case Qt::Key_Down: scroll(1); return; - case Qt::Key_I: - { - if (event->isAutoRepeat()) - { - QListWidget::keyPressEvent(event); - return; - } - - if (cpu && cpu->get_class() == thread_class::rsx) - { - create_rsx_command_detail(m_showing_selected_instruction ? m_selected_instruction : m_pc); - return; - } - - break; - } - default: break; - } - - QListWidget::keyPressEvent(event); -} - -void debugger_list::showEvent(QShowEvent* event) -{ - if (m_cmd_detail) - m_cmd_detail->show(); - QListWidget::showEvent(event); -} - -void debugger_list::hideEvent(QHideEvent* event) -{ - if (m_cmd_detail) - m_cmd_detail->hide(); - QListWidget::hideEvent(event); -} - -void debugger_list::create_rsx_command_detail(u32 pc) -{ - RSXDisAsm rsx_dis = static_cast(*m_disasm); - rsx_dis.change_mode(cpu_disasm_mode::list); - - // Either invalid or not a method - if (rsx_dis.disasm(pc) <= 4) - return; - - if (m_cmd_detail) - { - // Edit the existing dialog - m_detail_label->setText(QString::fromStdString(rsx_dis.last_opcode)); - m_cmd_detail->setFixedSize(m_cmd_detail->sizeHint()); - return; - } - - m_cmd_detail = new QDialog(this); - m_cmd_detail->setWindowTitle(tr("RSX Command Detail")); - - m_detail_label = new QLabel(QString::fromStdString(rsx_dis.last_opcode), this); - m_detail_label->setFont(font()); - gui::utils::set_font_size(*m_detail_label, 10); - m_detail_label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); - - QVBoxLayout* layout = new QVBoxLayout(this); - layout->addWidget(m_detail_label); - m_cmd_detail->setLayout(layout); - m_cmd_detail->setFixedSize(m_cmd_detail->sizeHint()); - m_cmd_detail->show(); - - connect(m_cmd_detail, &QDialog::finished, [this](int) - { - // Cleanup - std::exchange(m_cmd_detail, nullptr)->deleteLater(); - }); -} - -void debugger_list::mouseDoubleClickEvent(QMouseEvent* event) -{ - if (event->button() == Qt::LeftButton) - { - int i = currentRow(); - if (i < 0) - return; - - u32 pc = m_start_addr; - - const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr; - - for (; cpu && cpu->get_class() == thread_class::rsx && i; i--) - { - // If scrolling forwards (downwards), we can skip entire commands - pc += std::max(m_disasm->disasm(pc), 4); - } - - pc += i * 4; - m_selected_instruction = pc; - - // Let debugger_frame know about breakpoint. - // Other option is to add to breakpoint manager directly and have a signal there instead. - // Either the flow goes from debugger_list->breakpoint_manager->debugger_frame, or it goes debugger_list->debugger_frame, and I felt this was easier to read for now. - Q_EMIT BreakpointRequested(pc); - } -} - -void debugger_list::wheelEvent(QWheelEvent* event) -{ - const QPoint numSteps = event->angleDelta() / 8 / 15; // http://doc.qt.io/qt-5/qwheelevent.html#pixelDelta - const int value = numSteps.y(); - const auto direction = (event->modifiers() == Qt::ControlModifier); - - scroll(direction ? value : -value); -} - -void debugger_list::resizeEvent(QResizeEvent* event) -{ - QListWidget::resizeEvent(event); - - if (count() < 1 || visualItemRect(item(0)).height() < 1) - { - return; - } - - const u32 old_size = m_item_count; - - // It is fine if the QWidgetList is a tad bit larger than the frame - m_item_count = utils::aligned_div(rect().height() - frameWidth() * 2, visualItemRect(item(0)).height()); - - if (old_size <= m_item_count) - { - for (u32 i = old_size; i < m_item_count; ++i) - { - insertItem(i, new QListWidgetItem("")); - m_dirty_flag = true; - } - } - else - { - for (u32 i = old_size - 1; i >= m_item_count; --i) - { - delete takeItem(i); - } - } -} diff --git a/rpcs3qt-legacy/debugger_list.h b/rpcs3qt-legacy/debugger_list.h deleted file mode 100644 index d3589e013..000000000 --- a/rpcs3qt-legacy/debugger_list.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include "util/types.hpp" - -#include - -#include - -class breakpoint_handler; -class CPUDisAsm; -class cpu_thread; -class gui_settings; -class QLabel; - -class debugger_list : public QListWidget -{ - Q_OBJECT - -public: - u32 m_pc = 0; - u32 m_start_addr = 0; - u32 m_item_count = 30; - u32 m_selected_instruction = -1; - bool m_follow_thread = true; // If true, follow the selected thread to wherever it goes in code - bool m_showing_selected_instruction = false; - bool m_dirty_flag = false; - QColor m_color_bp; - QColor m_color_pc; - QColor m_text_color_bp; - QColor m_text_color_pc; - -Q_SIGNALS: - void BreakpointRequested(u32 loc, bool only_add = false); - -public: - debugger_list(QWidget* parent, std::shared_ptr settings, breakpoint_handler* handler); - void UpdateCPUData(std::shared_ptr disasm); - void EnableThreadFollowing(bool enable = true); -public Q_SLOTS: - void ShowAddress(u32 addr, bool select_addr = true, bool direct = false); - void RefreshView(); - -protected: - void keyPressEvent(QKeyEvent* event) override; - void mouseDoubleClickEvent(QMouseEvent* event) override; - void wheelEvent(QWheelEvent* event) override; - void resizeEvent(QResizeEvent* event) override; - void showEvent(QShowEvent* event) override; - void hideEvent(QHideEvent* event) override; - void scroll(s32 steps); - void create_rsx_command_detail(u32 pc); - -private: - /** - * It really upsetted me I had to copy this code to make debugger_list/frame not circularly dependent. - */ - u32 GetStartAddress(u32 address); - bool IsSpu() const; - - std::shared_ptr m_gui_settings; - - breakpoint_handler* m_ppu_breakpoint_handler = nullptr; - cpu_thread* m_cpu = nullptr; - std::shared_ptr m_disasm; - QDialog* m_cmd_detail = nullptr; - QLabel* m_detail_label = nullptr; -}; diff --git a/rpcs3qt-legacy/dimensions_dialog.cpp b/rpcs3qt-legacy/dimensions_dialog.cpp deleted file mode 100644 index f59483988..000000000 --- a/rpcs3qt-legacy/dimensions_dialog.cpp +++ /dev/null @@ -1,779 +0,0 @@ -#include "stdafx.h" -#include "util/File.h" -#include "dimensions_dialog.h" -#include "Emu/Io/Dimensions.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -dimensions_dialog* dimensions_dialog::inst = nullptr; -std::array, 7> figure_slots = {}; -static QString s_last_figure_path; - -LOG_CHANNEL(dimensions_log, "dimensions"); - -const std::map list_minifigs = { - {0, "Blank Tag"}, - {1, "Batman"}, - {2, "Gandalf"}, - {3, "Wyldstyle"}, - {4, "Aquaman"}, - {5, "Bad Cop"}, - {6, "Bane"}, - {7, "Bart Simpson"}, - {8, "Benny"}, - {9, "Chell"}, - {10, "Cole"}, - {11, "Cragger"}, - {12, "Cyborg"}, - {13, "Cyberman"}, - {14, "Doc Brown"}, - {15, "The Doctor"}, - {16, "Emmet"}, - {17, "Eris"}, - {18, "Gimli"}, - {19, "Gollum"}, - {20, "Harley Quinn"}, - {21, "Homer Simpson"}, - {22, "Jay"}, - {23, "Joker"}, - {24, "Kai"}, - {25, "ACU Trooper"}, - {26, "Gamer Kid"}, - {27, "Krusty the Clown"}, - {28, "Laval"}, - {29, "Legolas"}, - {30, "Lloyd"}, - {31, "Marty McFly"}, - {32, "Nya"}, - {33, "Owen Grady"}, - {34, "Peter Venkman"}, - {35, "Slimer"}, - {36, "Scooby-Doo"}, - {37, "Sensei Wu"}, - {38, "Shaggy"}, - {39, "Stay Puft"}, - {40, "Superman"}, - {41, "Unikitty"}, - {42, "Wicked Witch of the West"}, - {43, "Wonder Woman"}, - {44, "Zane"}, - {45, "Green Arrow"}, - {46, "Supergirl"}, - {47, "Abby Yates"}, - {48, "Finn the Human"}, - {49, "Ethan Hunt"}, - {50, "Lumpy Space Princess"}, - {51, "Jake the Dog"}, - {52, "Harry Potter"}, - {53, "Lord Voldemort"}, - {54, "Michael Knight"}, - {55, "B.A. Baracus"}, - {56, "Newt Scamander"}, - {57, "Sonic the Hedgehog"}, - {58, "Future Update (unreleased)"}, - {59, "Gizmo"}, - {60, "Stripe"}, - {61, "E.T."}, - {62, "Tina Goldstein"}, - {63, "Marceline the Vampire Queen"}, - {64, "Batgirl"}, - {65, "Robin"}, - {66, "Sloth"}, - {67, "Hermione Granger"}, - {68, "Chase McCain"}, - {69, "Excalibur Batman"}, - {70, "Raven"}, - {71, "Beast Boy"}, - {72, "Betelgeuse"}, - {73, "Lord Vortech (unreleased)"}, - {74, "Blossom"}, - {75, "Bubbles"}, - {76, "Buttercup"}, - {77, "Starfire"}, - {78, "World 15 (unreleased)"}, - {79, "World 16 (unreleased)"}, - {80, "World 17 (unreleased)"}, - {81, "World 18 (unreleased)"}, - {82, "World 19 (unreleased)"}, - {83, "World 20 (unreleased)"}, - {768, "Unknown 768"}, - {769, "Supergirl Red Lantern"}, - {770, "Unknown 770"}}; - -const std::map list_tokens = { - {1000, "Police Car"}, - {1001, "Aerial Squad Car"}, - {1002, "Missile Striker"}, - {1003, "Gravity Sprinter"}, - {1004, "Street Shredder"}, - {1005, "Sky Clobberer"}, - {1006, "Batmobile"}, - {1007, "Batblaster"}, - {1008, "Sonic Batray"}, - {1009, "Benny's Spaceship"}, - {1010, "Lasercraft"}, - {1011, "The Annihilator"}, - {1012, "DeLorean Time Machine"}, - {1013, "Electric Time Machine"}, - {1014, "Ultra Time Machine"}, - {1015, "Hoverboard"}, - {1016, "Cyclone Board"}, - {1017, "Ultimate Hoverjet"}, - {1018, "Eagle Interceptor"}, - {1019, "Eagle Sky Blazer"}, - {1020, "Eagle Swoop Diver"}, - {1021, "Swamp Skimmer"}, - {1022, "Cragger's Fireship"}, - {1023, "Croc Command Sub"}, - {1024, "Cyber-Guard"}, - {1025, "Cyber-Wrecker"}, - {1026, "Laser Robot Walker"}, - {1027, "K-9"}, - {1028, "K-9 Ruff Rover"}, - {1029, "K-9 Laser Cutter"}, - {1030, "TARDIS"}, - {1031, "Laser-Pulse TARDIS"}, - {1032, "Energy-Burst TARDIS"}, - {1033, "Emmet's Excavator"}, - {1034, "Destroy Dozer"}, - {1035, "Destruct-o-Mech"}, - {1036, "Winged Monkey"}, - {1037, "Battle Monkey"}, - {1038, "Commander Monkey"}, - {1039, "Axe Chariot"}, - {1040, "Axe Hurler"}, - {1041, "Soaring Chariot"}, - {1042, "Shelob the Great"}, - {1043, "8-Legged Stalker"}, - {1044, "Poison Slinger"}, - {1045, "Homer's Car"}, - {1047, "The SubmaHomer"}, - {1046, "The Homercraft"}, - {1048, "Taunt-o-Vision"}, - {1050, "The MechaHomer"}, - {1049, "Blast Cam"}, - {1051, "Velociraptor"}, - {1053, "Venom Raptor"}, - {1052, "Spike Attack Raptor"}, - {1054, "Gyrosphere"}, - {1055, "Sonic Beam Gyrosphere"}, - {1056, " Gyrosphere"}, - {1057, "Clown Bike"}, - {1058, "Cannon Bike"}, - {1059, "Anti-Gravity Rocket Bike"}, - {1060, "Mighty Lion Rider"}, - {1061, "Lion Blazer"}, - {1062, "Fire Lion"}, - {1063, "Arrow Launcher"}, - {1064, "Seeking Shooter"}, - {1065, "Triple Ballista"}, - {1066, "Mystery Machine"}, - {1067, "Mystery Tow & Go"}, - {1068, "Mystery Monster"}, - {1069, "Boulder Bomber"}, - {1070, "Boulder Blaster"}, - {1071, "Cyclone Jet"}, - {1072, "Storm Fighter"}, - {1073, "Lightning Jet"}, - {1074, "Electro-Shooter"}, - {1075, "Blade Bike"}, - {1076, "Flight Fire Bike"}, - {1077, "Blades of Fire"}, - {1078, "Samurai Mech"}, - {1079, "Samurai Shooter"}, - {1080, "Soaring Samurai Mech"}, - {1081, "Companion Cube"}, - {1082, "Laser Deflector"}, - {1083, "Gold Heart Emitter"}, - {1084, "Sentry Turret"}, - {1085, "Turret Striker"}, - {1086, "Flight Turret Carrier"}, - {1087, "Scooby Snack"}, - {1088, "Scooby Fire Snack"}, - {1089, "Scooby Ghost Snack"}, - {1090, "Cloud Cuckoo Car"}, - {1091, "X-Stream Soaker"}, - {1092, "Rainbow Cannon"}, - {1093, "Invisible Jet"}, - {1094, "Laser Shooter"}, - {1095, "Torpedo Bomber"}, - {1096, "NinjaCopter"}, - {1097, "Glaciator"}, - {1098, "Freeze Fighter"}, - {1099, "Travelling Time Train"}, - {1100, "Flight Time Train"}, - {1101, "Missile Blast Time Train"}, - {1102, "Aqua Watercraft"}, - {1103, "Seven Seas Speeder"}, - {1104, "Trident of Fire"}, - {1105, "Drill Driver"}, - {1106, "Bane Dig 'n' Drill"}, - {1107, "Bane Drill 'n' Blast"}, - {1108, "Quinn Mobile"}, - {1109, "Quinn Ultra Racer"}, - {1110, "Missile Launcher"}, - {1111, "The Joker's Chopper"}, - {1112, "Mischievous Missile Blaster"}, - {1113, "Lock 'n' Laser Jet"}, - {1114, "Hover Pod"}, - {1115, "Krypton Striker"}, - {1116, "Super Stealth Pod"}, - {1117, "Dalek"}, - {1118, "Fire 'n' Ride Dalek"}, - {1119, "Silver Shooter Dalek"}, - {1120, "Ecto-1"}, - {1121, "Ecto-1 Blaster"}, - {1122, "Ecto-1 Water Diver"}, - {1123, "Ghost Trap"}, - {1124, "Ghost Stun 'n' Trap"}, - {1125, "Proton Zapper"}, - {1126, "Unknown"}, - {1127, "Unknown"}, - {1128, "Unknown"}, - {1129, "Unknown"}, - {1130, "Unknown"}, - {1131, "Unknown"}, - {1132, "Lloyd's Golden Dragon"}, - {1133, "Sword Projector Dragon"}, - {1134, "Unknown"}, - {1135, "Unknown"}, - {1136, "Unknown"}, - {1137, "Unknown"}, - {1138, "Unknown"}, - {1139, "Unknown"}, - {1140, "Unknown"}, - {1141, "Unknown"}, - {1142, "Unknown"}, - {1143, "Unknown"}, - {1144, "Mega Flight Dragon"}, - {1145, "Unknown"}, - {1146, "Unknown"}, - {1147, "Unknown"}, - {1148, "Unknown"}, - {1149, "Unknown"}, - {1150, "Unknown"}, - {1151, "Unknown"}, - {1152, "Unknown"}, - {1153, "Unknown"}, - {1154, "Unknown"}, - {1155, "Flying White Dragon"}, - {1156, "Golden Fire Dragon"}, - {1157, "Ultra Destruction Dragon"}, - {1158, "Arcade Machine"}, - {1159, "8-Bit Shooter"}, - {1160, "The Pixelator"}, - {1161, "G-6155 Spy Hunter"}, - {1162, "Interdiver"}, - {1163, "Aerial Spyhunter"}, - {1164, "Slime Shooter"}, - {1165, "Slime Exploder"}, - {1166, "Slime Streamer"}, - {1167, "Terror Dog"}, - {1168, "Terror Dog Destroyer"}, - {1169, "Soaring Terror Dog"}, - {1170, "Ancient Psychic Tandem War Elephant"}, - {1171, "Cosmic Squid"}, - {1172, "Psychic Submarine"}, - {1173, "BMO"}, - {1174, "DOGMO"}, - {1175, "SNAKEMO"}, - {1176, "Jakemobile"}, - {1177, "Snail Dude Jake"}, - {1178, "Hover Jake"}, - {1179, "Lumpy Car"}, - {1181, "Lumpy Land Whale"}, - {1180, "Lumpy Truck"}, - {1182, "Lunatic Amp"}, - {1183, "Shadow Scorpion"}, - {1184, "Heavy Metal Monster"}, - {1185, "B.A.'s Van"}, - {1186, "Fool Smasher"}, - {1187, "Pain Plane"}, - {1188, "Phone Home"}, - {1189, "Mobile Uplink"}, - {1190, "Super-Charged Satellite"}, - {1191, "Niffler"}, - {1192, "Sinister Scorpion"}, - {1193, "Vicious Vulture"}, - {1194, "Swooping Evil"}, - {1195, "Brutal Bloom"}, - {1196, "Crawling Creeper"}, - {1197, "Ecto-1 (2016)"}, - {1198, "Ectozer"}, - {1199, "PerfEcto"}, - {1200, "Flash 'n' Finish"}, - {1201, "Rampage Record Player"}, - {1202, "Stripe's Throne"}, - {1203, "R.C. Racer"}, - {1204, "Gadget-O-Matic"}, - {1205, "Scarlet Scorpion"}, - {1206, "Hogwarts Express"}, - {1208, "Steam Warrior"}, - {1207, "Soaring Steam Plane"}, - {1209, "Enchanted Car"}, - {1210, "Shark Sub"}, - {1211, "Monstrous Mouth"}, - {1212, "IMF Scrambler"}, - {1213, "Shock Cycle"}, - {1214, "IMF Covert Jet"}, - {1215, "IMF Sports Car"}, - {1216, "IMF Tank"}, - {1217, "IMF Splorer"}, - {1218, "Sonic Speedster"}, - {1219, "Blue Typhoon"}, - {1220, "Moto Bug"}, - {1221, "The Tornado"}, - {1222, "Crabmeat"}, - {1223, "Eggcatcher"}, - {1224, "K.I.T.T."}, - {1225, "Goliath Armored Semi"}, - {1226, "K.I.T.T. Jet"}, - {1227, "Police Helicopter"}, - {1228, "Police Hovercraft"}, - {1229, "Police Plane"}, - {1230, "Bionic Steed"}, - {1231, "Bat-Raptor"}, - {1232, "Ultrabat"}, - {1233, "Batwing"}, - {1234, "The Black Thunder"}, - {1235, "Bat-Tank"}, - {1236, "Skeleton Organ"}, - {1237, "Skeleton Jukebox"}, - {1238, "Skele-Turkey"}, - {1239, "One-Eyed Willy's Pirate Ship"}, - {1240, "Fanged Fortune"}, - {1241, "Inferno Cannon"}, - {1242, "Buckbeak"}, - {1243, "Giant Owl"}, - {1244, "Fierce Falcon"}, - {1245, "Saturn's Sandworm"}, - {1247, "Haunted Vacuum"}, - {1246, "Spooky Spider"}, - {1248, "PPG Smartphone"}, - {1249, "PPG Hotline"}, - {1250, "Powerpuff Mag-Net"}, - {1253, "Mega Blast Bot"}, - {1251, "Ka-Pow Cannon"}, - {1252, "Slammin' Guitar"}, - {1254, "Octi"}, - {1255, "Super Skunk"}, - {1256, "Sonic Squid"}, - {1257, "T-Car"}, - {1258, "T-Forklift"}, - {1259, "T-Plane"}, - {1260, "Spellbook of Azarath"}, - {1261, "Raven Wings"}, - {1262, "Giant Hand"}, - {1263, "Titan Robot"}, - {1264, "T-Rocket"}, - {1265, "Robot Retriever"}}; - -minifig_creator_dialog::minifig_creator_dialog(QWidget* parent) - : QDialog(parent) -{ - setWindowTitle(tr("Figure Creator")); - setObjectName("figure_creator"); - setMinimumSize(QSize(500, 150)); - - QVBoxLayout* vbox_panel = new QVBoxLayout(); - - QComboBox* combo_figlist = new QComboBox(); - QStringList filterlist; - - for (const auto& [figure, figure_name] : list_minifigs) - { - QString name = QString::fromStdString(figure_name); - combo_figlist->addItem(name, QVariant(figure)); - filterlist << std::move(name); - } - - combo_figlist->addItem(tr("--Unknown--"), QVariant(0xFFFF)); - combo_figlist->setEditable(true); - combo_figlist->setInsertPolicy(QComboBox::NoInsert); - combo_figlist->model()->sort(0, Qt::AscendingOrder); - - QCompleter* co_compl = new QCompleter(filterlist, this); - co_compl->setCaseSensitivity(Qt::CaseInsensitive); - co_compl->setCompletionMode(QCompleter::PopupCompletion); - co_compl->setFilterMode(Qt::MatchContains); - combo_figlist->setCompleter(co_compl); - - vbox_panel->addWidget(combo_figlist); - - QFrame* line = new QFrame(); - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - vbox_panel->addWidget(line); - - QHBoxLayout* hbox_number = new QHBoxLayout(); - QLabel* label_number = new QLabel(tr("Figure Number:")); - QLineEdit* edit_number = new QLineEdit(QString::fromStdString(std::to_string(0))); - QRegularExpressionValidator* rxv = new QRegularExpressionValidator(QRegularExpression("\\d*"), this); - edit_number->setValidator(rxv); - hbox_number->addWidget(label_number); - hbox_number->addWidget(edit_number); - vbox_panel->addLayout(hbox_number); - - QHBoxLayout* hbox_buttons = new QHBoxLayout(); - QPushButton* btn_create = new QPushButton(tr("Create"), this); - QPushButton* btn_cancel = new QPushButton(tr("Cancel"), this); - hbox_buttons->addStretch(); - hbox_buttons->addWidget(btn_create); - hbox_buttons->addWidget(btn_cancel); - vbox_panel->addLayout(hbox_buttons); - - setLayout(vbox_panel); - - connect(combo_figlist, QOverload::of(&QComboBox::currentIndexChanged), [=](int index) - { - const u16 fig_info = combo_figlist->itemData(index).toUInt(); - if (fig_info != 0xFFFF) - { - edit_number->setText(QString::number(fig_info)); - } - }); - - connect(btn_create, &QAbstractButton::clicked, this, [=, this]() - { - bool ok_num = false; - const u16 fig_num = edit_number->text().toUInt(&ok_num) & 0xFFFF; - if (!ok_num) - { - QMessageBox::warning(this, tr("Error converting value"), tr("Figure number entered is invalid!"), QMessageBox::Ok); - return; - } - const auto found_figure = list_minifigs.find(fig_num); - if (found_figure != list_minifigs.cend()) - { - s_last_figure_path += QString::fromStdString(found_figure->second + ".bin"); - } - else - { - s_last_figure_path += QString("Unknown(%1).bin").arg(fig_num); - } - - m_file_path = QFileDialog::getSaveFileName(this, tr("Create Figure File"), s_last_figure_path, tr("Dimensions Figure (*.bin);;")); - if (m_file_path.isEmpty()) - { - return; - } - - fs::file dim_file(m_file_path.toStdString(), fs::read + fs::write + fs::create); - if (!dim_file) - { - QMessageBox::warning(this, tr("Failed to create minifig file!"), tr("Failed to create minifig file:\n%1").arg(m_file_path), QMessageBox::Ok); - return; - } - - std::array file_data{}; - g_dimensionstoypad.create_blank_character(file_data, fig_num); - - dim_file.write(file_data.data(), file_data.size()); - dim_file.close(); - - s_last_figure_path = QFileInfo(m_file_path).absolutePath() + "/"; - accept(); - }); - - connect(btn_cancel, &QAbstractButton::clicked, this, &QDialog::reject); - - connect(co_compl, QOverload::of(&QCompleter::activated), [=](const QString& text) - { - combo_figlist->setCurrentIndex(combo_figlist->findText(text)); - }); -} - -QString minifig_creator_dialog::get_file_path() const -{ - return m_file_path; -} - -minifig_move_dialog::minifig_move_dialog(QWidget* parent, u8 old_index) - : QDialog(parent) -{ - setWindowTitle(tr("Figure Mover")); - setObjectName("figure_mover"); - setMinimumSize(QSize(500, 150)); - - auto* grid_panel = new QGridLayout(); - - add_minifig_position(grid_panel, 0, 0, 0, old_index); - grid_panel->addWidget(new QLabel(tr("")), 0, 1); - add_minifig_position(grid_panel, 1, 0, 2, old_index); - grid_panel->addWidget(new QLabel(tr(""), this), 0, 3); - add_minifig_position(grid_panel, 2, 0, 4, old_index); - - add_minifig_position(grid_panel, 3, 1, 0, old_index); - add_minifig_position(grid_panel, 4, 1, 1, old_index); - grid_panel->addWidget(new QLabel(tr("")), 1, 2); - add_minifig_position(grid_panel, 5, 1, 3, old_index); - add_minifig_position(grid_panel, 6, 1, 4, old_index); - - setLayout(grid_panel); -} - -void minifig_move_dialog::add_minifig_position(QGridLayout* grid_panel, u8 index, u8 row, u8 column, u8 old_index) -{ - ensure(index < figure_slots.size()); - - auto* vbox_panel = new QVBoxLayout(); - - if (figure_slots[index]) - { - const auto found_figure = list_minifigs.find(figure_slots[index].value()); - if (found_figure != list_minifigs.cend()) - { - vbox_panel->addWidget(new QLabel(tr(found_figure->second.c_str()))); - } - } - else - { - vbox_panel->addWidget(new QLabel(tr("None"))); - } - auto* btn_move = new QPushButton(tr("Move Here"), this); - if (old_index == index) - { - btn_move->setText(tr("Pick up and Place")); - } - vbox_panel->addWidget(btn_move); - connect(btn_move, &QAbstractButton::clicked, this, [this, index] - { - m_index = index; - m_pad = index == 1 ? 1 : - index == 0 || index == 3 || index == 4 ? 2 : - 3; - accept(); - }); - - grid_panel->addLayout(vbox_panel, row, column); -} - -u8 minifig_move_dialog::get_new_pad() const -{ - return m_pad; -} - -u8 minifig_move_dialog::get_new_index() const -{ - return m_index; -} - -dimensions_dialog::dimensions_dialog(QWidget* parent) - : QDialog(parent) -{ - setWindowTitle(tr("Dimensions Manager")); - setObjectName("dimensions_manager"); - setAttribute(Qt::WA_DeleteOnClose); - setMinimumSize(QSize(800, 200)); - - QVBoxLayout* vbox_panel = new QVBoxLayout(); - QVBoxLayout* vbox_group = new QVBoxLayout(); - QHBoxLayout* hbox_group_1 = new QHBoxLayout(); - QHBoxLayout* hbox_group_2 = new QHBoxLayout(); - - QGroupBox* group_figures = new QGroupBox(tr("Active Dimensions Figures:")); - - add_minifig_slot(hbox_group_1, 2, 0); - hbox_group_1->addSpacerItem(new QSpacerItem(50, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); - add_minifig_slot(hbox_group_1, 1, 1); - hbox_group_1->addSpacerItem(new QSpacerItem(50, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); - add_minifig_slot(hbox_group_1, 3, 2); - - add_minifig_slot(hbox_group_2, 2, 3); - add_minifig_slot(hbox_group_2, 2, 4); - hbox_group_2->addSpacerItem(new QSpacerItem(50, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); - add_minifig_slot(hbox_group_2, 3, 5); - add_minifig_slot(hbox_group_2, 3, 6); - - vbox_group->addLayout(hbox_group_1); - vbox_group->addSpacerItem(new QSpacerItem(0, 20, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); - vbox_group->addLayout(hbox_group_2); - group_figures->setLayout(vbox_group); - vbox_panel->addWidget(group_figures); - setLayout(vbox_panel); -} - -dimensions_dialog::~dimensions_dialog() -{ - inst = nullptr; -} - -dimensions_dialog* dimensions_dialog::get_dlg(QWidget* parent) -{ - if (inst == nullptr) - inst = new dimensions_dialog(parent); - - return inst; -} - -void dimensions_dialog::add_minifig_slot(QHBoxLayout* layout, u8 pad, u8 index) -{ - ensure(index < figure_slots.size()); - - QVBoxLayout* vbox_layout = new QVBoxLayout(); - - QHBoxLayout* hbox_name_move = new QHBoxLayout(); - QHBoxLayout* hbox_actions = new QHBoxLayout(); - - QPushButton* clear_btn = new QPushButton(tr("Clear")); - QPushButton* create_btn = new QPushButton(tr("Create")); - QPushButton* load_btn = new QPushButton(tr("Load")); - QPushButton* move_btn = new QPushButton(tr("Move")); - - m_edit_figures[index] = new QLineEdit(); - m_edit_figures[index]->setEnabled(false); - if (figure_slots[index]) - { - const auto found_figure = list_minifigs.find(figure_slots[index].value()); - if (found_figure != list_minifigs.cend()) - { - m_edit_figures[index]->setText(QString::fromStdString(found_figure->second)); - } - else - { - m_edit_figures[index]->setText(tr("Unknown Figure")); - } - } - else - { - m_edit_figures[index]->setText(tr("None")); - } - - connect(clear_btn, &QAbstractButton::clicked, this, [this, pad, index] - { - clear_figure(pad, index); - }); - connect(create_btn, &QAbstractButton::clicked, this, [this, pad, index] - { - create_figure(pad, index); - }); - connect(load_btn, &QAbstractButton::clicked, this, [this, pad, index] - { - load_figure(pad, index); - }); - connect(move_btn, &QAbstractButton::clicked, this, [this, pad, index] - { - if (figure_slots[index]) - { - move_figure(pad, index); - } - }); - - hbox_name_move->addWidget(m_edit_figures[index]); - hbox_name_move->addWidget(move_btn); - hbox_actions->addWidget(clear_btn); - hbox_actions->addWidget(create_btn); - hbox_actions->addWidget(load_btn); - - vbox_layout->addLayout(hbox_name_move); - vbox_layout->addLayout(hbox_actions); - - layout->addLayout(vbox_layout); -} - -void dimensions_dialog::clear_figure(u8 pad, u8 index) -{ - ensure(index < figure_slots.size()); - - if (figure_slots[index]) - { - g_dimensionstoypad.remove_figure(pad, index, true, true); - figure_slots[index] = std::nullopt; - m_edit_figures[index]->setText(tr("None")); - } -} - -void dimensions_dialog::create_figure(u8 pad, u8 index) -{ - ensure(index < figure_slots.size()); - minifig_creator_dialog create_dlg(this); - if (create_dlg.exec() == Accepted) - { - load_figure_path(pad, index, create_dlg.get_file_path()); - } -} - -void dimensions_dialog::load_figure(u8 pad, u8 index) -{ - ensure(index < figure_slots.size()); - const QString file_path = QFileDialog::getOpenFileName(this, tr("Select Dimensions File"), s_last_figure_path, tr("Dimensions Figure (*.bin);;")); - if (file_path.isEmpty()) - { - return; - } - - s_last_figure_path = QFileInfo(file_path).absolutePath() + "/"; - - load_figure_path(pad, index, file_path); -} - -void dimensions_dialog::move_figure(u8 pad, u8 index) -{ - ensure(index < figure_slots.size()); - minifig_move_dialog move_dlg(this, index); - g_dimensionstoypad.temp_remove(index); - if (move_dlg.exec() == Accepted) - { - g_dimensionstoypad.move_figure(move_dlg.get_new_pad(), move_dlg.get_new_index(), pad, index); - if (index != move_dlg.get_new_index()) - { - figure_slots[move_dlg.get_new_index()] = figure_slots[index]; - m_edit_figures[move_dlg.get_new_index()]->setText(m_edit_figures[index]->text()); - figure_slots[index] = std::nullopt; - m_edit_figures[index]->setText(tr("None")); - } - } - else - { - g_dimensionstoypad.cancel_remove(index); - } -} - -void dimensions_dialog::load_figure_path(u8 pad, u8 index, const QString& path) -{ - fs::file dim_file(path.toStdString(), fs::read + fs::write + fs::lock); - if (!dim_file) - { - QMessageBox::warning(this, tr("Failed to open the figure file!"), tr("Failed to open the figure file(%1)!\nFile may already be in use on the base.").arg(path), QMessageBox::Ok); - return; - } - - std::array data; - if (dim_file.read(data.data(), data.size()) != data.size()) - { - QMessageBox::warning(this, tr("Failed to read the figure file!"), tr("Failed to read the figure file(%1)!\nFile was too small.").arg(path), QMessageBox::Ok); - return; - } - - clear_figure(pad, index); - - const u32 fig_num = g_dimensionstoypad.load_figure(data, std::move(dim_file), pad, index, true); - - figure_slots[index] = fig_num; - const auto name = list_minifigs.find(fig_num); - if (name != list_minifigs.cend()) - { - m_edit_figures[index]->setText(QString::fromStdString(name->second)); - } - else - { - const auto blank_name = list_tokens.find(fig_num); - if (blank_name != list_tokens.cend()) - { - m_edit_figures[index]->setText(QString::fromStdString(blank_name->second)); - } - else - { - m_edit_figures[index]->setText("Blank Tag"); - } - } -} diff --git a/rpcs3qt-legacy/dimensions_dialog.h b/rpcs3qt-legacy/dimensions_dialog.h deleted file mode 100644 index ceaeaef31..000000000 --- a/rpcs3qt-legacy/dimensions_dialog.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include "util/types.hpp" - -#include -#include -#include - -class minifig_creator_dialog : public QDialog -{ - Q_OBJECT - -public: - explicit minifig_creator_dialog(QWidget* parent); - QString get_file_path() const; - -protected: - QString m_file_path; -}; - -class minifig_move_dialog : public QDialog -{ - Q_OBJECT - -public: - explicit minifig_move_dialog(QWidget* parent, u8 old_index); - u8 get_new_pad() const; - u8 get_new_index() const; - -protected: - u8 m_pad = 0; - u8 m_index = 0; - -private: - void add_minifig_position(QGridLayout* grid_panel, u8 index, u8 row, u8 column, u8 old_index); -}; - -class dimensions_dialog : public QDialog -{ - Q_OBJECT - -public: - explicit dimensions_dialog(QWidget* parent); - ~dimensions_dialog(); - static dimensions_dialog* get_dlg(QWidget* parent); - - dimensions_dialog(dimensions_dialog const&) = delete; - void operator=(dimensions_dialog const&) = delete; - -protected: - void clear_figure(u8 pad, u8 index); - void create_figure(u8 pad, u8 index); - void load_figure(u8 pad, u8 index); - void move_figure(u8 pad, u8 index); - void load_figure_path(u8 pad, u8 index, const QString& path); - -protected: - std::array m_edit_figures{}; - -private: - void add_minifig_slot(QHBoxLayout* layout, u8 pad, u8 index); - static dimensions_dialog* inst; -}; diff --git a/rpcs3qt-legacy/display_sleep_control.cpp b/rpcs3qt-legacy/display_sleep_control.cpp deleted file mode 100644 index 4716be20e..000000000 --- a/rpcs3qt-legacy/display_sleep_control.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include "display_sleep_control.h" - -#ifdef _WIN32 -#include - -#elif defined(__APPLE__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" -#include -#pragma GCC diagnostic pop - -static IOPMAssertionID s_pm_assertion = kIOPMNullAssertionID; - -#elif defined(HAVE_QTDBUS) -#include -#include -#include -#include -#include "util/types.hpp" -static u32 s_dbus_cookie = 0; -#endif - -bool display_sleep_control_supported() -{ -#if defined(_WIN32) || defined(__APPLE__) - return true; -#elif defined(HAVE_QTDBUS) - for (const char* service : {"org.freedesktop.ScreenSaver", "org.mate.ScreenSaver"}) - { - QDBusInterface interface(service, "/ScreenSaver", service, QDBusConnection::sessionBus()); - if (interface.isValid()) - { - return true; - } - } - return false; -#else - return false; -#endif -} - -void enable_display_sleep(bool enabled) -{ - if (!display_sleep_control_supported()) - { - return; - } - -#ifdef _WIN32 - SetThreadExecutionState(enabled ? ES_CONTINUOUS : (ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED)); -#elif defined(__APPLE__) - if (enabled && s_pm_assertion != kIOPMNullAssertionID) - { - IOPMAssertionRelease(s_pm_assertion); - s_pm_assertion = kIOPMNullAssertionID; - } - else if (!enabled) - { -#pragma GCC diagnostic push -// Necessary as some of those values are macro using old casts -#pragma GCC diagnostic ignored "-Wold-style-cast" - IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleDisplaySleep, kIOPMAssertionLevelOn, CFSTR("Game running"), &s_pm_assertion); -#pragma GCC diagnostic pop - } -#elif defined(HAVE_QTDBUS) - if (enabled && s_dbus_cookie != 0) - { - for (const char* service : {"org.freedesktop.ScreenSaver", "org.mate.ScreenSaver"}) - { - QDBusInterface interface(service, "/ScreenSaver", service, QDBusConnection::sessionBus()); - if (interface.isValid()) - { - interface.call("UnInhibit", s_dbus_cookie); - break; - } - } - s_dbus_cookie = 0; - } - else if (!enabled) - { - for (const char* service : {"org.freedesktop.ScreenSaver", "org.mate.ScreenSaver"}) - { - QDBusInterface interface(service, "/ScreenSaver", service, QDBusConnection::sessionBus()); - if (interface.isValid()) - { - QDBusReply reply = interface.call("Inhibit", "rpcs3", "Game running"); - if (reply.isValid()) - { - s_dbus_cookie = reply.value(); - } - break; - } - } - } -#endif -} diff --git a/rpcs3qt-legacy/display_sleep_control.h b/rpcs3qt-legacy/display_sleep_control.h deleted file mode 100644 index 66d246a46..000000000 --- a/rpcs3qt-legacy/display_sleep_control.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -bool display_sleep_control_supported(); -void enable_display_sleep(bool enabled); diff --git a/rpcs3qt-legacy/downloader.cpp b/rpcs3qt-legacy/downloader.cpp deleted file mode 100644 index 999a78e08..000000000 --- a/rpcs3qt-legacy/downloader.cpp +++ /dev/null @@ -1,204 +0,0 @@ -#include -#include - -#include "downloader.h" -#include "curl_handle.h" -#include "progress_dialog.h" - -#include "util/logs.hpp" - -LOG_CHANNEL(network_log, "NET"); - -usz curl_write_cb_compat(char* ptr, usz /*size*/, usz nmemb, void* userdata) -{ - downloader* download = static_cast(userdata); - return download->update_buffer(ptr, nmemb); -} - -downloader::downloader(QWidget* parent) - : QObject(parent), m_parent(parent), m_curl(new rpcs3::curl::curl_handle()) -{ -} - -downloader::~downloader() -{ - if (m_thread && m_thread->isRunning()) - { - m_curl_abort = true; - m_thread->wait(); - } -} - -void downloader::start(const std::string& url, bool follow_location, bool show_progress_dialog, const QString& progress_dialog_title, bool keep_progress_dialog_open, int expected_size) -{ - network_log.notice("Starting download from URL: %s", url); - - if (m_thread) - { - if (m_thread->isRunning()) - { - m_curl_abort = true; - m_thread->wait(); - } - m_thread->deleteLater(); - } - - m_keep_progress_dialog_open = keep_progress_dialog_open; - m_curl_buf.clear(); - m_curl_abort = false; - - CURLcode err = curl_easy_setopt(m_curl->get_curl(), CURLOPT_URL, url.c_str()); - if (err != CURLE_OK) - network_log.error("curl_easy_setopt(CURLOPT_URL, %s) error: %s", url, curl_easy_strerror(err)); - - err = curl_easy_setopt(m_curl->get_curl(), CURLOPT_WRITEFUNCTION, curl_write_cb_compat); - if (err != CURLE_OK) - network_log.error("curl_easy_setopt(CURLOPT_WRITEFUNCTION, curl_write_cb_compat) error: %s", curl_easy_strerror(err)); - - err = curl_easy_setopt(m_curl->get_curl(), CURLOPT_WRITEDATA, this); - if (err != CURLE_OK) - network_log.error("curl_easy_setopt(CURLOPT_WRITEDATA) error: %s", curl_easy_strerror(err)); - - err = curl_easy_setopt(m_curl->get_curl(), CURLOPT_FOLLOWLOCATION, follow_location ? 1 : 0); - if (err != CURLE_OK) - network_log.error("curl_easy_setopt(CURLOPT_FOLLOWLOCATION, %d) error: %s", follow_location, curl_easy_strerror(err)); - - m_thread = QThread::create([this] - { - // Reset error buffer before we call curl_easy_perform - m_curl->reset_error_buffer(); - - const CURLcode result = curl_easy_perform(m_curl->get_curl()); - m_curl_success = result == CURLE_OK; - - if (!m_curl_success && !m_curl_abort) - { - const std::string error = fmt::format("curl_easy_perform(): %s", m_curl->get_verbose_error(result)); - network_log.error("%s", error); - Q_EMIT signal_download_error(QString::fromStdString(error)); - } - }); - - connect(m_thread, &QThread::finished, this, [this]() - { - if (m_curl_abort) - { - network_log.notice("Download aborted"); - return; - } - - if (!m_keep_progress_dialog_open || !m_curl_success) - { - close_progress_dialog(); - } - - if (m_curl_success) - { - network_log.notice("Download finished"); - Q_EMIT signal_download_finished(m_curl_buf); - } - }); - - // The downloader's signals are expected to be disconnected and customized before start is called. - // Therefore we need to (re)connect its signal(s) here and not in the constructor. - connect(this, &downloader::signal_buffer_update, this, &downloader::handle_buffer_update); - - if (show_progress_dialog) - { - const int maximum = expected_size > 0 ? expected_size : 100; - - if (m_progress_dialog) - { - m_progress_dialog->setWindowTitle(progress_dialog_title); - m_progress_dialog->setAutoClose(!m_keep_progress_dialog_open); - m_progress_dialog->SetRange(0, maximum); - } - else - { - m_progress_dialog = new progress_dialog(progress_dialog_title, tr("Please wait..."), tr("Abort"), 0, maximum, true, m_parent); - m_progress_dialog->setAutoReset(false); - m_progress_dialog->setAutoClose(!m_keep_progress_dialog_open); - m_progress_dialog->show(); - - // Handle abort - connect(m_progress_dialog, &QProgressDialog::canceled, this, [this]() - { - m_curl_abort = true; - m_progress_dialog = nullptr; // The progress dialog deletes itself on close - Q_EMIT signal_download_canceled(); - }); - connect(m_progress_dialog, &QProgressDialog::finished, this, [this]() - { - m_progress_dialog = nullptr; // The progress dialog deletes itself on close - }); - } - } - - m_thread->setObjectName("Download Thread"); - m_thread->setParent(this); - m_thread->start(); -} - -void downloader::update_progress_dialog(const QString& title) const -{ - if (m_progress_dialog) - { - m_progress_dialog->setWindowTitle(title); - } -} - -void downloader::close_progress_dialog() -{ - if (m_progress_dialog) - { - m_progress_dialog->accept(); - m_progress_dialog = nullptr; - } -} - -progress_dialog* downloader::get_progress_dialog() const -{ - return m_progress_dialog; -} - -usz downloader::update_buffer(char* data, usz size) -{ - if (m_curl_abort) - { - return 0; - } - - const auto old_size = m_curl_buf.size(); - const auto new_size = old_size + size; - m_curl_buf.resize(static_cast(new_size)); - memcpy(m_curl_buf.data() + old_size, data, size); - - int max = 0; - - if (m_actual_download_size < 0) - { - if (curl_easy_getinfo(m_curl->get_curl(), CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &m_actual_download_size) == CURLE_OK && m_actual_download_size > 0) - { - max = static_cast(m_actual_download_size); - } - } - - Q_EMIT signal_buffer_update(static_cast(new_size), max); - - return size; -} - -void downloader::handle_buffer_update(int size, int max) const -{ - if (m_curl_abort) - { - return; - } - - if (m_progress_dialog) - { - m_progress_dialog->SetRange(0, max > 0 ? max : m_progress_dialog->maximum()); - m_progress_dialog->SetValue(size); - QApplication::processEvents(); - } -} diff --git a/rpcs3qt-legacy/downloader.h b/rpcs3qt-legacy/downloader.h deleted file mode 100644 index 5e96b3476..000000000 --- a/rpcs3qt-legacy/downloader.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include -#include "util/atomic.hpp" - -namespace rpcs3 -{ - namespace curl - { - class curl_handle; - } -} // namespace rpcs3 - -class progress_dialog; - -class downloader : public QObject -{ - Q_OBJECT - -public: - explicit downloader(QWidget* parent = nullptr); - ~downloader(); - - void start(const std::string& url, bool follow_location, bool show_progress_dialog, const QString& progress_dialog_title = "", bool keep_progress_dialog_open = false, int expected_size = -1); - usz update_buffer(char* data, usz size); - - void update_progress_dialog(const QString& title) const; - void close_progress_dialog(); - - progress_dialog* get_progress_dialog() const; - -private Q_SLOTS: - void handle_buffer_update(int size, int max) const; - -Q_SIGNALS: - void signal_download_error(const QString& error); - void signal_download_finished(const QByteArray& data); - void signal_download_canceled(); - void signal_buffer_update(int size, int max); - -private: - QWidget* m_parent = nullptr; - - std::unique_ptr m_curl; - QByteArray m_curl_buf; - atomic_t m_curl_abort = false; - atomic_t m_curl_success = false; - double m_actual_download_size = -1.0; - - progress_dialog* m_progress_dialog = nullptr; - atomic_t m_keep_progress_dialog_open = false; - QString m_progress_dialog_title; - - QThread* m_thread = nullptr; -}; diff --git a/rpcs3qt-legacy/elf_memory_dumping_dialog.cpp b/rpcs3qt-legacy/elf_memory_dumping_dialog.cpp deleted file mode 100644 index f15cf5998..000000000 --- a/rpcs3qt-legacy/elf_memory_dumping_dialog.cpp +++ /dev/null @@ -1,222 +0,0 @@ -#include "elf_memory_dumping_dialog.h" -#include "Emu/Cell/SPUThread.h" - -#include "qt_utils.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -LOG_CHANNEL(gui_log, "GUI"); - -Q_DECLARE_METATYPE(spu_memory_segment_dump_data); - -elf_memory_dumping_dialog::elf_memory_dumping_dialog(u32 ppu_debugger_addr, std::shared_ptr _gui_settings, QWidget* parent) - : QDialog(parent), m_gui_settings(std::move(_gui_settings)) -{ - setWindowTitle(tr("SPU ELF Dumper")); - setAttribute(Qt::WA_DeleteOnClose); - - m_seg_list = new QListWidget(); - - // Font - const int pSize = 10; - QFont mono = QFontDatabase::systemFont(QFontDatabase::FixedFont); - mono.setPointSize(pSize); - - m_seg_list->setMinimumWidth(gui::utils::get_label_width(tr("PPU Address: 0x00000000, LS Address: 0x00000, Segment Size: 0x00000, Flags: 0x0"))); - - // Address expression input - auto make_hex_edit = [this, mono](u32 max_digits) - { - QLineEdit* le = new QLineEdit(); - le->setFont(mono); - le->setMaxLength(max_digits + 2); - le->setPlaceholderText("0x" + QStringLiteral("0").repeated(max_digits)); - le->setValidator(new QRegularExpressionValidator(QRegularExpression(QStringLiteral("^(0[xX])?0*[a-fA-F0-9]{0,%1}$").arg(max_digits)), this)); - return le; - }; - - m_segment_size_input = make_hex_edit(5); - m_ppu_address_input = make_hex_edit(8); - m_ls_address_input = make_hex_edit(5); - m_segment_flags_input = make_hex_edit(1); - m_segment_flags_input->setText("0x7"); // READ WRITE EXEC - m_ppu_address_input->setText(QStringLiteral("0x%1").arg(ppu_debugger_addr & -0x10000, 1, 16)); // SPU code segments are usually 128 bytes aligned, let's make it even 64k so the user would have to type himself the lower part to avoid human errors. - - QPushButton* add_segment_button = new QPushButton(QStringLiteral("+")); - add_segment_button->setToolTip(tr("Add new segment")); - add_segment_button->setFixedWidth(add_segment_button->sizeHint().height()); // Make button square - connect(add_segment_button, &QAbstractButton::clicked, this, &elf_memory_dumping_dialog::add_new_segment); - - QPushButton* remove_segment_button = new QPushButton(QStringLiteral("-")); - remove_segment_button->setToolTip(tr("Remove segment")); - remove_segment_button->setFixedWidth(remove_segment_button->sizeHint().height()); // Make button square - remove_segment_button->setEnabled(false); - connect(remove_segment_button, &QAbstractButton::clicked, this, &elf_memory_dumping_dialog::remove_segment); - - QPushButton* save_to_file = new QPushButton(tr("Save To ELF")); - save_to_file->setToolTip(tr("Save To An ELF file")); - connect(save_to_file, &QAbstractButton::clicked, this, &elf_memory_dumping_dialog::save_to_file); - - QHBoxLayout* hbox_input = new QHBoxLayout; - hbox_input->addWidget(new QLabel(tr("Segment Size:"))); - hbox_input->addWidget(m_segment_size_input); - hbox_input->addSpacing(5); - - hbox_input->addWidget(new QLabel(tr("PPU Address:"))); - hbox_input->addWidget(m_ppu_address_input); - hbox_input->addSpacing(5); - hbox_input->addWidget(new QLabel(tr("LS Address:"))); - hbox_input->addWidget(m_ls_address_input); - hbox_input->addSpacing(5); - hbox_input->addWidget(new QLabel(tr("Flags:"))); - hbox_input->addWidget(m_segment_flags_input); - - QHBoxLayout* hbox_save_and_edit = new QHBoxLayout; - hbox_save_and_edit->addStretch(2); - hbox_save_and_edit->addWidget(add_segment_button); - hbox_save_and_edit->addSpacing(4); - hbox_save_and_edit->addWidget(remove_segment_button); - hbox_save_and_edit->addSpacing(4); - hbox_save_and_edit->addWidget(save_to_file); - - QVBoxLayout* vbox = new QVBoxLayout(); - vbox->addLayout(hbox_input); - vbox->addSpacing(5); - vbox->addWidget(m_seg_list); - vbox->addSpacing(5); - vbox->addLayout(hbox_save_and_edit); - - setLayout(vbox); - - connect(m_seg_list, &QListWidget::currentRowChanged, this, [this, remove_segment_button](int row) - { - remove_segment_button->setEnabled(row >= 0 && m_seg_list->item(row)); - }); - - show(); -} - -void elf_memory_dumping_dialog::add_new_segment() -{ - QStringList errors; - auto interpret = [&](QString text, QString error_field) -> u32 - { - bool ok = false; - - // Parse expression (or at least used to, was nuked to remove the need for QtJsEngine) - const QString fixed_expression = QRegularExpression(QRegularExpression::anchoredPattern("a .*|^[A-Fa-f0-9]+$")).match(text).hasMatch() ? "0x" + text : text; - const u32 res = static_cast(fixed_expression.toULong(&ok, 16)); - - if (!ok) - { - errors << error_field; - return umax; - } - - return res; - }; - - spu_memory_segment_dump_data data{}; - data.segment_size = interpret(m_segment_size_input->text(), tr("Segment Size")); - data.src_addr = vm::get_super_ptr(interpret(m_ppu_address_input->text(), tr("PPU Address"))); - data.ls_addr = interpret(m_ls_address_input->text(), tr("LS Address")); - data.flags = interpret(m_segment_flags_input->text(), tr("Segment Flags")); - - if (!errors.isEmpty()) - { - QMessageBox::warning(this, tr("Failed To Add Segment"), tr("Segment parameters are incorrect:\n%1").arg(errors.join('\n'))); - return; - } - - if (data.segment_size % 4) - { - QMessageBox::warning(this, tr("Failed To Add Segment"), tr("SPU segment size must be 4 bytes aligned.")); - return; - } - - if (data.segment_size + data.ls_addr > SPU_LS_SIZE || data.segment_size == 0 || data.segment_size % 4) - { - QMessageBox::warning(this, tr("Failed To Add Segment"), tr("SPU segment range is invalid.")); - return; - } - - if (!vm::check_addr(vm::try_get_addr(data.src_addr).first, vm::page_readable, data.segment_size)) - { - QMessageBox::warning(this, tr("Failed To Add Segment"), tr("PPU address range is not accessible.")); - return; - } - - for (int i = 0; i < m_seg_list->count(); ++i) - { - ensure(m_seg_list->item(i)->data(Qt::UserRole).canConvert()); - const auto seg_stored = m_seg_list->item(i)->data(Qt::UserRole).value(); - - const auto stored_max = seg_stored.src_addr + seg_stored.segment_size; - const auto data_max = data.src_addr + data.segment_size; - - if (seg_stored.src_addr < data_max && data.src_addr < stored_max) - { - QMessageBox::warning(this, tr("Failed To Add Segment"), tr("SPU segment overlaps with previous SPU segment(s)\n")); - return; - } - } - - auto item = new QListWidgetItem(tr("PPU Address: 0x%0, LS Address: 0x%1, Segment Size: 0x%2, Flags: 0x%3").arg(+vm::try_get_addr(data.src_addr).first, 5, 16).arg(data.ls_addr, 2, 16).arg(data.segment_size, 2, 16).arg(data.flags, 2, 16), m_seg_list); - item->setData(Qt::UserRole, QVariant::fromValue(data)); - m_seg_list->setCurrentItem(item); -} - -void elf_memory_dumping_dialog::remove_segment() -{ - const int row = m_seg_list->currentRow(); - if (row >= 0) - { - QListWidgetItem* item = m_seg_list->takeItem(row); - delete item; - } -} - -void elf_memory_dumping_dialog::save_to_file() -{ - std::vector segs; - segs.reserve(m_seg_list->count()); - - for (int i = 0; i < m_seg_list->count(); ++i) - { - ensure(m_seg_list->item(i)->data(Qt::UserRole).canConvert()); - const auto seg_stored = m_seg_list->item(i)->data(Qt::UserRole).value(); - segs.emplace_back(seg_stored); - } - - if (segs.empty()) - { - return; - } - - const QString path_last_elf = m_gui_settings->GetValue(gui::fd_save_elf).toString(); - - const QString qpath = QFileDialog::getSaveFileName(this, tr("Capture"), path_last_elf, "SPU ELF (*.elf)"); - const std::string path = qpath.toStdString(); - - if (!path.empty()) - { - const auto result = spu_thread::capture_memory_as_elf({segs.data(), segs.size()}).save(); - - if (!result.empty() && fs::write_file(path, fs::rewrite, result)) - { - gui_log.success("Saved ELF at %s", path); - m_gui_settings->SetValue(gui::fd_save_elf, qpath); - } - else - { - QMessageBox::warning(this, tr("Save Failure"), tr("Failed to save SPU ELF.")); - } - } -} diff --git a/rpcs3qt-legacy/elf_memory_dumping_dialog.h b/rpcs3qt-legacy/elf_memory_dumping_dialog.h deleted file mode 100644 index 3c6d7fe59..000000000 --- a/rpcs3qt-legacy/elf_memory_dumping_dialog.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "util/types.hpp" -#include "gui_settings.h" - -#include -#include -#include - -#include - -class elf_memory_dumping_dialog : public QDialog -{ - Q_OBJECT - -public: - explicit elf_memory_dumping_dialog(u32 ppu_debugger_pc, std::shared_ptr _gui_settings, QWidget* parent = nullptr); - -protected: - void add_new_segment(); - void remove_segment(); - void save_to_file(); - - std::shared_ptr m_gui_settings; - - // UI variables needed in higher scope - QListWidget* m_seg_list = nullptr; - - QLineEdit* m_ls_address_input = nullptr; - QLineEdit* m_segment_size_input = nullptr; - QLineEdit* m_ppu_address_input = nullptr; - QLineEdit* m_segment_flags_input = nullptr; -}; diff --git a/rpcs3qt-legacy/emu_settings.cpp b/rpcs3qt-legacy/emu_settings.cpp deleted file mode 100644 index 459262c4b..000000000 --- a/rpcs3qt-legacy/emu_settings.cpp +++ /dev/null @@ -1,1468 +0,0 @@ -#include "emu_settings.h" -#include "config_adapter.h" - -#include -#include -#include -#include - -#include "Emu/System.h" -#include "Emu/system_config.h" -#include "Emu/system_utils.hpp" -#include "rpcsx/fw/ps3/cellSysutil.h" -#include "Emu/Io/Keyboard.h" - -#include "util/yaml.hpp" -#include "util/File.h" -#include "util/Config.h" - -LOG_CHANNEL(cfg_log, "CFG"); - -extern std::string g_cfg_defaults; //! Default settings grabbed from util/Config.h - -// Emit sorted YAML -namespace -{ - static NEVER_INLINE void emit_data(YAML::Emitter& out, const YAML::Node& node) - { - // TODO - out << node; - } - - // Incrementally load YAML - static NEVER_INLINE void operator+=(YAML::Node& left, const YAML::Node& node) - { - if (node && !node.IsNull()) - { - if (node.IsMap()) - { - for (const auto& pair : node) - { - if (pair.first.IsScalar()) - { - auto&& lhs = left[pair.first.Scalar()]; - lhs += pair.second; - } - else - { - // Exotic case (TODO: probably doesn't work) - auto&& lhs = left[YAML::Clone(pair.first)]; - lhs += pair.second; - } - } - } - else if (node.IsScalar() || node.IsSequence()) - { - // Scalars and sequences are replaced completely, but this may change in future. - // This logic may be overwritten by custom demands of every specific cfg:: node. - left = node; - } - } - } -} // namespace - -emu_settings::emu_settings() - : QObject() -{ -} - -bool emu_settings::Init() -{ - m_render_creator = new render_creator(this); - - if (m_render_creator->abort_requested) - { - return false; - } - - // Make Vulkan default setting if it is supported - if (m_render_creator->Vulkan.supported && !m_render_creator->Vulkan.adapters.empty()) - { - const std::string adapter = ::at32(m_render_creator->Vulkan.adapters, 0).toStdString(); - cfg_log.notice("Setting the default renderer to Vulkan. Default GPU: '%s'", adapter); - Emu.SetDefaultRenderer(video_renderer::vulkan); - Emu.SetDefaultGraphicsAdapter(adapter); - } - - return true; -} - -void emu_settings::LoadSettings(const std::string& title_id, bool create_config_from_global) -{ - m_title_id = title_id; - - // Create config path if necessary - fs::create_path(title_id.empty() ? fs::get_config_dir(true) : rpcs3::utils::get_custom_config_dir()); - - // Load default config - auto [default_config, default_error] = yaml_load(g_cfg_defaults); - - if (default_error.empty()) - { - m_default_settings = default_config; - m_current_settings = YAML::Clone(default_config); - } - else - { - cfg_log.fatal("Failed to load default config:\n%s", default_error); - QMessageBox::critical(nullptr, tr("Config Error"), tr("Failed to load default config:\n%0").arg(QString::fromStdString(default_error)), QMessageBox::Ok); - } - - if (create_config_from_global) - { - // Add global config - const std::string global_config_path = fs::get_config_dir(true) + "config.yml"; - fs::g_tls_error = fs::error::ok; - fs::file config(global_config_path, fs::read + fs::create); - auto [global_config, global_error] = yaml_load(config ? config.to_string() : ""); - - if (config && global_error.empty()) - { - m_current_settings += global_config; - } - else - { - config.close(); - cfg_log.fatal("Failed to load global config %s:\n%s (%s)", global_config_path, global_error, fs::g_tls_error); - QMessageBox::critical(nullptr, tr("Config Error"), tr("Failed to load global config:\nFile: %0\nError: %1").arg(QString::fromStdString(global_config_path)).arg(QString::fromStdString(global_error)), QMessageBox::Ok); - } - } - - // Add game config - if (!title_id.empty()) - { - // Remove obsolete settings of the global config before adding the custom settings. - // Otherwise we'll always trigger the "obsolete settings dialog" when editing custom configs. - ValidateSettings(true); - - std::string custom_config_path; - - if (std::string config_path = rpcs3::utils::get_custom_config_path(m_title_id); fs::is_file(config_path)) - { - custom_config_path = std::move(config_path); - } - - if (!custom_config_path.empty()) - { - if (fs::file config{custom_config_path}) - { - auto [custom_config, custom_error] = yaml_load(config.to_string()); - config.close(); - - if (custom_error.empty()) - { - m_current_settings += custom_config; - } - else - { - cfg_log.fatal("Failed to load custom config %s:\n%s", custom_config_path, custom_error); - QMessageBox::critical(nullptr, tr("Config Error"), tr("Failed to load custom config:\nFile: %0\nError: %1").arg(QString::fromStdString(custom_config_path)).arg(QString::fromStdString(custom_error)), QMessageBox::Ok); - } - } - else if (fs::g_tls_error != fs::error::noent) - { - cfg_log.fatal("Failed to load custom config %s (file error: %s)", custom_config_path, fs::g_tls_error); - QMessageBox::critical(nullptr, tr("Config Error"), tr("Failed to load custom config:\nFile: %0\nError: %1").arg(QString::fromStdString(custom_config_path)).arg(QString::fromStdString(fmt::format("%s", fs::g_tls_error))), QMessageBox::Ok); - } - } - } -} - -bool emu_settings::ValidateSettings(bool cleanup) -{ - bool is_clean = true; - - std::function&, cfg::_base*)> search_level; - search_level = [&search_level, &is_clean, &cleanup, this](int level, YAML::Node& yml_node, std::vector& keys, cfg::_base* cfg_base) - { - if (!yml_node || !yml_node.IsMap()) - { - return; - } - - const int next_level = level + 1; - - for (const auto& yml_entry : yml_node) - { - const std::string& key = yml_entry.first.Scalar(); - cfg::_base* cfg_node = nullptr; - - keys.resize(next_level); - keys[level] = key; - - if (cfg_base && cfg_base->get_type() == cfg::type::node) - { - for (const auto& node : static_cast(cfg_base)->get_nodes()) - { - if (node->get_name() == keys[level]) - { - cfg_node = node; - break; - } - } - } - - if (cfg_node) - { - // Ignore every node in Log subsection - if (level == 0 && cfg_node->get_name() == "Log") - { - continue; - } - - YAML::Node next_node = yml_node[key]; - search_level(next_level, next_node, keys, cfg_node); - } - else - { - const auto get_full_key = [&keys](const std::string& separator) -> std::string - { - std::string full_key; - for (usz i = 0; i < keys.size(); i++) - { - full_key += keys[i]; - if (i < keys.size() - 1) - full_key += separator; - } - return full_key; - }; - - is_clean = false; - - if (cleanup) - { - if (!yml_node.remove(key)) - { - cfg_log.error("Could not remove config entry: %s", get_full_key(": ")); - is_clean = true; // abort - return; - } - - // Let's only remove one entry at a time. I got some weird issues when doing all at once. - return; - } - else - { - cfg_log.warning("Unknown config entry found: %s", get_full_key(": ")); - } - } - } - }; - - std::unique_ptr root = std::make_unique(); - std::vector keys; - - do - { - is_clean = true; - search_level(0, m_current_settings, keys, root.get()); - } while (cleanup && !is_clean); - - return is_clean; -} - -void emu_settings::RestoreDefaults() -{ - m_current_settings = YAML::Clone(m_default_settings); - Q_EMIT RestoreDefaultsSignal(); -} - -void emu_settings::SaveSettings() const -{ - YAML::Emitter out; - emit_data(out, m_current_settings); - Emulator::SaveSettings(out.c_str(), m_title_id); -} - -void emu_settings::EnhanceComboBox(QComboBox* combobox, emu_settings_type type, bool is_ranged, bool use_max, int max, bool sorted, bool strict) -{ - if (!combobox) - { - cfg_log.fatal("EnhanceComboBox '%s' was used with an invalid object", cfg_adapter::get_setting_name(type)); - return; - } - - if (is_ranged) - { - if (sorted) - { - cfg_log.warning("EnhanceCombobox '%s': ignoring sorting request on ranged combo box", cfg_adapter::get_setting_name(type)); - } - - const QStringList range = GetQStringSettingOptions(type); - ensure(!range.empty()); - - const int max_item = use_max ? max : range.last().toInt(); - - for (int i = range.first().toInt(); i <= max_item; i++) - { - combobox->addItem(QString::number(i), i); - } - } - else - { - const QStringList settings = GetQStringSettingOptions(type); - - for (int i = 0; i < settings.count(); i++) - { - const QString localized_setting = GetLocalizedSetting(settings[i], type, combobox->count(), strict); - combobox->addItem(localized_setting, QVariant({settings[i], i})); - } - - if (sorted) - { - combobox->model()->sort(0, Qt::AscendingOrder); - } - } - - // Since the QComboBox has localised strings, we can't just findText / findData, so we need to manually iterate through it to find our index - const auto find_index = [](QComboBox* combobox, const QString& value) - { - if (!combobox) - { - return -1; - } - - for (int i = 0; i < combobox->count(); i++) - { - const QVariantList var_list = combobox->itemData(i).toList(); - if (var_list.size() != 2 || !var_list[0].canConvert()) - { - fmt::throw_exception("Invalid data found in combobox entry %d (text='%s', listsize=%d, itemcount=%d)", i, combobox->itemText(i), var_list.size(), combobox->count()); - } - - if (value == var_list[0].toString()) - { - return i; - } - } - return -1; - }; - - const std::string def = GetSettingDefault(type); - const std::string selected = GetSetting(type); - const QString selected_q = QString::fromStdString(selected); - int index; - - if (is_ranged) - { - index = combobox->findData(selected_q); - } - else - { - index = find_index(combobox, selected_q); - } - - if (index == -1) - { - cfg_log.error("EnhanceComboBox '%s' tried to set an invalid value: %s. Setting to default: %s", cfg_adapter::get_setting_name(type), selected, def); - - if (is_ranged) - { - index = combobox->findData(QString::fromStdString(def)); - } - else - { - index = find_index(combobox, QString::fromStdString(def)); - } - - m_broken_types.insert(type); - } - - combobox->setCurrentIndex(index); - - connect(combobox, QOverload::of(&QComboBox::currentIndexChanged), combobox, [this, is_ranged, combobox, type](int index) - { - if (index < 0) - return; - - if (is_ranged) - { - SetSetting(type, combobox->itemData(index).toString().toStdString()); - } - else - { - const QVariantList var_list = combobox->itemData(index).toList(); - if (var_list.size() != 2 || !var_list[0].canConvert()) - { - fmt::throw_exception("Invalid data found in combobox entry %d (text='%s', listsize=%d, itemcount=%d)", index, combobox->itemText(index), var_list.size(), combobox->count()); - } - SetSetting(type, var_list[0].toString().toStdString()); - } - }); - - connect(this, &emu_settings::RestoreDefaultsSignal, combobox, [def, combobox, is_ranged, find_index]() - { - if (is_ranged) - { - combobox->setCurrentIndex(combobox->findData(QString::fromStdString(def))); - } - else - { - combobox->setCurrentIndex(find_index(combobox, QString::fromStdString(def))); - } - }); -} - -void emu_settings::EnhanceCheckBox(QCheckBox* checkbox, emu_settings_type type) -{ - if (!checkbox) - { - cfg_log.fatal("EnhanceCheckBox '%s' was used with an invalid object", cfg_adapter::get_setting_name(type)); - return; - } - - std::string def = GetSettingDefault(type); - std::transform(def.begin(), def.end(), def.begin(), ::tolower); - - if (def != "true" && def != "false") - { - cfg_log.fatal("EnhanceCheckBox '%s' was used with an invalid emu_settings_type", cfg_adapter::get_setting_name(type)); - return; - } - - std::string selected = GetSetting(type); - std::transform(selected.begin(), selected.end(), selected.begin(), ::tolower); - - if (selected == "true") - { - checkbox->setChecked(true); - } - else if (selected != "false") - { - cfg_log.error("EnhanceCheckBox '%s' tried to set an invalid value: %s. Setting to default: %s", cfg_adapter::get_setting_name(type), selected, def); - checkbox->setChecked(def == "true"); - m_broken_types.insert(type); - } - - connect(checkbox, &QCheckBox::checkStateChanged, this, [type, this](Qt::CheckState val) - { - const std::string str = val != Qt::Unchecked ? "true" : "false"; - SetSetting(type, str); - }); - - connect(this, &emu_settings::RestoreDefaultsSignal, checkbox, [def, checkbox]() - { - checkbox->setChecked(def == "true"); - }); -} - -void emu_settings::EnhanceDateTimeEdit(QDateTimeEdit* date_time_edit, emu_settings_type type, const QString& format, bool use_calendar, bool as_offset_from_now, int offset_update_time) -{ - if (!date_time_edit) - { - cfg_log.fatal("EnhanceDateTimeEdit '%s' was used with an invalid object", cfg_adapter::get_setting_name(type)); - return; - } - - date_time_edit->setDisplayFormat(format); - date_time_edit->setCalendarPopup(use_calendar); - - if (as_offset_from_now) - { - // If using offset from now, then we disable the keyboard tracking to reduce the numebr of events that occur (since for each event we will lose focus) - date_time_edit->setKeyboardTracking(false); - - const QStringList range = GetQStringSettingOptions(type); - ensure(!range.empty()); - - bool ok_def = false, ok_min = false, ok_max = false; - const s64 def = QString::fromStdString(GetSettingDefault(type)).toLongLong(&ok_def); - const s64 min = range.first().toLongLong(&ok_min); - const s64 max = range.last().toLongLong(&ok_max); - if (!ok_def || !ok_min || !ok_max) - { - cfg_log.fatal("EnhanceDateTimeEdit '%s' was used with an invalid emu_settings_type", cfg_adapter::get_setting_name(type)); - return; - } - - bool ok_sel = false; - s64 val = QString::fromStdString(GetSetting(type)).toLongLong(&ok_sel); - if (!ok_sel || val < min || val > max) - { - cfg_log.error("EnhanceDateTimeEdit '%s' tried to set an invalid value: %d. Setting to default: %d. Allowed range: [%d, %d]", cfg_adapter::get_setting_name(type), val, def, min, max); - val = def; - m_broken_types.insert(type); - SetSetting(type, std::to_string(def)); - } - - // we'll capture the DateTime once, and apply the min/max and offset against it here. - const QDateTime now = QDateTime::currentDateTime(); - - // we set the allowed limits - date_time_edit->setDateTimeRange(now.addSecs(min), now.addSecs(max)); - - // we add the offset, and set the control to have this datetime value - const QDateTime date_time = now.addSecs(val); - date_time_edit->setDateTime(date_time); - - // if we have an invalid update time then we won't run the update timer - if (offset_update_time > 0) - { - QTimer* console_time_update = new QTimer(date_time_edit); - connect(console_time_update, &QTimer::timeout, date_time_edit, [this, date_time_edit, min, max]() - { - if (!date_time_edit->hasFocus() && (!date_time_edit->calendarPopup() || !date_time_edit->calendarWidget()->hasFocus())) - { - const QDateTime now = QDateTime::currentDateTime(); - const s64 offset = QString::fromStdString(GetSetting(emu_settings_type::ConsoleTimeOffset)).toLongLong(); - date_time_edit->setDateTime(now.addSecs(offset)); - date_time_edit->setDateTimeRange(now.addSecs(min), now.addSecs(max)); - } - }); - - console_time_update->start(offset_update_time); - } - - connect(this, &emu_settings::RestoreDefaultsSignal, date_time_edit, [def, date_time_edit]() - { - date_time_edit->setDateTime(QDateTime::currentDateTime().addSecs(def)); - }); - } - else - { - const QStringList range = GetQStringSettingOptions(type); - ensure(!range.empty()); - - QString str = QString::fromStdString(GetSettingDefault(type)); - const QDateTime def = QDateTime::fromString(str, Qt::ISODate); - const QDateTime min = QDateTime::fromString(range.first(), Qt::ISODate); - const QDateTime max = QDateTime::fromString(range.last(), Qt::ISODate); - if (!def.isValid() || !min.isValid() || !max.isValid()) - { - cfg_log.fatal("EnhanceDateTimeEdit '%s' was used with an invalid emu_settings_type", cfg_adapter::get_setting_name(type)); - return; - } - - str = QString::fromStdString(GetSetting(type)); - QDateTime val = QDateTime::fromString(str, Qt::ISODate); - if (!val.isValid() || val < min || val > max) - { - cfg_log.error("EnhanceDateTimeEdit '%s' tried to set an invalid value: %s. Setting to default: %s Allowed range: [%s, %s]", - cfg_adapter::get_setting_name(type), val.toString(Qt::ISODate), def.toString(Qt::ISODate), min.toString(Qt::ISODate), max.toString(Qt::ISODate)); - val = def; - m_broken_types.insert(type); - SetSetting(type, def.toString(Qt::ISODate).toStdString()); - } - - // we set the allowed limits - date_time_edit->setDateTimeRange(min, max); - - // set the date_time value to the control - date_time_edit->setDateTime(val); - - connect(this, &emu_settings::RestoreDefaultsSignal, date_time_edit, [def, date_time_edit]() - { - date_time_edit->setDateTime(def); - }); - } - - connect(date_time_edit, &QDateTimeEdit::dateTimeChanged, this, [date_time_edit, type, as_offset_from_now, this](const QDateTime& datetime) - { - if (as_offset_from_now) - { - // offset will be applied in seconds - const s64 offset = QDateTime::currentDateTime().secsTo(datetime); - SetSetting(type, std::to_string(offset)); - - // HACK: We are only looking at whether the control has focus to prevent the time from updating dynamically, so we - // clear the focus, so that this dynamic updating isn't suppressed. - if (date_time_edit) - { - date_time_edit->clearFocus(); - } - } - else - { - // date time will be written straight into settings - SetSetting(type, datetime.toString(Qt::ISODate).toStdString()); - } - }); -} - -void emu_settings::EnhanceSlider(QSlider* slider, emu_settings_type type) -{ - if (!slider) - { - cfg_log.fatal("EnhanceSlider '%s' was used with an invalid object", cfg_adapter::get_setting_name(type)); - return; - } - - const QStringList range = GetQStringSettingOptions(type); - ensure(!range.empty()); - - bool ok_def, ok_sel, ok_min, ok_max; - - const int def = QString::fromStdString(GetSettingDefault(type)).toInt(&ok_def); - const int min = range.first().toInt(&ok_min); - const int max = range.last().toInt(&ok_max); - - if (!ok_def || !ok_min || !ok_max) - { - cfg_log.fatal("EnhanceSlider '%s' was used with an invalid emu_settings_type", cfg_adapter::get_setting_name(type)); - return; - } - - const QString selected = QString::fromStdString(GetSetting(type)); - int val = selected.toInt(&ok_sel); - - if (!ok_sel || val < min || val > max) - { - cfg_log.error("EnhanceSlider '%s' tried to set an invalid value: %d. Setting to default: %d. Allowed range: [%d, %d]", cfg_adapter::get_setting_name(type), val, def, min, max); - val = def; - m_broken_types.insert(type); - } - - slider->setRange(min, max); - slider->setValue(val); - - connect(slider, &QSlider::valueChanged, this, [type, this](int value) - { - SetSetting(type, QString::number(value).toStdString()); - }); - - connect(this, &emu_settings::RestoreDefaultsSignal, slider, [def, slider]() - { - slider->setValue(def); - }); -} - -void emu_settings::EnhanceSpinBox(QSpinBox* spinbox, emu_settings_type type, const QString& prefix, const QString& suffix) -{ - if (!spinbox) - { - cfg_log.fatal("EnhanceSpinBox '%s' was used with an invalid object", cfg_adapter::get_setting_name(type)); - return; - } - - const QStringList range = GetQStringSettingOptions(type); - ensure(!range.empty()); - - bool ok_def, ok_sel, ok_min, ok_max; - - const int def = QString::fromStdString(GetSettingDefault(type)).toInt(&ok_def); - const int min = range.first().toInt(&ok_min); - const int max = range.last().toInt(&ok_max); - - if (!ok_def || !ok_min || !ok_max) - { - cfg_log.fatal("EnhanceSpinBox '%s' was used with an invalid type", cfg_adapter::get_setting_name(type)); - return; - } - - const std::string selected = GetSetting(type); - int val = QString::fromStdString(selected).toInt(&ok_sel); - - if (!ok_sel || val < min || val > max) - { - cfg_log.error("EnhanceSpinBox '%s' tried to set an invalid value: %d. Setting to default: %d. Allowed range: [%d, %d]", cfg_adapter::get_setting_name(type), selected, def, min, max); - val = def; - m_broken_types.insert(type); - } - - spinbox->setPrefix(prefix); - spinbox->setSuffix(suffix); - spinbox->setRange(min, max); - spinbox->setValue(val); - - connect(spinbox, &QSpinBox::textChanged, this, [type, spinbox, this](const QString& /* text*/) - { - if (!spinbox) - return; - SetSetting(type, spinbox->cleanText().toStdString()); - }); - - connect(this, &emu_settings::RestoreDefaultsSignal, spinbox, [def, spinbox]() - { - spinbox->setValue(def); - }); -} - -void emu_settings::EnhanceDoubleSpinBox(QDoubleSpinBox* spinbox, emu_settings_type type, const QString& prefix, const QString& suffix) -{ - if (!spinbox) - { - cfg_log.fatal("EnhanceDoubleSpinBox '%s' was used with an invalid object", cfg_adapter::get_setting_name(type)); - return; - } - - const std::vector range = GetSettingOptions(type); - ensure(!range.empty()); - - const std::string def_s = GetSettingDefault(type); - const std::string val_s = GetSetting(type); - const std::string& min_s = range.front(); - const std::string& max_s = range.back(); - - // cfg::_float range is in s32 - constexpr s32 min_value = ::std::numeric_limits::min(); - constexpr s32 max_value = ::std::numeric_limits::max(); - - f64 val, def, min, max; - const bool ok_sel = try_to_float(&val, val_s, min_value, max_value); - const bool ok_def = try_to_float(&def, def_s, min_value, max_value); - const bool ok_min = try_to_float(&min, min_s, min_value, max_value); - const bool ok_max = try_to_float(&max, max_s, min_value, max_value); - - if (!ok_def || !ok_min || !ok_max) - { - cfg_log.fatal("EnhanceDoubleSpinBox '%s' was used with an invalid type. (val='%s', def='%s', min_s='%s', max_s='%s')", cfg_adapter::get_setting_name(type), val_s, def_s, min_s, max_s); - return; - } - - if (!ok_sel || val < min || val > max) - { - cfg_log.error("EnhanceDoubleSpinBox '%s' tried to set an invalid value: %f. Setting to default: %f. Allowed range: [%f, %f]", cfg_adapter::get_setting_name(type), val, def, min, max); - val = def; - m_broken_types.insert(type); - } - - spinbox->setPrefix(prefix); - spinbox->setSuffix(suffix); - spinbox->setRange(min, max); - spinbox->setValue(val); - - connect(spinbox, &QDoubleSpinBox::textChanged, this, [type, spinbox, this](const QString& /* text*/) - { - if (!spinbox) - return; - SetSetting(type, spinbox->cleanText().toStdString()); - }); - - connect(this, &emu_settings::RestoreDefaultsSignal, spinbox, [def, spinbox]() - { - spinbox->setValue(def); - }); -} - -void emu_settings::EnhanceLineEdit(QLineEdit* edit, emu_settings_type type) -{ - if (!edit) - { - cfg_log.fatal("EnhanceEdit '%s' was used with an invalid object", cfg_adapter::get_setting_name(type)); - return; - } - - const std::string set_text = GetSetting(type); - edit->setText(QString::fromStdString(set_text)); - - connect(edit, &QLineEdit::textChanged, this, [type, this](const QString& text) - { - const QString trimmed = text.trimmed(); - if (trimmed.size() != text.size()) - { - cfg_log.warning("EnhanceLineEdit '%s' input was trimmed", cfg_adapter::get_setting_name(type)); - } - SetSetting(type, trimmed.toStdString()); - }); - - connect(this, &emu_settings::RestoreDefaultsSignal, edit, [this, edit, type]() - { - edit->setText(QString::fromStdString(GetSettingDefault(type))); - }); -} - -void emu_settings::EnhanceRadioButton(QButtonGroup* button_group, emu_settings_type type) -{ - if (!button_group) - { - cfg_log.fatal("EnhanceRadioButton '%s' was used with an invalid object", cfg_adapter::get_setting_name(type)); - return; - } - - const QString selected = QString::fromStdString(GetSetting(type)); - const QString def = QString::fromStdString(GetSettingDefault(type)); - const QStringList options = GetQStringSettingOptions(type); - - if (button_group->buttons().count() < options.size()) - { - cfg_log.fatal("EnhanceRadioButton '%s': wrong button count", cfg_adapter::get_setting_name(type)); - return; - } - - bool found = false; - int def_pos = -1; - - for (int i = 0; i < options.count(); i++) - { - const QString& option = options[i]; - const QString localized_setting = GetLocalizedSetting(option, type, i, true); - - QAbstractButton* button = button_group->button(i); - button->setText(localized_setting); - - if (!found && option == selected) - { - found = true; - button->setChecked(true); - } - - if (def_pos == -1 && option == def) - { - def_pos = i; - } - - connect(button, &QAbstractButton::toggled, this, [this, type, val = option.toStdString()](bool checked) - { - if (checked) - { - SetSetting(type, val); - } - }); - } - - if (!found) - { - ensure(def_pos >= 0); - - cfg_log.error("EnhanceRadioButton '%s' tried to set an invalid value: %s. Setting to default: %s.", cfg_adapter::get_setting_name(type), selected, def); - m_broken_types.insert(type); - - // Select the default option on invalid setting string - button_group->button(def_pos)->setChecked(true); - } - - connect(this, &emu_settings::RestoreDefaultsSignal, button_group, [button_group, def_pos]() - { - if (button_group && button_group->button(def_pos)) - { - button_group->button(def_pos)->setChecked(true); - } - }); -} - -std::vector emu_settings::GetLibrariesControl() -{ - return m_current_settings["Core"]["Libraries Control"].as, std::initializer_list>({}); -} - -void emu_settings::SaveSelectedLibraries(const std::vector& libs) -{ - m_current_settings["Core"]["Libraries Control"] = libs; -} - -std::vector emu_settings::GetSettingOptions(emu_settings_type type) -{ - return cfg_adapter::get_options(::at32(settings_location, type)); -} - -QStringList emu_settings::GetQStringSettingOptions(emu_settings_type type) -{ - QStringList values; - for (const std::string& value : cfg_adapter::get_options(::at32(settings_location, type))) - { - values.append(QString::fromStdString(value)); - } - return values; -} - -std::string emu_settings::GetSettingDefault(emu_settings_type type) const -{ - if (const auto node = cfg_adapter::get_node(m_default_settings, ::at32(settings_location, type)); node && node.IsScalar()) - { - return node.Scalar(); - } - - cfg_log.fatal("GetSettingDefault(type=%d) could not retrieve the requested node", static_cast(type)); - return ""; -} - -std::string emu_settings::GetSetting(emu_settings_type type) const -{ - if (const auto node = cfg_adapter::get_node(m_current_settings, ::at32(settings_location, type)); node && node.IsScalar()) - { - return node.Scalar(); - } - - cfg_log.fatal("GetSetting(type=%d) could not retrieve the requested node", static_cast(type)); - return ""; -} - -void emu_settings::SetSetting(emu_settings_type type, const std::string& val) const -{ - cfg_adapter::get_node(m_current_settings, ::at32(settings_location, type)) = val; -} - -emu_settings_type emu_settings::FindSettingsType(const cfg::_base* node) const -{ - // Add key and value to static map on first use - static std::map id_to_type; - static std::mutex mtx; - std::lock_guard lock(mtx); - - if (!node) [[unlikely]] - { - // Provoke error. Don't use ensure or we will get a nullptr deref warning in VS - return ::at32(id_to_type, umax); - } - - std::vector node_location; - if (!id_to_type.contains(node->get_id())) - { - for (const cfg::_base* n = node; n; n = n->get_parent()) - { - if (!n->get_name().empty()) - { - node_location.push_back(n->get_name()); - } - } - - std::reverse(node_location.begin(), node_location.end()); - - for (const auto& [type, loc] : settings_location) - { - if (node_location.size() != loc.size()) - { - continue; - } - - bool is_match = true; - for (usz i = 0; i < node_location.size(); i++) - { - if (node_location[i] != loc[i]) - { - is_match = false; - break; - } - } - - if (is_match && !id_to_type.try_emplace(node->get_id(), type).second) - { - cfg_log.error("'%s' already exists", loc.back()); - } - } - } - - if (!id_to_type.contains(node->get_id())) - { - fmt::throw_exception("Node '%s' not represented in emu_settings_type", node->get_name()); - } - - return ::at32(id_to_type, node->get_id()); -} - -void emu_settings::OpenCorrectionDialog(QWidget* parent) -{ - if (!m_broken_types.empty() && QMessageBox::question(parent, tr("Fix invalid settings?"), - tr( - "Your config file contained one or more unrecognized values for settings.\n" - "Their default value will be used until they are corrected.\n" - "Consider that a correction might render them invalid for other versions of RPCS3.\n" - "\n" - "Do you wish to let the program correct them for you?\n" - "This change will only be final when you save the config."), - QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) - { - for (const auto& type : m_broken_types) - { - const std::string def = GetSettingDefault(type); - const std::string old = GetSetting(type); - SetSetting(type, def); - cfg_log.success("The config entry '%s' was corrected from '%s' to '%s'", cfg_adapter::get_setting_name(type), old, def); - } - - m_broken_types.clear(); - cfg_log.success("You need to save the settings in order to make these changes permanent!"); - } -} - -QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_type type, int index, bool strict) const -{ - switch (type) - { - case emu_settings_type::SPUBlockSize: - switch (static_cast(index)) - { - case spu_block_size_type::safe: return tr("Safe", "SPU block size"); - case spu_block_size_type::mega: return tr("Mega", "SPU block size"); - case spu_block_size_type::giga: return tr("Giga", "SPU block size"); - } - break; - case emu_settings_type::ThreadSchedulerMode: - switch (static_cast(index)) - { - case thread_scheduler_mode::old: return tr("RPCS3 Scheduler", "Thread Scheduler Mode"); - case thread_scheduler_mode::alt: return tr("RPCS3 Alternative Scheduler", "Thread Scheduler Mode"); - case thread_scheduler_mode::os: return tr("Operating System", "Thread Scheduler Mode"); - } - break; - case emu_settings_type::EnableTSX: - switch (static_cast(index)) - { - case tsx_usage::disabled: return tr("Disabled", "Enable TSX"); - case tsx_usage::enabled: return tr("Enabled", "Enable TSX"); - case tsx_usage::forced: return tr("Forced", "Enable TSX"); - } - break; - case emu_settings_type::Renderer: - switch (static_cast(index)) - { - case video_renderer::null: return tr("Disable Video Output", "Video renderer"); - case video_renderer::opengl: return tr("OpenGL", "Video renderer"); - case video_renderer::vulkan: return tr("Vulkan", "Video renderer"); - } - break; - case emu_settings_type::ShaderMode: - switch (static_cast(index)) - { - case shader_mode::recompiler: return tr("Legacy (single threaded)", "Shader Mode"); - case shader_mode::async_recompiler: return tr("Async (multi threaded)", "Shader Mode"); - case shader_mode::async_with_interpreter: return tr("Async with Shader Interpreter", "Shader Mode"); - case shader_mode::interpreter_only: return tr("Shader Interpreter only", "Shader Mode"); - } - break; - case emu_settings_type::Resolution: - switch (static_cast(index)) - { - case video_resolution::_1080p: return tr("1080p", "Resolution"); - case video_resolution::_1080i: return tr("1080i", "Resolution"); - case video_resolution::_720p: return tr("720p", "Resolution"); - case video_resolution::_480p: return tr("480p", "Resolution"); - case video_resolution::_480i: return tr("480i", "Resolution"); - case video_resolution::_576p: return tr("576p", "Resolution"); - case video_resolution::_576i: return tr("576i", "Resolution"); - case video_resolution::_1600x1080p: return tr("1600x1080p", "Resolution"); - case video_resolution::_1440x1080p: return tr("1440x1080p", "Resolution"); - case video_resolution::_1280x1080p: return tr("1280x1080p", "Resolution"); - case video_resolution::_960x1080p: return tr("960x1080p", "Resolution"); - } - break; - case emu_settings_type::FrameLimit: - switch (static_cast(index)) - { - case frame_limit_type::none: return tr("Off", "Frame limit"); - case frame_limit_type::_30: return tr("30", "Frame limit"); - case frame_limit_type::_50: return tr("50", "Frame limit"); - case frame_limit_type::_60: return tr("60", "Frame limit"); - case frame_limit_type::_120: return tr("120", "Frame limit"); - case frame_limit_type::display_rate: return tr("Display", "Frame limit"); - case frame_limit_type::_auto: return tr("Auto", "Frame limit"); - case frame_limit_type::_ps3: return tr("PS3 Native", "Frame limit"); - case frame_limit_type::infinite: return tr("Infinite", "Frame limit"); - } - break; - case emu_settings_type::MSAA: - switch (static_cast(index)) - { - case msaa_level::none: return tr("Disabled", "MSAA"); - case msaa_level::_auto: return tr("Auto", "MSAA"); - } - break; - case emu_settings_type::ShaderPrecisionQuality: - switch (static_cast(index)) - { - case gpu_preset_level::_auto: return tr("Auto", "Shader Precision"); - case gpu_preset_level::ultra: return tr("Ultra", "Shader Precision"); - case gpu_preset_level::high: return tr("High", "Shader Precision"); - case gpu_preset_level::low: return tr("Low", "Shader Precision"); - } - break; - case emu_settings_type::OutputScalingMode: - switch (static_cast(index)) - { - case output_scaling_mode::nearest: return tr("Nearest", "Output Scaling Mode"); - case output_scaling_mode::bilinear: return tr("Bilinear", "Output Scaling Mode"); - case output_scaling_mode::fsr: return tr("FidelityFX Super Resolution 1", "Output Scaling Mode"); - } - break; - case emu_settings_type::AudioRenderer: - switch (static_cast(index)) - { - case audio_renderer::null: return tr("Disable Audio Output", "Audio renderer"); -#ifdef _WIN32 - case audio_renderer::xaudio: return tr("XAudio2", "Audio renderer"); -#endif - case audio_renderer::cubeb: return tr("Cubeb", "Audio renderer"); -#ifdef HAVE_FAUDIO - case audio_renderer::faudio: return tr("FAudio", "Audio renderer"); -#endif - } - break; - case emu_settings_type::MicrophoneType: - switch (static_cast(index)) - { - case microphone_handler::null: return tr("Disabled", "Microphone handler"); - case microphone_handler::standard: return tr("Standard", "Microphone handler"); - case microphone_handler::singstar: return tr("SingStar", "Microphone handler"); - case microphone_handler::real_singstar: return tr("Real SingStar", "Microphone handler"); - case microphone_handler::rocksmith: return tr("Rocksmith", "Microphone handler"); - } - break; - case emu_settings_type::KeyboardHandler: - switch (static_cast(index)) - { - case keyboard_handler::null: return tr("Null", "Keyboard handler"); - case keyboard_handler::basic: return tr("Basic", "Keyboard handler"); - } - break; - case emu_settings_type::MouseHandler: - switch (static_cast(index)) - { - case mouse_handler::null: return tr("Null", "Mouse handler"); - case mouse_handler::basic: return tr("Basic", "Mouse handler"); - case mouse_handler::raw: return tr("Raw", "Mouse handler"); - } - break; - case emu_settings_type::CameraType: - switch (static_cast(index)) - { - case fake_camera_type::unknown: return tr("Unknown", "Camera type"); - case fake_camera_type::eyetoy: return tr("EyeToy", "Camera type"); - case fake_camera_type::eyetoy2: return tr("PS Eye", "Camera type"); - case fake_camera_type::uvc1_1: return tr("UVC 1.1", "Camera type"); - } - break; - case emu_settings_type::CameraFlip: - switch (static_cast(index)) - { - case camera_flip::none: return tr("No", "Camera flip"); - case camera_flip::horizontal: return tr("Flip horizontally", "Camera flip"); - case camera_flip::vertical: return tr("Flip vertically", "Camera flip"); - case camera_flip::both: return tr("Flip both axes", "Camera flip"); - } - break; - case emu_settings_type::Camera: - switch (static_cast(index)) - { - case camera_handler::null: return tr("Null", "Camera handler"); - case camera_handler::fake: return tr("Fake", "Camera handler"); - case camera_handler::qt: return tr("Qt", "Camera handler"); - } - break; - case emu_settings_type::MusicHandler: - switch (static_cast(index)) - { - case music_handler::null: return tr("Null", "Music handler"); - case music_handler::qt: return tr("Qt", "Music handler"); - } - break; - case emu_settings_type::PadHandlerMode: - switch (static_cast(index)) - { - case pad_handler_mode::single_threaded: return tr("Single-threaded", "Pad handler mode"); - case pad_handler_mode::multi_threaded: return tr("Multi-threaded", "Pad handler mode"); - } - break; - case emu_settings_type::Move: - switch (static_cast(index)) - { - case move_handler::null: return tr("Null", "Move handler"); - case move_handler::real: return tr("Real", "Move handler"); - case move_handler::fake: return tr("Fake", "Move handler"); - case move_handler::mouse: return tr("Mouse", "Move handler"); - case move_handler::raw_mouse: return tr("Raw Mouse", "Move handler"); -#ifdef HAVE_LIBEVDEV - case move_handler::gun: return tr("Gun", "Gun handler"); -#endif - } - break; - case emu_settings_type::Buzz: - switch (static_cast(index)) - { - case buzz_handler::null: return tr("Null (use real Buzzers)", "Buzz handler"); - case buzz_handler::one_controller: return tr("1 controller (1-4 players)", "Buzz handler"); - case buzz_handler::two_controllers: return tr("2 controllers (5-7 players)", "Buzz handler"); - } - break; - case emu_settings_type::Turntable: - switch (static_cast(index)) - { - case turntable_handler::null: return tr("Null", "Turntable handler"); - case turntable_handler::one_controller: return tr("1 controller", "Turntable handler"); - case turntable_handler::two_controllers: return tr("2 controllers", "Turntable handler"); - } - break; - case emu_settings_type::GHLtar: - switch (static_cast(index)) - { - case ghltar_handler::null: return tr("Null", "GHLtar handler"); - case ghltar_handler::one_controller: return tr("1 controller", "GHLtar handler"); - case ghltar_handler::two_controllers: return tr("2 controllers", "GHLtar handler"); - } - break; - case emu_settings_type::InternetStatus: - switch (static_cast(index)) - { - case np_internet_status::disabled: return tr("Disconnected", "Internet Status"); - case np_internet_status::enabled: return tr("Connected", "Internet Status"); - } - break; - case emu_settings_type::PSNStatus: - switch (static_cast(index)) - { - case np_psn_status::disabled: return tr("Disconnected", "PSN Status"); - case np_psn_status::psn_fake: return tr("Simulated", "PSN Status"); - case np_psn_status::psn_rpcn: return tr("RPCN", "PSN Status"); - } - break; - case emu_settings_type::SleepTimersAccuracy: - switch (static_cast(index)) - { - case sleep_timers_accuracy_level::_as_host: return tr("As Host", "Sleep timers accuracy"); - case sleep_timers_accuracy_level::_usleep: return tr("Usleep Only", "Sleep timers accuracy"); - case sleep_timers_accuracy_level::_all_timers: return tr("All Timers", "Sleep timers accuracy"); - } - break; - case emu_settings_type::FIFOAccuracy: - switch (static_cast(index)) - { - case rsx_fifo_mode::fast: return tr("Fast", "RSX FIFO Accuracy"); - case rsx_fifo_mode::atomic: return tr("Atomic", "RSX FIFO Accuracy"); - case rsx_fifo_mode::atomic_ordered: return tr("Ordered & Atomic", "RSX FIFO Accuracy"); - case rsx_fifo_mode::as_ps3: return tr("PS3", "RSX FIFO Accuracy"); - } - break; - case emu_settings_type::PerfOverlayDetailLevel: - switch (static_cast(index)) - { - case detail_level::none: return tr("None", "Detail Level"); - case detail_level::minimal: return tr("Minimal", "Detail Level"); - case detail_level::low: return tr("Low", "Detail Level"); - case detail_level::medium: return tr("Medium", "Detail Level"); - case detail_level::high: return tr("High", "Detail Level"); - } - break; - case emu_settings_type::PerfOverlayFramerateDetailLevel: - case emu_settings_type::PerfOverlayFrametimeDetailLevel: - switch (static_cast(index)) - { - case perf_graph_detail_level::minimal: return tr("Minimal", "Perf Graph Detail Level"); - case perf_graph_detail_level::show_min_max: return tr("Show Min And Max", "Perf Graph Detail Level"); - case perf_graph_detail_level::show_one_percent_avg: return tr("Show 1% Low And Average", "Perf Graph Detail Level"); - case perf_graph_detail_level::show_all: return tr("Show All", "Perf Graph Detail Level"); - } - break; - case emu_settings_type::PerfOverlayPosition: - switch (static_cast(index)) - { - case screen_quadrant::top_left: return tr("Top Left", "Performance overlay position"); - case screen_quadrant::top_right: return tr("Top Right", "Performance overlay position"); - case screen_quadrant::bottom_left: return tr("Bottom Left", "Performance overlay position"); - case screen_quadrant::bottom_right: return tr("Bottom Right", "Performance overlay position"); - } - break; - case emu_settings_type::PPUDecoder: - switch (static_cast(index)) - { - case ppu_decoder_type::_static: return tr("Interpreter (Legacy)", "PPU decoder"); - case ppu_decoder_type::llvm_legacy: return tr("LLVM Recompiler (Legacy)", "PPU decoder"); - case ppu_decoder_type::interpreter: return tr("Interpreter", "PPU decoder"); - } - break; - case emu_settings_type::SPUDecoder: - switch (static_cast(index)) - { - case spu_decoder_type::_static: return tr("Interpreter (static)", "SPU decoder"); - case spu_decoder_type::dynamic: return tr("Interpreter (dynamic)", "SPU decoder"); - case spu_decoder_type::asmjit: return tr("Recompiler (ASMJIT)", "SPU decoder"); - case spu_decoder_type::llvm: return tr("Recompiler (LLVM)", "SPU decoder"); - } - break; - case emu_settings_type::EnterButtonAssignment: - switch (static_cast(index)) - { - case enter_button_assign::circle: return tr("Enter with circle", "Enter button assignment"); - case enter_button_assign::cross: return tr("Enter with cross", "Enter button assignment"); - } - break; - case emu_settings_type::AudioFormat: - switch (static_cast(index)) - { - case audio_format::stereo: return tr("Stereo", "Audio format"); - case audio_format::surround_5_1: return tr("Surround 5.1", "Audio format"); - case audio_format::surround_7_1: return tr("Surround 7.1", "Audio format"); - case audio_format::manual: return tr("Manual", "Audio format"); - case audio_format::automatic: return tr("Automatic", "Audio format"); - } - break; - case emu_settings_type::AudioFormats: - switch (static_cast(index)) - { - case audio_format_flag::lpcm_2_48khz: return tr("Linear PCM 2 Ch. 48 kHz", "Audio format flag"); - case audio_format_flag::lpcm_5_1_48khz: return tr("Linear PCM 5.1 Ch. 48 kHz", "Audio format flag"); - case audio_format_flag::lpcm_7_1_48khz: return tr("Linear PCM 7.1 Ch. 48 kHz", "Audio format flag"); - case audio_format_flag::ac3: return tr("Dolby Digital 5.1 Ch.", "Audio format flag"); - case audio_format_flag::dts: return tr("DTS 5.1 Ch.", "Audio format flag"); - } - break; - case emu_settings_type::AudioProvider: - switch (static_cast(index)) - { - case audio_provider::none: return tr("None", "Audio Provider"); - case audio_provider::cell_audio: return tr("CellAudio", "Audio Provider"); - case audio_provider::rsxaudio: return tr("RSXAudio", "Audio Provider"); - } - break; - case emu_settings_type::AudioAvport: - switch (static_cast(index)) - { - case audio_avport::hdmi_0: return tr("HDMI 0", "Audio Avport"); - case audio_avport::hdmi_1: return tr("HDMI 1", "Audio Avport"); - case audio_avport::avmulti: return tr("AV multiout", "Audio Avport"); - case audio_avport::spdif_0: return tr("SPDIF 0", "Audio Avport"); - case audio_avport::spdif_1: return tr("SPDIF 1", "Audio Avport"); - } - break; - case emu_settings_type::AudioChannelLayout: - switch (static_cast(index)) - { - case audio_channel_layout::automatic: return tr("Auto", "Audio Channel Layout"); - case audio_channel_layout::mono: return tr("Mono", "Audio Channel Layout"); - case audio_channel_layout::stereo: return tr("Stereo", "Audio Channel Layout"); - case audio_channel_layout::stereo_lfe: return tr("Stereo LFE", "Audio Channel Layout"); - case audio_channel_layout::quadraphonic: return tr("Quadraphonic", "Audio Channel Layout"); - case audio_channel_layout::quadraphonic_lfe: return tr("Quadraphonic LFE", "Audio Channel Layout"); - case audio_channel_layout::surround_5_1: return tr("Surround 5.1", "Audio Channel Layout"); - case audio_channel_layout::surround_7_1: return tr("Surround 7.1", "Audio Channel Layout"); - } - break; - case emu_settings_type::LicenseArea: - switch (static_cast(index)) - { - case CellSysutilLicenseArea::CELL_SYSUTIL_LICENSE_AREA_J: return tr("Japan", "License Area"); - case CellSysutilLicenseArea::CELL_SYSUTIL_LICENSE_AREA_A: return tr("America", "License Area"); - case CellSysutilLicenseArea::CELL_SYSUTIL_LICENSE_AREA_E: return tr("Europe, Oceania, Middle East, Russia", "License Area"); - case CellSysutilLicenseArea::CELL_SYSUTIL_LICENSE_AREA_H: return tr("Southeast Asia", "License Area"); - case CellSysutilLicenseArea::CELL_SYSUTIL_LICENSE_AREA_K: return tr("Korea", "License Area"); - case CellSysutilLicenseArea::CELL_SYSUTIL_LICENSE_AREA_C: return tr("China", "License Area"); - case CellSysutilLicenseArea::CELL_SYSUTIL_LICENSE_AREA_OTHER: return tr("Other", "License Area"); - } - break; - case emu_settings_type::VulkanAsyncSchedulerDriver: - switch (static_cast(index)) - { - case vk_gpu_scheduler_mode::safe: return tr("Safe", "Asynchronous Queue Scheduler"); - case vk_gpu_scheduler_mode::fast: return tr("Fast", "Asynchronous Queue Scheduler"); - } - break; - case emu_settings_type::Language: - switch (static_cast(index)) - { - case CELL_SYSUTIL_LANG_JAPANESE: return tr("Japanese", "System Language"); - case CELL_SYSUTIL_LANG_ENGLISH_US: return tr("English (US)", "System Language"); - case CELL_SYSUTIL_LANG_FRENCH: return tr("French", "System Language"); - case CELL_SYSUTIL_LANG_SPANISH: return tr("Spanish", "System Language"); - case CELL_SYSUTIL_LANG_GERMAN: return tr("German", "System Language"); - case CELL_SYSUTIL_LANG_ITALIAN: return tr("Italian", "System Language"); - case CELL_SYSUTIL_LANG_DUTCH: return tr("Dutch", "System Language"); - case CELL_SYSUTIL_LANG_PORTUGUESE_PT: return tr("Portuguese (Portugal)", "System Language"); - case CELL_SYSUTIL_LANG_RUSSIAN: return tr("Russian", "System Language"); - case CELL_SYSUTIL_LANG_KOREAN: return tr("Korean", "System Language"); - case CELL_SYSUTIL_LANG_CHINESE_T: return tr("Chinese (Traditional)", "System Language"); - case CELL_SYSUTIL_LANG_CHINESE_S: return tr("Chinese (Simplified)", "System Language"); - case CELL_SYSUTIL_LANG_FINNISH: return tr("Finnish", "System Language"); - case CELL_SYSUTIL_LANG_SWEDISH: return tr("Swedish", "System Language"); - case CELL_SYSUTIL_LANG_DANISH: return tr("Danish", "System Language"); - case CELL_SYSUTIL_LANG_NORWEGIAN: return tr("Norwegian", "System Language"); - case CELL_SYSUTIL_LANG_POLISH: return tr("Polish", "System Language"); - case CELL_SYSUTIL_LANG_ENGLISH_GB: return tr("English (UK)", "System Language"); - case CELL_SYSUTIL_LANG_PORTUGUESE_BR: return tr("Portuguese (Brazil)", "System Language"); - case CELL_SYSUTIL_LANG_TURKISH: return tr("Turkish", "System Language"); - default: - break; - } - break; - case emu_settings_type::KeyboardType: - switch (static_cast(index)) - { - case CELL_KB_MAPPING_101: return tr("English keyboard (US standard)", "Keyboard Type"); - case CELL_KB_MAPPING_106: return tr("Japanese keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_106_KANA: return tr("Japanese keyboard (Kana state)", "Keyboard Type"); - case CELL_KB_MAPPING_GERMAN_GERMANY: return tr("German keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_SPANISH_SPAIN: return tr("Spanish keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_FRENCH_FRANCE: return tr("French keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_ITALIAN_ITALY: return tr("Italian keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_DUTCH_NETHERLANDS: return tr("Dutch keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_PORTUGUESE_PORTUGAL: return tr("Portuguese keyboard (Portugal)", "Keyboard Type"); - case CELL_KB_MAPPING_RUSSIAN_RUSSIA: return tr("Russian keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_ENGLISH_UK: return tr("English keyboard (UK standard)", "Keyboard Type"); - case CELL_KB_MAPPING_KOREAN_KOREA: return tr("Korean keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_NORWEGIAN_NORWAY: return tr("Norwegian keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_FINNISH_FINLAND: return tr("Finnish keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_DANISH_DENMARK: return tr("Danish keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_SWEDISH_SWEDEN: return tr("Swedish keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_CHINESE_TRADITIONAL: return tr("Chinese keyboard (Traditional)", "Keyboard Type"); - case CELL_KB_MAPPING_CHINESE_SIMPLIFIED: return tr("Chinese keyboard (Simplified)", "Keyboard Type"); - case CELL_KB_MAPPING_SWISS_FRENCH_SWITZERLAND: return tr("French keyboard (Switzerland)", "Keyboard Type"); - case CELL_KB_MAPPING_SWISS_GERMAN_SWITZERLAND: return tr("German keyboard (Switzerland)", "Keyboard Type"); - case CELL_KB_MAPPING_CANADIAN_FRENCH_CANADA: return tr("French keyboard (Canada)", "Keyboard Type"); - case CELL_KB_MAPPING_BELGIAN_BELGIUM: return tr("French keyboard (Belgium)", "Keyboard Type"); - case CELL_KB_MAPPING_POLISH_POLAND: return tr("Polish keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_PORTUGUESE_BRAZIL: return tr("Portuguese keyboard (Brazil)", "Keyboard Type"); - case CELL_KB_MAPPING_TURKISH_TURKEY: return tr("Turkish keyboard", "Keyboard Type"); - } - break; - case emu_settings_type::ExclusiveFullscreenMode: - switch (static_cast(index)) - { - case vk_exclusive_fs_mode::unspecified: return tr("Automatic (Default)", "Exclusive Fullscreen Mode"); - case vk_exclusive_fs_mode::disable: return tr("Prefer borderless fullscreen", "Exclusive Fullscreen Mode"); - case vk_exclusive_fs_mode::enable: return tr("Prefer exclusive fullscreen", "Exclusive Fullscreen Mode"); - } - break; - case emu_settings_type::StereoRenderMode: - switch (static_cast(index)) - { - case stereo_render_mode_options::disabled: return tr("Disabled", "3D Display Mode"); - case stereo_render_mode_options::side_by_side: return tr("Side-by-side", "3D Display Mode"); - case stereo_render_mode_options::over_under: return tr("Over-under", "3D Display Mode"); - case stereo_render_mode_options::interlaced: return tr("Interlaced", "3D Display Mode"); - case stereo_render_mode_options::anaglyph_red_green: return tr("Anaglyph Red-Green", "3D Display Mode"); - case stereo_render_mode_options::anaglyph_red_blue: return tr("Anaglyph Red-Blue", "3D Display Mode"); - case stereo_render_mode_options::anaglyph_red_cyan: return tr("Anaglyph Red-Cyan", "3D Display Mode"); - case stereo_render_mode_options::anaglyph_magenta_cyan: return tr("Anaglyph Magenta-Cyan", "3D Display Mode"); - case stereo_render_mode_options::anaglyph_trioscopic: return tr("Anaglyph Green-Magenta (Trioscopic)", "3D Display Mode"); - case stereo_render_mode_options::anaglyph_amber_blue: return tr("Anaglyph Amber-Blue (ColorCode 3D)", "3D Display Mode"); - } - break; - case emu_settings_type::MidiDevices: - switch (static_cast(index)) - { - case midi_device_type::guitar: return tr("Guitar (17 frets)", "Midi Device Type"); - case midi_device_type::guitar_22fret: return tr("Guitar (22 frets)", "Midi Device Type"); - case midi_device_type::keyboard: return tr("Keyboard", "Midi Device Type"); - case midi_device_type::drums: return tr("Drums", "Midi Device Type"); - } - break; - case emu_settings_type::XFloatAccuracy: - switch (static_cast(index)) - { - case xfloat_accuracy::accurate: return tr("Accurate XFloat"); - case xfloat_accuracy::approximate: return tr("Approximate XFloat"); - case xfloat_accuracy::relaxed: return tr("Relaxed XFloat"); - case xfloat_accuracy::inaccurate: return tr("Inaccurate XFloat"); - } - break; - default: - break; - } - - // if (strict) - // { - // std::string type_string; - // if (const auto it = settings_location.find(type); it != settings_location.cend()) - // { - // for (const char* loc : it->second) - // { - // if (!type_string.empty()) - // type_string += ": "; - // type_string += loc; - // } - // } - // fmt::throw_exception("Missing translation for emu setting (original=%s, type='%s'=%d, index=%d)", original, type_string.empty() ? "?" : type_string, static_cast(type), index); - // } - - return original; -} - -std::string emu_settings::GetLocalizedSetting(const std::string& original, emu_settings_type type, int index, bool strict) const -{ - return GetLocalizedSetting(QString::fromStdString(original), type, index, strict).toStdString(); -} - -std::string emu_settings::GetLocalizedSetting(const cfg::_base* node, u32 index) const -{ - const emu_settings_type type = FindSettingsType(node); - const std::vector settings = GetSettingOptions(type); - return GetLocalizedSetting(::at32(settings, index), type, index, true); -} diff --git a/rpcs3qt-legacy/emu_settings.h b/rpcs3qt-legacy/emu_settings.h deleted file mode 100644 index 8ecb25afd..000000000 --- a/rpcs3qt-legacy/emu_settings.h +++ /dev/null @@ -1,130 +0,0 @@ -#pragma once - -#include "util/types.hpp" -#include "util/yaml.hpp" - -#include "microphone_creator.h" -#include "midi_creator.h" -#include "render_creator.h" -#include "emu_settings_type.h" - -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace cfg -{ - class _base; -} - -constexpr auto qstr = QString::fromStdString; - -class emu_settings : public QObject -{ - /** A settings class for Emulator specific settings. This class is a refactored version of the wx version. It is much nicer - * - */ - Q_OBJECT -public: - std::set m_broken_types; // list of broken settings - - /** Creates a settings object which reads in the config.yml file at rpcs3/bin/%path%/config.yml - * Settings are only written when SaveSettings is called. - */ - emu_settings(); - - bool Init(); - - /** Connects a combo box with the target settings type*/ - void EnhanceComboBox(QComboBox* combobox, emu_settings_type type, bool is_ranged = false, bool use_max = false, int max = 0, bool sorted = false, bool strict = true); - - /** Connects a check box with the target settings type*/ - void EnhanceCheckBox(QCheckBox* checkbox, emu_settings_type type); - - /** Connects a date time edit box with the target settings type*/ - void EnhanceDateTimeEdit(QDateTimeEdit* date_time_edit, emu_settings_type type, const QString& format, bool use_calendar, bool as_offset_from_now, int offset_update_time = 0); - - /** Connects a slider with the target settings type*/ - void EnhanceSlider(QSlider* slider, emu_settings_type type); - - /** Connects an integer spin box with the target settings type*/ - void EnhanceSpinBox(QSpinBox* spinbox, emu_settings_type type, const QString& prefix = "", const QString& suffix = ""); - - /** Connects a double spin box with the target settings type*/ - void EnhanceDoubleSpinBox(QDoubleSpinBox* spinbox, emu_settings_type type, const QString& prefix = "", const QString& suffix = ""); - - /** Connects a line edit with the target settings type*/ - void EnhanceLineEdit(QLineEdit* edit, emu_settings_type type); - - /** Connects a button group with the target settings type*/ - void EnhanceRadioButton(QButtonGroup* button_group, emu_settings_type type); - - std::vector GetLibrariesControl(); - void SaveSelectedLibraries(const std::vector& libs); - - /** Returns the valid options for a given setting.*/ - static std::vector GetSettingOptions(emu_settings_type type); - - /** Returns the valid options for a given setting.*/ - static QStringList GetQStringSettingOptions(emu_settings_type type); - - /** Returns the default value of the setting type.*/ - std::string GetSettingDefault(emu_settings_type type) const; - - /** Returns the value of the setting type.*/ - std::string GetSetting(emu_settings_type type) const; - - /** Sets the setting type to a given value.*/ - void SetSetting(emu_settings_type type, const std::string& val) const; - - /** Try to find the settings type for a given string.*/ - emu_settings_type FindSettingsType(const cfg::_base* node) const; - - /** Gets all the renderer info for gpu settings.*/ - render_creator* m_render_creator = nullptr; - - /** Gets a list of all the microphones available.*/ - microphone_creator m_microphone_creator; - - /** Gets a list of all the midi devices available.*/ - midi_creator m_midi_creator; - - /** Loads the settings from path.*/ - void LoadSettings(const std::string& title_id = "", bool create_config_from_global = true); - - /** Fixes all registered invalid settings after asking the user for permission.*/ - void OpenCorrectionDialog(QWidget* parent = Q_NULLPTR); - - /** Get a localized and therefore freely adjustable version of the string used in config.yml.*/ - QString GetLocalizedSetting(const QString& original, emu_settings_type type, int index, bool strict) const; - - /** Get a localized and therefore freely adjustable version of the string used in config.yml.*/ - std::string GetLocalizedSetting(const std::string& original, emu_settings_type type, int index, bool strict) const; - - /** Get a localized and therefore freely adjustable version of the string used in config.yml.*/ - std::string GetLocalizedSetting(const cfg::_base* node, u32 index) const; - - /** Validates the settings and logs unused entries or cleans up the yaml*/ - bool ValidateSettings(bool cleanup); - - /** Resets the current settings to the global default. This includes all connected widgets. */ - void RestoreDefaults(); - -Q_SIGNALS: - void RestoreDefaultsSignal(); - -public Q_SLOTS: - /** Writes the unsaved settings to file. Used in settings dialog on accept.*/ - void SaveSettings() const; - -private: - YAML::Node m_default_settings; // The default settings as a YAML node. - YAML::Node m_current_settings; // The current settings as a YAML node. - std::string m_title_id; -}; diff --git a/rpcs3qt-legacy/emu_settings_type.h b/rpcs3qt-legacy/emu_settings_type.h deleted file mode 100644 index 22895042f..000000000 --- a/rpcs3qt-legacy/emu_settings_type.h +++ /dev/null @@ -1,414 +0,0 @@ -#pragma once - -#include -#include - -// Node location -using cfg_location = std::vector; - -enum class emu_settings_type -{ - // Core - PPUDecoder, - SPUDecoder, - HookStaticFuncs, - ThreadSchedulerMode, - SPULoopDetection, - PreferredSPUThreads, - PPUDebug, - SPUDebug, - MFCDebug, - MaxLLVMThreads, - LLVMPrecompilation, - EnableTSX, - AccurateSpuDMA, - AccurateClineStores, - AccurateRSXAccess, - FIFOAccuracy, - XFloatAccuracy, - AccuratePPU128Loop, - MFCCommandsShuffling, - NumPPUThreads, - SetDAZandFTZ, - SPUBlockSize, - SPUCache, - DebugConsoleMode, - SilenceAllLogs, - SuspendEmulationSavestateMode, - CompatibleEmulationSavestateMode, - StartSavestatePaused, - MaxSPURSThreads, - SleepTimersAccuracy, - ClocksScale, - PerformanceReport, - FullWidthAVX512, - PPUNJFixup, - AccurateDFMA, - AccuratePPUSAT, - AccuratePPUNJ, - FixupPPUVNAN, - AccuratePPUVNAN, - AccuratePPUFPCC, - MaxPreemptCount, - SPUProfiler, - DisableSpinOptimization, - - // Graphics - Renderer, - Resolution, - AspectRatio, - FrameLimit, - MSAA, - LogShaderPrograms, - WriteDepthBuffer, - WriteColorBuffers, - ReadColorBuffers, - ReadDepthBuffer, - HandleRSXTiledMemory, - VSync, - DebugOutput, - DebugOverlay, - RenderdocCompatibility, - GPUTextureScaling, - StretchToDisplayArea, - VulkanAdapter, - ForceHighpZ, - StrictRenderingMode, - DisableVertexCache, - DisableOcclusionQueries, - DisableVideoOutput, - DisableFIFOReordering, - StrictTextureFlushing, - ShaderPrecisionQuality, - StereoRenderMode, - AnisotropicFilterOverride, - TextureLodBias, - ResolutionScale, - MinimumScalableDimension, - FsrSharpeningStrength, - ExclusiveFullscreenMode, - ForceCPUBlitEmulation, - DisableOnDiskShaderCache, - DisableVulkanMemAllocator, - ShaderMode, - ShaderCompilerNumThreads, - MultithreadedRSX, - VBlankRate, - VBlankNTSCFixup, - RelaxedZCULL, - PreciseZCULL, - DriverWakeUpDelay, - VulkanAsyncTextureUploads, - VulkanAsyncSchedulerDriver, - AllowHostGPULabels, - DisableMSLFastMath, - OutputScalingMode, - ForceHwMSAAResolve, - DisableAsyncHostMM, - - // Performance Overlay - PerfOverlayEnabled, - PerfOverlayFramerateGraphEnabled, - PerfOverlayFrametimeGraphEnabled, - PerfOverlayFramerateDatapoints, - PerfOverlayFrametimeDatapoints, - PerfOverlayDetailLevel, - PerfOverlayFramerateDetailLevel, - PerfOverlayFrametimeDetailLevel, - PerfOverlayPosition, - PerfOverlayUpdateInterval, - PerfOverlayFontSize, - PerfOverlayOpacity, - PerfOverlayMarginX, - PerfOverlayMarginY, - PerfOverlayCenterX, - PerfOverlayCenterY, - - // Shader Loading Dialog - ShaderLoadBgEnabled, - ShaderLoadBgDarkening, - ShaderLoadBgBlur, - - // Audio - AudioRenderer, - DumpToFile, - ConvertTo16Bit, - AudioFormat, - AudioFormats, - AudioProvider, - AudioAvport, - AudioDevice, - AudioChannelLayout, - MasterVolume, - EnableBuffering, - AudioBufferDuration, - EnableTimeStretching, - TimeStretchingThreshold, - MicrophoneType, - MicrophoneDevices, - MusicHandler, - - // Input / Output - BackgroundInput, - ShowMoveCursor, - LockOvlIptToP1, - PadHandlerMode, - PadConnection, - KeyboardHandler, - MouseHandler, - Camera, - CameraType, - CameraFlip, - CameraID, - Move, - Buzz, - Turntable, - GHLtar, - MidiDevices, - SDLMappings, - IoDebugOverlay, - - // Misc - ExitRPCS3OnFinish, - StartOnBoot, - PauseOnFocusLoss, - StartGameFullscreen, - PreventDisplaySleep, - ShowTrophyPopups, - ShowRpcnPopups, - UseNativeInterface, - ShowShaderCompilationHint, - ShowPPUCompilationHint, - ShowAutosaveAutoloadHint, - ShowPressureIntensityToggleHint, - ShowAnalogLimiterToggleHint, - ShowMouseAndKeyboardToggleHint, - WindowTitleFormat, - PauseDuringHomeMenu, - - // Network - InternetStatus, - DNSAddress, - IpSwapList, - PSNStatus, - BindAddress, - EnableUpnp, - PSNCountry, - - // System - LicenseArea, - Language, - KeyboardType, - EnterButtonAssignment, - EnableHostRoot, - EmptyHdd0Tmp, - LimitCacheSize, - MaximumCacheSize, - ConsoleTimeOffset, -}; - -/** A helper map that keeps track of where a given setting type is located*/ -inline static const std::map settings_location = - { - // Core Tab - {emu_settings_type::PPUDecoder, {"Core", "PPU Decoder"}}, - {emu_settings_type::SPUDecoder, {"Core", "SPU Decoder"}}, - {emu_settings_type::HookStaticFuncs, {"Core", "Hook static functions"}}, - {emu_settings_type::ThreadSchedulerMode, {"Core", "Thread Scheduler Mode"}}, - {emu_settings_type::SPULoopDetection, {"Core", "SPU loop detection"}}, - {emu_settings_type::PreferredSPUThreads, {"Core", "Preferred SPU Threads"}}, - {emu_settings_type::PPUDebug, {"Core", "PPU Debug"}}, - {emu_settings_type::SPUDebug, {"Core", "SPU Debug"}}, - {emu_settings_type::MFCDebug, {"Core", "MFC Debug"}}, - {emu_settings_type::MaxLLVMThreads, {"Core", "Max LLVM Compile Threads"}}, - {emu_settings_type::LLVMPrecompilation, {"Core", "LLVM Precompilation"}}, - {emu_settings_type::EnableTSX, {"Core", "Enable TSX"}}, - {emu_settings_type::AccurateSpuDMA, {"Core", "Accurate SPU DMA"}}, - {emu_settings_type::AccurateClineStores, {"Core", "Accurate Cache Line Stores"}}, - {emu_settings_type::AccurateRSXAccess, {"Core", "Accurate RSX reservation access"}}, - {emu_settings_type::FIFOAccuracy, {"Core", "RSX FIFO Accuracy"}}, - {emu_settings_type::XFloatAccuracy, {"Core", "XFloat Accuracy"}}, - {emu_settings_type::MFCCommandsShuffling, {"Core", "MFC Commands Shuffling Limit"}}, - {emu_settings_type::SetDAZandFTZ, {"Core", "Set DAZ and FTZ"}}, - {emu_settings_type::SPUBlockSize, {"Core", "SPU Block Size"}}, - {emu_settings_type::SPUCache, {"Core", "SPU Cache"}}, - {emu_settings_type::DebugConsoleMode, {"Core", "Debug Console Mode"}}, - {emu_settings_type::MaxSPURSThreads, {"Core", "Max SPURS Threads"}}, - {emu_settings_type::SleepTimersAccuracy, {"Core", "Sleep Timers Accuracy"}}, - {emu_settings_type::ClocksScale, {"Core", "Clocks scale"}}, - {emu_settings_type::AccuratePPU128Loop, {"Core", "Accurate PPU 128-byte Reservation Op Max Length"}}, - {emu_settings_type::PerformanceReport, {"Core", "Enable Performance Report"}}, - {emu_settings_type::FullWidthAVX512, {"Core", "Full Width AVX-512"}}, - {emu_settings_type::NumPPUThreads, {"Core", "PPU Threads"}}, - {emu_settings_type::PPUNJFixup, {"Core", "PPU LLVM Java Mode Handling"}}, - {emu_settings_type::AccurateDFMA, {"Core", "Use Accurate DFMA"}}, - {emu_settings_type::AccuratePPUSAT, {"Core", "PPU Set Saturation Bit"}}, - {emu_settings_type::AccuratePPUNJ, {"Core", "PPU Accurate Non-Java Mode"}}, - {emu_settings_type::FixupPPUVNAN, {"Core", "PPU Fixup Vector NaN Values"}}, - {emu_settings_type::AccuratePPUVNAN, {"Core", "PPU Accurate Vector NaN Values"}}, - {emu_settings_type::AccuratePPUFPCC, {"Core", "PPU Set FPCC Bits"}}, - {emu_settings_type::MaxPreemptCount, {"Core", "Max CPU Preempt Count"}}, - {emu_settings_type::SPUProfiler, {"Core", "SPU Profiler"}}, - {emu_settings_type::DisableSpinOptimization, {"Core", "Disable SPU GETLLAR Spin Optimization"}}, - - // Graphics Tab - {emu_settings_type::Renderer, {"Video", "Renderer"}}, - {emu_settings_type::Resolution, {"Video", "Resolution"}}, - {emu_settings_type::AspectRatio, {"Video", "Aspect ratio"}}, - {emu_settings_type::FrameLimit, {"Video", "Frame limit"}}, - {emu_settings_type::MSAA, {"Video", "MSAA"}}, - {emu_settings_type::LogShaderPrograms, {"Video", "Log shader programs"}}, - {emu_settings_type::WriteDepthBuffer, {"Video", "Write Depth Buffer"}}, - {emu_settings_type::WriteColorBuffers, {"Video", "Write Color Buffers"}}, - {emu_settings_type::ReadColorBuffers, {"Video", "Read Color Buffers"}}, - {emu_settings_type::ReadDepthBuffer, {"Video", "Read Depth Buffer"}}, - {emu_settings_type::HandleRSXTiledMemory, {"Video", "Handle RSX Memory Tiling"}}, - {emu_settings_type::VSync, {"Video", "VSync"}}, - {emu_settings_type::DebugOutput, {"Video", "Debug output"}}, - {emu_settings_type::DebugOverlay, {"Video", "Debug overlay"}}, - {emu_settings_type::RenderdocCompatibility, {"Video", "Renderdoc Compatibility Mode"}}, - {emu_settings_type::GPUTextureScaling, {"Video", "Use GPU texture scaling"}}, - {emu_settings_type::StretchToDisplayArea, {"Video", "Stretch To Display Area"}}, - {emu_settings_type::ForceHighpZ, {"Video", "Force High Precision Z buffer"}}, - {emu_settings_type::StrictRenderingMode, {"Video", "Strict Rendering Mode"}}, - {emu_settings_type::DisableVertexCache, {"Video", "Disable Vertex Cache"}}, - {emu_settings_type::DisableOcclusionQueries, {"Video", "Disable ZCull Occlusion Queries"}}, - {emu_settings_type::DisableVideoOutput, {"Video", "Disable Video Output"}}, - {emu_settings_type::DisableFIFOReordering, {"Video", "Disable FIFO Reordering"}}, - {emu_settings_type::StereoRenderMode, {"Video", "3D Display Mode"}}, - {emu_settings_type::StrictTextureFlushing, {"Video", "Strict Texture Flushing"}}, - {emu_settings_type::ForceCPUBlitEmulation, {"Video", "Force CPU Blit"}}, - {emu_settings_type::DisableOnDiskShaderCache, {"Video", "Disable On-Disk Shader Cache"}}, - {emu_settings_type::DisableVulkanMemAllocator, {"Video", "Disable Vulkan Memory Allocator"}}, - {emu_settings_type::ShaderMode, {"Video", "Shader Mode"}}, - {emu_settings_type::ShaderCompilerNumThreads, {"Video", "Shader Compiler Threads"}}, - {emu_settings_type::ShaderPrecisionQuality, {"Video", "Shader Precision"}}, - {emu_settings_type::MultithreadedRSX, {"Video", "Multithreaded RSX"}}, - {emu_settings_type::RelaxedZCULL, {"Video", "Relaxed ZCULL Sync"}}, - {emu_settings_type::PreciseZCULL, {"Video", "Accurate ZCULL stats"}}, - {emu_settings_type::AnisotropicFilterOverride, {"Video", "Anisotropic Filter Override"}}, - {emu_settings_type::TextureLodBias, {"Video", "Texture LOD Bias Addend"}}, - {emu_settings_type::ResolutionScale, {"Video", "Resolution Scale"}}, - {emu_settings_type::MinimumScalableDimension, {"Video", "Minimum Scalable Dimension"}}, - {emu_settings_type::VulkanAdapter, {"Video", "Vulkan", "Adapter"}}, - {emu_settings_type::VBlankRate, {"Video", "Vblank Rate"}}, - {emu_settings_type::VBlankNTSCFixup, {"Video", "Vblank NTSC Fixup"}}, - {emu_settings_type::DriverWakeUpDelay, {"Video", "Driver Wake-Up Delay"}}, - {emu_settings_type::AllowHostGPULabels, {"Video", "Allow Host GPU Labels"}}, - {emu_settings_type::DisableMSLFastMath, {"Video", "Disable MSL Fast Math"}}, - {emu_settings_type::OutputScalingMode, {"Video", "Output Scaling Mode"}}, - {emu_settings_type::ForceHwMSAAResolve, {"Video", "Force Hardware MSAA Resolve"}}, - {emu_settings_type::DisableAsyncHostMM, {"Video", "Disable Asynchronous Memory Manager"}}, - - // Vulkan - {emu_settings_type::VulkanAsyncTextureUploads, {"Video", "Vulkan", "Asynchronous Texture Streaming 2"}}, - {emu_settings_type::VulkanAsyncSchedulerDriver, {"Video", "Vulkan", "Asynchronous Queue Scheduler"}}, - {emu_settings_type::FsrSharpeningStrength, {"Video", "Vulkan", "FidelityFX CAS Sharpening Intensity"}}, - {emu_settings_type::ExclusiveFullscreenMode, {"Video", "Vulkan", "Exclusive Fullscreen Mode"}}, - - // Performance Overlay - {emu_settings_type::PerfOverlayEnabled, {"Video", "Performance Overlay", "Enabled"}}, - {emu_settings_type::PerfOverlayFramerateGraphEnabled, {"Video", "Performance Overlay", "Enable Framerate Graph"}}, - {emu_settings_type::PerfOverlayFrametimeGraphEnabled, {"Video", "Performance Overlay", "Enable Frametime Graph"}}, - {emu_settings_type::PerfOverlayFramerateDatapoints, {"Video", "Performance Overlay", "Framerate datapoints"}}, - {emu_settings_type::PerfOverlayFrametimeDatapoints, {"Video", "Performance Overlay", "Frametime datapoints"}}, - {emu_settings_type::PerfOverlayDetailLevel, {"Video", "Performance Overlay", "Detail level"}}, - {emu_settings_type::PerfOverlayFramerateDetailLevel, {"Video", "Performance Overlay", "Framerate graph detail level"}}, - {emu_settings_type::PerfOverlayFrametimeDetailLevel, {"Video", "Performance Overlay", "Frametime graph detail level"}}, - {emu_settings_type::PerfOverlayPosition, {"Video", "Performance Overlay", "Position"}}, - {emu_settings_type::PerfOverlayUpdateInterval, {"Video", "Performance Overlay", "Metrics update interval (ms)"}}, - {emu_settings_type::PerfOverlayFontSize, {"Video", "Performance Overlay", "Font size (px)"}}, - {emu_settings_type::PerfOverlayOpacity, {"Video", "Performance Overlay", "Opacity (%)"}}, - {emu_settings_type::PerfOverlayMarginX, {"Video", "Performance Overlay", "Horizontal Margin (px)"}}, - {emu_settings_type::PerfOverlayMarginY, {"Video", "Performance Overlay", "Vertical Margin (px)"}}, - {emu_settings_type::PerfOverlayCenterX, {"Video", "Performance Overlay", "Center Horizontally"}}, - {emu_settings_type::PerfOverlayCenterY, {"Video", "Performance Overlay", "Center Vertically"}}, - - // Shader Loading Dialog - {emu_settings_type::ShaderLoadBgEnabled, {"Video", "Shader Loading Dialog", "Allow custom background"}}, - {emu_settings_type::ShaderLoadBgDarkening, {"Video", "Shader Loading Dialog", "Darkening effect strength"}}, - {emu_settings_type::ShaderLoadBgBlur, {"Video", "Shader Loading Dialog", "Blur effect strength"}}, - - // Audio - {emu_settings_type::AudioRenderer, {"Audio", "Renderer"}}, - {emu_settings_type::DumpToFile, {"Audio", "Dump to file"}}, - {emu_settings_type::ConvertTo16Bit, {"Audio", "Convert to 16 bit"}}, - {emu_settings_type::AudioFormat, {"Audio", "Audio Format"}}, - {emu_settings_type::AudioFormats, {"Audio", "Audio Formats"}}, - {emu_settings_type::AudioProvider, {"Audio", "Audio Provider"}}, - {emu_settings_type::AudioAvport, {"Audio", "RSXAudio Avport"}}, - {emu_settings_type::AudioDevice, {"Audio", "Audio Device"}}, - {emu_settings_type::AudioChannelLayout, {"Audio", "Audio Channel Layout"}}, - {emu_settings_type::MasterVolume, {"Audio", "Master Volume"}}, - {emu_settings_type::EnableBuffering, {"Audio", "Enable Buffering"}}, - {emu_settings_type::AudioBufferDuration, {"Audio", "Desired Audio Buffer Duration"}}, - {emu_settings_type::EnableTimeStretching, {"Audio", "Enable Time Stretching"}}, - {emu_settings_type::TimeStretchingThreshold, {"Audio", "Time Stretching Threshold"}}, - {emu_settings_type::MicrophoneType, {"Audio", "Microphone Type"}}, - {emu_settings_type::MicrophoneDevices, {"Audio", "Microphone Devices"}}, - {emu_settings_type::MusicHandler, {"Audio", "Music Handler"}}, - - // Input / Output - {emu_settings_type::BackgroundInput, {"Input/Output", "Background input enabled"}}, - {emu_settings_type::ShowMoveCursor, {"Input/Output", "Show move cursor"}}, - {emu_settings_type::LockOvlIptToP1, {"Input/Output", "Lock overlay input to player one"}}, - {emu_settings_type::PadHandlerMode, {"Input/Output", "Pad handler mode"}}, - {emu_settings_type::PadConnection, {"Input/Output", "Keep pads connected"}}, - {emu_settings_type::KeyboardHandler, {"Input/Output", "Keyboard"}}, - {emu_settings_type::MouseHandler, {"Input/Output", "Mouse"}}, - {emu_settings_type::Camera, {"Input/Output", "Camera"}}, - {emu_settings_type::CameraType, {"Input/Output", "Camera type"}}, - {emu_settings_type::CameraFlip, {"Input/Output", "Camera flip"}}, - {emu_settings_type::CameraID, {"Input/Output", "Camera ID"}}, - {emu_settings_type::Move, {"Input/Output", "Move"}}, - {emu_settings_type::Buzz, {"Input/Output", "Buzz emulated controller"}}, - {emu_settings_type::Turntable, {"Input/Output", "Turntable emulated controller"}}, - {emu_settings_type::GHLtar, {"Input/Output", "GHLtar emulated controller"}}, - {emu_settings_type::MidiDevices, {"Input/Output", "Emulated Midi devices"}}, - {emu_settings_type::SDLMappings, {"Input/Output", "Load SDL GameController Mappings"}}, - {emu_settings_type::IoDebugOverlay, {"Input/Output", "IO Debug overlay"}}, - - // Misc - {emu_settings_type::ExitRPCS3OnFinish, {"Miscellaneous", "Exit RPCS3 when process finishes"}}, - {emu_settings_type::StartOnBoot, {"Miscellaneous", "Automatically start games after boot"}}, - {emu_settings_type::PauseOnFocusLoss, {"Miscellaneous", "Pause emulation on RPCS3 focus loss"}}, - {emu_settings_type::StartGameFullscreen, {"Miscellaneous", "Start games in fullscreen mode"}}, - {emu_settings_type::PreventDisplaySleep, {"Miscellaneous", "Prevent display sleep while running games"}}, - {emu_settings_type::ShowTrophyPopups, {"Miscellaneous", "Show trophy popups"}}, - {emu_settings_type::ShowRpcnPopups, {"Miscellaneous", "Show RPCN popups"}}, - {emu_settings_type::UseNativeInterface, {"Miscellaneous", "Use native user interface"}}, - {emu_settings_type::ShowShaderCompilationHint, {"Miscellaneous", "Show shader compilation hint"}}, - {emu_settings_type::ShowPPUCompilationHint, {"Miscellaneous", "Show PPU compilation hint"}}, - {emu_settings_type::ShowAutosaveAutoloadHint, {"Miscellaneous", "Show autosave/autoload hint"}}, - {emu_settings_type::ShowPressureIntensityToggleHint, {"Miscellaneous", "Show pressure intensity toggle hint"}}, - {emu_settings_type::ShowAnalogLimiterToggleHint, {"Miscellaneous", "Show analog limiter toggle hint"}}, - {emu_settings_type::ShowMouseAndKeyboardToggleHint, {"Miscellaneous", "Show mouse and keyboard toggle hint"}}, - {emu_settings_type::SilenceAllLogs, {"Miscellaneous", "Silence All Logs"}}, - {emu_settings_type::WindowTitleFormat, {"Miscellaneous", "Window Title Format"}}, - {emu_settings_type::PauseDuringHomeMenu, {"Miscellaneous", "Pause Emulation During Home Menu"}}, - - // Networking - {emu_settings_type::InternetStatus, {"Net", "Internet enabled"}}, - {emu_settings_type::DNSAddress, {"Net", "DNS address"}}, - {emu_settings_type::IpSwapList, {"Net", "IP swap list"}}, - {emu_settings_type::PSNStatus, {"Net", "PSN status"}}, - {emu_settings_type::BindAddress, {"Net", "Bind address"}}, - {emu_settings_type::EnableUpnp, {"Net", "UPNP Enabled"}}, - {emu_settings_type::PSNCountry, {"Net", "PSN Country"}}, - - // System - {emu_settings_type::LicenseArea, {"System", "License Area"}}, - {emu_settings_type::Language, {"System", "Language"}}, - {emu_settings_type::KeyboardType, {"System", "Keyboard Type"}}, - {emu_settings_type::EnterButtonAssignment, {"System", "Enter button assignment"}}, - {emu_settings_type::EnableHostRoot, {"VFS", "Enable /host_root/"}}, - {emu_settings_type::EmptyHdd0Tmp, {"VFS", "Empty /dev_hdd0/tmp/"}}, - {emu_settings_type::LimitCacheSize, {"VFS", "Limit disk cache size"}}, - {emu_settings_type::MaximumCacheSize, {"VFS", "Disk cache maximum size (MB)"}}, - {emu_settings_type::ConsoleTimeOffset, {"System", "Console time offset (s)"}}, - - // Savestates - {emu_settings_type::SuspendEmulationSavestateMode, {"Savestate", "Suspend Emulation Savestate Mode"}}, - {emu_settings_type::CompatibleEmulationSavestateMode, {"Savestate", "Compatible Savestate Mode"}}, - {emu_settings_type::StartSavestatePaused, {"Savestate", "Start Paused"}}, -}; diff --git a/rpcs3qt-legacy/emulated_pad_settings_dialog.cpp b/rpcs3qt-legacy/emulated_pad_settings_dialog.cpp deleted file mode 100644 index 4992298d2..000000000 --- a/rpcs3qt-legacy/emulated_pad_settings_dialog.cpp +++ /dev/null @@ -1,609 +0,0 @@ -#include "stdafx.h" -#include "emulated_pad_settings_dialog.h" -#include "localized_emu.h" -#include "Input/raw_mouse_config.h" -#include "Emu/Io/mouse_config.h" -#include "Emu/Io/buzz_config.h" -#include "Emu/Io/gem_config.h" -#include "Emu/Io/ghltar_config.h" -#include "Emu/Io/guncon3_config.h" -#include "Emu/Io/topshotelite_config.h" -#include "Emu/Io/topshotfearmaster_config.h" -#include "Emu/Io/turntable_config.h" -#include "Emu/Io/usio_config.h" -#include "util/asm.hpp" - -#include -#include -#include -#include -#include -#include - -enum button_role -{ - button = Qt::UserRole, - emulated_button -}; - -emulated_pad_settings_dialog::emulated_pad_settings_dialog(pad_type type, QWidget* parent) - : QDialog(parent), m_type(type) -{ - setObjectName("emulated_pad_settings_dialog"); - setAttribute(Qt::WA_DeleteOnClose); - setAttribute(Qt::WA_StyledBackground); - setModal(true); - - QVBoxLayout* v_layout = new QVBoxLayout(this); - - QTabWidget* tabs = new QTabWidget(); - tabs->setUsesScrollButtons(false); - - QDialogButtonBox* buttons = new QDialogButtonBox(this); - buttons->setStandardButtons(QDialogButtonBox::Apply | QDialogButtonBox::Cancel | QDialogButtonBox::Save | QDialogButtonBox::RestoreDefaults); - - connect(buttons, &QDialogButtonBox::clicked, this, [this, buttons](QAbstractButton* button) - { - if (button == buttons->button(QDialogButtonBox::Apply)) - { - save_config(); - } - else if (button == buttons->button(QDialogButtonBox::Save)) - { - save_config(); - accept(); - } - else if (button == buttons->button(QDialogButtonBox::RestoreDefaults)) - { - if (QMessageBox::question(this, tr("Confirm Reset"), tr("Reset all buttons of all players?")) != QMessageBox::Yes) - return; - reset_config(); - } - else if (button == buttons->button(QDialogButtonBox::Cancel)) - { - // Restore config - load_config(); - reject(); - } - }); - - load_config(); - - switch (m_type) - { - case emulated_pad_settings_dialog::pad_type::buzz: - setWindowTitle(tr("Configure Emulated Buzz")); - add_tabs(tabs); - break; - case emulated_pad_settings_dialog::pad_type::turntable: - setWindowTitle(tr("Configure Emulated Turntable")); - add_tabs(tabs); - break; - case emulated_pad_settings_dialog::pad_type::ghltar: - setWindowTitle(tr("Configure Emulated GHLtar")); - add_tabs(tabs); - break; - case emulated_pad_settings_dialog::pad_type::usio: - setWindowTitle(tr("Configure Emulated USIO")); - add_tabs(tabs); - break; - case emulated_pad_settings_dialog::pad_type::gem: - setWindowTitle(tr("Configure Emulated PS Move (Real)")); - add_tabs(tabs); - break; - case emulated_pad_settings_dialog::pad_type::ds3gem: - setWindowTitle(tr("Configure Emulated PS Move (Fake)")); - add_tabs(tabs); - break; - case emulated_pad_settings_dialog::pad_type::mousegem: - setWindowTitle(tr("Configure Emulated PS Move (Mouse)")); - add_tabs(tabs); - break; - case emulated_pad_settings_dialog::pad_type::guncon3: - setWindowTitle(tr("Configure Emulated GunCon 3")); - add_tabs(tabs); - break; - case emulated_pad_settings_dialog::pad_type::topshotelite: - setWindowTitle(tr("Configure Emulated Top Shot Elite")); - add_tabs(tabs); - break; - case emulated_pad_settings_dialog::pad_type::topshotfearmaster: - setWindowTitle(tr("Configure Emulated Top Shot Fearmaster")); - add_tabs(tabs); - break; - } - - v_layout->addWidget(tabs); - v_layout->addWidget(buttons); - setLayout(v_layout); -} - -template -void emulated_pad_settings_dialog::add_tabs(QTabWidget* tabs) -{ - ensure(!!tabs); - - std::set ignored_values; - - const auto remove_value = [&ignored_values](int value) - { - ignored_values.insert(static_cast(value)); - }; - - usz players = 0; - switch (m_type) - { - case pad_type::buzz: - players = g_cfg_buzz.players.size(); - break; - case pad_type::turntable: - players = g_cfg_turntable.players.size(); - break; - case pad_type::ghltar: - players = g_cfg_ghltar.players.size(); - break; - case pad_type::usio: - players = g_cfg_usio.players.size(); - break; - case pad_type::gem: - players = g_cfg_gem_real.players.size(); - - // Ignore combo, x and y axis - remove_value(static_cast(gem_btn::x_axis)); - remove_value(static_cast(gem_btn::y_axis)); - for (int i = static_cast(gem_btn::combo_begin); i <= static_cast(gem_btn::combo_end); i++) - { - remove_value(i); - } - break; - case pad_type::ds3gem: - players = g_cfg_gem_fake.players.size(); - - // Ignore combo - for (int i = static_cast(gem_btn::combo_begin); i <= static_cast(gem_btn::combo_end); i++) - { - remove_value(i); - } - break; - case pad_type::mousegem: - players = g_cfg_gem_mouse.players.size(); - - // Ignore x and y axis - remove_value(static_cast(gem_btn::x_axis)); - remove_value(static_cast(gem_btn::y_axis)); - break; - case pad_type::guncon3: - players = g_cfg_guncon3.players.size(); - break; - case pad_type::topshotelite: - players = g_cfg_topshotelite.players.size(); - break; - case pad_type::topshotfearmaster: - players = g_cfg_topshotfearmaster.players.size(); - break; - } - - constexpr u32 max_items_per_column = 6; - const int count = static_cast(T::count) - static_cast(ignored_values.size()); - int rows = count; - - for (u32 cols = 1; utils::aligned_div(static_cast(count), cols) > max_items_per_column;) - { - rows = utils::aligned_div(static_cast(count), ++cols); - } - - m_combos.resize(players); - - const bool show_mouse_legend = m_type == pad_type::mousegem; - - if (show_mouse_legend) - { - if (!g_cfg_mouse.load()) - { - cfg_log.notice("Could not restore mouse config. Using defaults."); - } - - if (!g_cfg_raw_mouse.load()) - { - cfg_log.notice("Could not restore raw mouse config. Using defaults."); - } - } - - for (usz player = 0; player < players; player++) - { - // Create grid with all buttons - QGridLayout* grid_layout = new QGridLayout(this); - - for (int i = 0, row = 0, col = 0; i < static_cast(T::count); i++) - { - if (ignored_values.contains(i)) - continue; - - const T id = static_cast(i); - const QString name = QString::fromStdString(fmt::format("%s", id)); - - QHBoxLayout* h_layout = new QHBoxLayout(this); - QGroupBox* gb = new QGroupBox(name, this); - QComboBox* combo = new QComboBox; - - if constexpr (std::is_same_v) - { - const gem_btn btn = static_cast(i); - if (btn >= gem_btn::combo_begin && btn <= gem_btn::combo_end) - { - gb->setToolTip(tr("Press the \"Combo\" button in combination with any of the other combo buttons to trigger their related PS Move button.\n" - "This can be useful if your device does not have enough regular buttons.")); - } - } - - // Add empty value - combo->addItem(""); - const int index = combo->findText(""); - combo->setItemData(index, static_cast(pad_button::pad_button_max_enum), button_role::button); - combo->setItemData(index, i, button_role::emulated_button); - - if (m_type != pad_type::mousegem) - { - for (int p = 0; p < static_cast(pad_button::pad_button_max_enum); p++) - { - const QString translated = localized_emu::translated_pad_button(static_cast(p)); - combo->addItem(translated); - const int index = combo->findText(translated); - combo->setItemData(index, p, button_role::button); - combo->setItemData(index, i, button_role::emulated_button); - } - } - - if (std::is_same_v || std::is_same_v || std::is_same_v || m_type == pad_type::mousegem) - { - for (int p = static_cast(pad_button::mouse_button_1); p <= static_cast(pad_button::mouse_button_8); p++) - { - const QString translated = localized_emu::translated_pad_button(static_cast(p)); - combo->addItem(translated); - const int index = combo->findText(translated); - combo->setItemData(index, p, button_role::button); - combo->setItemData(index, i, button_role::emulated_button); - } - } - - pad_button saved_btn_id = pad_button::pad_button_max_enum; - switch (m_type) - { - case pad_type::buzz: - saved_btn_id = ::at32(g_cfg_buzz.players, player)->get_pad_button(static_cast(id)); - break; - case pad_type::turntable: - saved_btn_id = ::at32(g_cfg_turntable.players, player)->get_pad_button(static_cast(id)); - break; - case pad_type::ghltar: - saved_btn_id = ::at32(g_cfg_ghltar.players, player)->get_pad_button(static_cast(id)); - break; - case pad_type::usio: - saved_btn_id = ::at32(g_cfg_usio.players, player)->get_pad_button(static_cast(id)); - break; - case pad_type::gem: - saved_btn_id = ::at32(g_cfg_gem_real.players, player)->get_pad_button(static_cast(id)); - break; - case pad_type::ds3gem: - saved_btn_id = ::at32(g_cfg_gem_fake.players, player)->get_pad_button(static_cast(id)); - break; - case pad_type::mousegem: - saved_btn_id = ::at32(g_cfg_gem_mouse.players, player)->get_pad_button(static_cast(id)); - break; - case pad_type::guncon3: - saved_btn_id = ::at32(g_cfg_guncon3.players, player)->get_pad_button(static_cast(id)); - break; - case pad_type::topshotelite: - saved_btn_id = ::at32(g_cfg_topshotelite.players, player)->get_pad_button(static_cast(id)); - break; - case pad_type::topshotfearmaster: - saved_btn_id = ::at32(g_cfg_topshotfearmaster.players, player)->get_pad_button(static_cast(id)); - break; - } - - combo->setCurrentIndex(combo->findData(static_cast(saved_btn_id))); - - connect(combo, QOverload::of(&QComboBox::currentIndexChanged), this, [this, player, id, combo](int index) - { - if (index < 0 || !combo) - return; - - const QVariant data = combo->itemData(index, button_role::button); - if (!data.isValid() || !data.canConvert()) - return; - - const pad_button btn_id = static_cast(data.toInt()); - - switch (m_type) - { - case pad_type::buzz: - ::at32(g_cfg_buzz.players, player)->set_button(static_cast(id), btn_id); - break; - case pad_type::turntable: - ::at32(g_cfg_turntable.players, player)->set_button(static_cast(id), btn_id); - break; - case pad_type::ghltar: - ::at32(g_cfg_ghltar.players, player)->set_button(static_cast(id), btn_id); - break; - case pad_type::usio: - ::at32(g_cfg_usio.players, player)->set_button(static_cast(id), btn_id); - break; - case pad_type::gem: - ::at32(g_cfg_gem_real.players, player)->set_button(static_cast(id), btn_id); - break; - case pad_type::ds3gem: - ::at32(g_cfg_gem_fake.players, player)->set_button(static_cast(id), btn_id); - break; - case pad_type::mousegem: - ::at32(g_cfg_gem_mouse.players, player)->set_button(static_cast(id), btn_id); - break; - case pad_type::guncon3: - ::at32(g_cfg_guncon3.players, player)->set_button(static_cast(id), btn_id); - break; - case pad_type::topshotelite: - ::at32(g_cfg_topshotelite.players, player)->set_button(static_cast(id), btn_id); - break; - case pad_type::topshotfearmaster: - ::at32(g_cfg_topshotfearmaster.players, player)->set_button(static_cast(id), btn_id); - break; - } - }); - - if (row >= rows) - { - row = 0; - col++; - } - - ::at32(m_combos, player).push_back(combo); - h_layout->addWidget(combo); - gb->setLayout(h_layout); - grid_layout->addWidget(gb, row, col); - - row++; - } - - QVBoxLayout* v_layout = new QVBoxLayout(this); - - // Create a legend of the current mouse settings - if (show_mouse_legend) - { - QHBoxLayout* legend_layout = new QHBoxLayout(this); - if (player == 0) - { - std::string basic_mouse_settings; - fmt::append(basic_mouse_settings, "1: %s\n", g_cfg_mouse.mouse_button_1.to_string()); - fmt::append(basic_mouse_settings, "2: %s\n", g_cfg_mouse.mouse_button_2.to_string()); - fmt::append(basic_mouse_settings, "3: %s\n", g_cfg_mouse.mouse_button_3.to_string()); - fmt::append(basic_mouse_settings, "4: %s\n", g_cfg_mouse.mouse_button_4.to_string()); - fmt::append(basic_mouse_settings, "5: %s\n", g_cfg_mouse.mouse_button_5.to_string()); - fmt::append(basic_mouse_settings, "6: %s\n", g_cfg_mouse.mouse_button_6.to_string()); - fmt::append(basic_mouse_settings, "7: %s\n", g_cfg_mouse.mouse_button_7.to_string()); - fmt::append(basic_mouse_settings, "8: %s", g_cfg_mouse.mouse_button_8.to_string()); - - QGroupBox* gb_legend_basic = new QGroupBox(tr("Current Basic Mouse Config"), this); - QVBoxLayout* gb_legend_basic_layout = new QVBoxLayout(this); - gb_legend_basic_layout->addWidget(new QLabel(QString::fromStdString(basic_mouse_settings), this)); - gb_legend_basic->setLayout(gb_legend_basic_layout); - legend_layout->addWidget(gb_legend_basic); - } - { - std::string raw_mouse_settings; - const auto& raw_cfg = *ensure(::at32(g_cfg_raw_mouse.players, player)); - fmt::append(raw_mouse_settings, "1: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_1.to_string())); - fmt::append(raw_mouse_settings, "2: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_2.to_string())); - fmt::append(raw_mouse_settings, "3: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_3.to_string())); - fmt::append(raw_mouse_settings, "4: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_4.to_string())); - fmt::append(raw_mouse_settings, "5: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_5.to_string())); - fmt::append(raw_mouse_settings, "6: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_6.to_string())); - fmt::append(raw_mouse_settings, "7: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_7.to_string())); - fmt::append(raw_mouse_settings, "8: %s", raw_mouse_config::get_button_name(raw_cfg.mouse_button_8.to_string())); - - QGroupBox* gb_legend_raw = new QGroupBox(tr("Current Raw Mouse Config"), this); - QVBoxLayout* gb_legend_raw_layout = new QVBoxLayout(this); - gb_legend_raw_layout->addWidget(new QLabel(QString::fromStdString(raw_mouse_settings), this)); - gb_legend_raw->setLayout(gb_legend_raw_layout); - legend_layout->addWidget(gb_legend_raw); - } - v_layout->addLayout(legend_layout); - } - - v_layout->addLayout(grid_layout); - - QWidget* widget = new QWidget(this); - widget->setLayout(v_layout); - - tabs->addTab(widget, tr("Player %0").arg(player + 1)); - } -} - -void emulated_pad_settings_dialog::load_config() -{ - switch (m_type) - { - case emulated_pad_settings_dialog::pad_type::buzz: - if (!g_cfg_buzz.load()) - { - cfg_log.notice("Could not load buzz config. Using defaults."); - } - break; - case emulated_pad_settings_dialog::pad_type::turntable: - if (!g_cfg_turntable.load()) - { - cfg_log.notice("Could not load turntable config. Using defaults."); - } - break; - case emulated_pad_settings_dialog::pad_type::ghltar: - if (!g_cfg_ghltar.load()) - { - cfg_log.notice("Could not load ghltar config. Using defaults."); - } - break; - case emulated_pad_settings_dialog::pad_type::usio: - if (!g_cfg_usio.load()) - { - cfg_log.notice("Could not load usio config. Using defaults."); - } - break; - case emulated_pad_settings_dialog::pad_type::gem: - if (!g_cfg_gem_real.load()) - { - cfg_log.notice("Could not load gem config. Using defaults."); - } - break; - case emulated_pad_settings_dialog::pad_type::ds3gem: - if (!g_cfg_gem_fake.load()) - { - cfg_log.notice("Could not load fake gem config. Using defaults."); - } - break; - case emulated_pad_settings_dialog::pad_type::mousegem: - if (!g_cfg_gem_mouse.load()) - { - cfg_log.notice("Could not load mouse gem config. Using defaults."); - } - break; - case emulated_pad_settings_dialog::pad_type::guncon3: - if (!g_cfg_guncon3.load()) - { - cfg_log.notice("Could not load guncon3 config. Using defaults."); - } - break; - case emulated_pad_settings_dialog::pad_type::topshotelite: - if (!g_cfg_topshotelite.load()) - { - cfg_log.notice("Could not load topshotelite config. Using defaults."); - } - break; - case emulated_pad_settings_dialog::pad_type::topshotfearmaster: - if (!g_cfg_topshotfearmaster.load()) - { - cfg_log.notice("Could not load topshotfearmaster config. Using defaults."); - } - break; - } -} - -void emulated_pad_settings_dialog::save_config() -{ - switch (m_type) - { - case emulated_pad_settings_dialog::pad_type::buzz: - g_cfg_buzz.save(); - break; - case emulated_pad_settings_dialog::pad_type::turntable: - g_cfg_turntable.save(); - break; - case emulated_pad_settings_dialog::pad_type::ghltar: - g_cfg_ghltar.save(); - break; - case emulated_pad_settings_dialog::pad_type::usio: - g_cfg_usio.save(); - break; - case emulated_pad_settings_dialog::pad_type::gem: - g_cfg_gem_real.save(); - break; - case emulated_pad_settings_dialog::pad_type::ds3gem: - g_cfg_gem_fake.save(); - break; - case emulated_pad_settings_dialog::pad_type::mousegem: - g_cfg_gem_mouse.save(); - break; - case emulated_pad_settings_dialog::pad_type::guncon3: - g_cfg_guncon3.save(); - break; - case emulated_pad_settings_dialog::pad_type::topshotelite: - g_cfg_topshotelite.save(); - break; - case emulated_pad_settings_dialog::pad_type::topshotfearmaster: - g_cfg_topshotfearmaster.save(); - break; - } -} - -void emulated_pad_settings_dialog::reset_config() -{ - switch (m_type) - { - case emulated_pad_settings_dialog::pad_type::buzz: - g_cfg_buzz.from_default(); - break; - case emulated_pad_settings_dialog::pad_type::turntable: - g_cfg_turntable.from_default(); - break; - case emulated_pad_settings_dialog::pad_type::ghltar: - g_cfg_ghltar.from_default(); - break; - case emulated_pad_settings_dialog::pad_type::usio: - g_cfg_usio.from_default(); - break; - case emulated_pad_settings_dialog::pad_type::gem: - g_cfg_gem_real.from_default(); - break; - case emulated_pad_settings_dialog::pad_type::ds3gem: - g_cfg_gem_fake.from_default(); - break; - case emulated_pad_settings_dialog::pad_type::mousegem: - g_cfg_gem_mouse.from_default(); - break; - case emulated_pad_settings_dialog::pad_type::guncon3: - g_cfg_guncon3.from_default(); - break; - case emulated_pad_settings_dialog::pad_type::topshotelite: - g_cfg_topshotelite.from_default(); - break; - case emulated_pad_settings_dialog::pad_type::topshotfearmaster: - g_cfg_topshotfearmaster.from_default(); - break; - } - - for (usz player = 0; player < m_combos.size(); player++) - { - for (QComboBox* combo : m_combos.at(player)) - { - if (!combo) - continue; - - const QVariant data = combo->itemData(0, button_role::emulated_button); - if (!data.isValid() || !data.canConvert()) - continue; - - pad_button def_btn_id = pad_button::pad_button_max_enum; - switch (m_type) - { - case pad_type::buzz: - def_btn_id = ::at32(g_cfg_buzz.players, player)->default_pad_button(static_cast(data.toInt())); - break; - case pad_type::turntable: - def_btn_id = ::at32(g_cfg_turntable.players, player)->default_pad_button(static_cast(data.toInt())); - break; - case pad_type::ghltar: - def_btn_id = ::at32(g_cfg_ghltar.players, player)->default_pad_button(static_cast(data.toInt())); - break; - case pad_type::usio: - def_btn_id = ::at32(g_cfg_usio.players, player)->default_pad_button(static_cast(data.toInt())); - break; - case pad_type::gem: - def_btn_id = ::at32(g_cfg_gem_real.players, player)->default_pad_button(static_cast(data.toInt())); - break; - case pad_type::ds3gem: - def_btn_id = ::at32(g_cfg_gem_fake.players, player)->default_pad_button(static_cast(data.toInt())); - break; - case pad_type::mousegem: - def_btn_id = ::at32(g_cfg_gem_mouse.players, player)->default_pad_button(static_cast(data.toInt())); - break; - case pad_type::guncon3: - def_btn_id = ::at32(g_cfg_guncon3.players, player)->default_pad_button(static_cast(data.toInt())); - break; - case pad_type::topshotelite: - def_btn_id = ::at32(g_cfg_topshotelite.players, player)->default_pad_button(static_cast(data.toInt())); - break; - case pad_type::topshotfearmaster: - def_btn_id = ::at32(g_cfg_topshotfearmaster.players, player)->default_pad_button(static_cast(data.toInt())); - break; - } - - combo->setCurrentIndex(combo->findData(static_cast(def_btn_id))); - } - } -} diff --git a/rpcs3qt-legacy/emulated_pad_settings_dialog.h b/rpcs3qt-legacy/emulated_pad_settings_dialog.h deleted file mode 100644 index 490dee3ae..000000000 --- a/rpcs3qt-legacy/emulated_pad_settings_dialog.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -class emulated_pad_settings_dialog : public QDialog -{ - Q_OBJECT - -public: - enum class pad_type - { - buzz, - turntable, - ghltar, - usio, - gem, - ds3gem, - mousegem, - guncon3, - topshotelite, - topshotfearmaster, - }; - - emulated_pad_settings_dialog(pad_type type, QWidget* parent = nullptr); - -private: - template - void add_tabs(QTabWidget* tabs); - - void load_config(); - void save_config(); - void reset_config(); - - pad_type m_type; - - std::vector> m_combos; -}; diff --git a/rpcs3qt-legacy/fatal_error_dialog.cpp b/rpcs3qt-legacy/fatal_error_dialog.cpp deleted file mode 100644 index de6cffbd9..000000000 --- a/rpcs3qt-legacy/fatal_error_dialog.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "fatal_error_dialog.h" - -#include -#include -#include - -const QString document_with_help_text = R"( - -

- %1
- %2
- https://github.com/RPCS3/rpcs3/wiki/How-to-ask-for-Support
- %3
-

-)"; - -const QString document_without_help_text = R"( - -

- %1
-

-)"; - -fatal_error_dialog::fatal_error_dialog(std::string_view text, bool is_html, bool include_help_text) : QMessageBox() -{ - const QString qstr = QString::fromUtf8(text.data(), text.size()); - const QString msg = is_html ? qstr : Qt::convertFromPlainText(qstr); - - QString document_body; - if (include_help_text) [[likely]] - { - document_body = document_with_help_text - .arg(msg) - .arg(tr("HOW TO REPORT ERRORS:")) - .arg(tr("Please, don't send incorrect reports. Thanks for understanding.")); - } - else - { - document_body = document_without_help_text.arg(msg); - } - -#ifndef __APPLE__ - setWindowIcon(QIcon(":/rpcs3.ico")); -#endif - setWindowTitle(tr("RPCS3: Fatal Error")); - setIcon(QMessageBox::Icon::Critical); - setTextFormat(Qt::TextFormat::RichText); - setText(document_body); - layout()->setSizeConstraint(QLayout::SetFixedSize); -} diff --git a/rpcs3qt-legacy/fatal_error_dialog.h b/rpcs3qt-legacy/fatal_error_dialog.h deleted file mode 100644 index 913daf284..000000000 --- a/rpcs3qt-legacy/fatal_error_dialog.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -#include - -class fatal_error_dialog : public QMessageBox -{ - Q_OBJECT - -public: - explicit fatal_error_dialog(std::string_view text, bool is_html, bool include_help_text); -}; diff --git a/rpcs3qt-legacy/find_dialog.cpp b/rpcs3qt-legacy/find_dialog.cpp deleted file mode 100644 index e88b74266..000000000 --- a/rpcs3qt-legacy/find_dialog.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "find_dialog.h" - -#include - -find_dialog::find_dialog(QPlainTextEdit* edit, QWidget* parent, Qt::WindowFlags f) : QDialog(parent, f), m_text_edit(edit) -{ - setWindowTitle(tr("Find string")); - - m_find_bar = new QLineEdit(); - m_find_bar->setPlaceholderText(tr("Search...")); - - m_label_count_lines = new QLabel(tr("Counted in lines: -")); - m_label_count_total = new QLabel(tr("Counted in total: -")); - - m_find_first = new QPushButton(tr("First")); - m_find_last = new QPushButton(tr("Last")); - m_find_next = new QPushButton(tr("Next")); - m_find_previous = new QPushButton(tr("Previous")); - - QHBoxLayout* count_layout = new QHBoxLayout(); - count_layout->addWidget(m_label_count_lines); - count_layout->addWidget(m_label_count_total); - - QHBoxLayout* button_layout = new QHBoxLayout(); - button_layout->addWidget(m_find_first); - button_layout->addWidget(m_find_last); - button_layout->addWidget(m_find_previous); - button_layout->addWidget(m_find_next); - - QVBoxLayout* layout = new QVBoxLayout(); - layout->addWidget(m_find_bar); - layout->addLayout(count_layout); - layout->addLayout(button_layout); - setLayout(layout); - - connect(m_find_first, &QPushButton::clicked, this, &find_dialog::find_first); - connect(m_find_last, &QPushButton::clicked, this, &find_dialog::find_last); - connect(m_find_next, &QPushButton::clicked, this, &find_dialog::find_next); - connect(m_find_previous, &QPushButton::clicked, this, &find_dialog::find_previous); - - m_find_next->setDefault(true); - - show(); -} - -int find_dialog::count_all() -{ - m_count_lines = 0; - m_count_total = 0; - - if (!m_text_edit || m_find_bar->text().isEmpty()) - { - show_count(); - return 0; - } - - const QTextCursor old_cursor = m_text_edit->textCursor(); - m_text_edit->moveCursor(QTextCursor::Start); - - int old_line_index = -1; - - while (m_text_edit->find(m_find_bar->text())) - { - m_count_total++; - const int new_line_index = m_text_edit->textCursor().blockNumber(); - - if (new_line_index != old_line_index) - { - m_count_lines++; - old_line_index = new_line_index; - } - } - - m_text_edit->setTextCursor(old_cursor); - show_count(); - return m_count_total; -} - -void find_dialog::find_first() -{ - if (count_all() <= 0) - return; - - m_text_edit->moveCursor(QTextCursor::Start); - m_text_edit->find(m_find_bar->text()); -} - -void find_dialog::find_last() -{ - if (count_all() <= 0) - return; - - m_text_edit->moveCursor(QTextCursor::End); - m_text_edit->find(m_find_bar->text(), QTextDocument::FindBackward); -} - -void find_dialog::find_next() -{ - if (count_all() <= 0) - return; - - m_text_edit->find(m_find_bar->text()); -} - -void find_dialog::find_previous() -{ - if (count_all() <= 0) - return; - - m_text_edit->find(m_find_bar->text(), QTextDocument::FindBackward); -} - -void find_dialog::show_count() const -{ - m_label_count_lines->setText(tr("Counted in lines: %0").arg(m_count_lines)); - m_label_count_total->setText(tr("Counted in total: %0").arg(m_count_total)); -} diff --git a/rpcs3qt-legacy/find_dialog.h b/rpcs3qt-legacy/find_dialog.h deleted file mode 100644 index cbf0cf582..000000000 --- a/rpcs3qt-legacy/find_dialog.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -class find_dialog : public QDialog -{ - Q_OBJECT - -public: - find_dialog(QPlainTextEdit* edit, QWidget* parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()); - -private: - int m_count_lines = 0; - int m_count_total = 0; - QLabel* m_label_count_lines; - QLabel* m_label_count_total; - QPlainTextEdit* m_text_edit; - QLineEdit* m_find_bar; - QPushButton* m_find_first; - QPushButton* m_find_last; - QPushButton* m_find_next; - QPushButton* m_find_previous; - -private Q_SLOTS: - int count_all(); - void find_first(); - void find_last(); - void find_next(); - void find_previous(); - void show_count() const; -}; diff --git a/rpcs3qt-legacy/flow_layout.cpp b/rpcs3qt-legacy/flow_layout.cpp deleted file mode 100644 index c70793bcb..000000000 --- a/rpcs3qt-legacy/flow_layout.cpp +++ /dev/null @@ -1,288 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include "flow_layout.h" - -flow_layout::flow_layout(QWidget* parent, int margin, bool dynamic_spacing, int hSpacing, int vSpacing) - : QLayout(parent), m_dynamic_spacing(dynamic_spacing), m_h_space_initial(hSpacing), m_v_space_initial(vSpacing), m_h_space(hSpacing), m_v_space(vSpacing) -{ - setContentsMargins(margin, margin, margin, margin); -} - -flow_layout::flow_layout(int margin, bool dynamic_spacing, int hSpacing, int vSpacing) - : m_dynamic_spacing(dynamic_spacing), m_h_space_initial(hSpacing), m_v_space_initial(vSpacing), m_h_space(hSpacing), m_v_space(vSpacing) -{ - setContentsMargins(margin, margin, margin, margin); -} - -flow_layout::~flow_layout() -{ - clear(); -} - -void flow_layout::clear() -{ - // We can't use a ranged loop here, since deleting the widget will call takeAt on the layout. So let's also use takeAt. - while (QLayoutItem* item = takeAt(0)) - { - if (item) - { - delete item->widget(); - delete item; - } - } - m_item_list.clear(); - m_positions.clear(); -} - -void flow_layout::addItem(QLayoutItem* item) -{ - m_positions.append(position{.row = -1, .col = -1}); - m_item_list.append(item); -} - -int flow_layout::horizontalSpacing() const -{ - if (m_h_space >= 0) - { - return m_h_space; - } - - return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); -} - -int flow_layout::verticalSpacing() const -{ - if (m_v_space >= 0) - { - return m_v_space; - } - - return smartSpacing(QStyle::PM_LayoutVerticalSpacing); -} - -int flow_layout::count() const -{ - return m_item_list.size(); -} - -QLayoutItem* flow_layout::itemAt(int index) const -{ - return m_item_list.value(index); -} - -QLayoutItem* flow_layout::takeAt(int index) -{ - if (index >= 0 && index < m_item_list.size()) - { - m_positions.takeAt(index); - return m_item_list.takeAt(index); - } - - return nullptr; -} - -Qt::Orientations flow_layout::expandingDirections() const -{ - return {}; -} - -bool flow_layout::hasHeightForWidth() const -{ - return true; -} - -int flow_layout::heightForWidth(int width) const -{ - return doLayout(QRect(0, 0, width, 0), true); -} - -void flow_layout::setGeometry(const QRect& rect) -{ - QLayout::setGeometry(rect); - doLayout(rect, false); -} - -QSize flow_layout::sizeHint() const -{ - return minimumSize(); -} - -QSize flow_layout::minimumSize() const -{ - QSize size; - for (const QLayoutItem* item : m_item_list) - { - if (item) - { - size = size.expandedTo(item->minimumSize()); - } - } - - const QMargins margins = contentsMargins(); - size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom()); - return size; -} - -int flow_layout::doLayout(const QRect& rect, bool testOnly) const -{ - int left, top, right, bottom; - getContentsMargins(&left, &top, &right, &bottom); - const QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); - int x = effectiveRect.x(); - int y = effectiveRect.y(); - int lineHeight = 0; - int row_count = 0; - int col_count = 0; - - if (m_dynamic_spacing) - { - const int available_width = effectiveRect.width(); - const int min_spacing = smartSpacing(QStyle::PM_LayoutHorizontalSpacing); - bool fits_into_width = true; - int width = 0; - int index = 0; - - for (; index < m_item_list.size(); index++) - { - if (QLayoutItem* item = m_item_list.at(index)) - { - const int new_width = width + item->sizeHint().width() + (width > 0 ? min_spacing : 0); - - if (new_width > effectiveRect.width()) - { - fits_into_width = false; - break; - } - - width = new_width; - } - } - - // Try to evenly distribute the items across the width - m_h_space = (index == 0) ? -1 : ((available_width - width) / index); - - if (fits_into_width) - { - // Make sure there aren't huge gaps between the items - m_h_space = smartSpacing(QStyle::PM_LayoutHorizontalSpacing); - } - } - else - { - m_h_space = m_h_space_initial; - } - - for (int i = 0, row = 0, col = 0; i < m_item_list.size(); i++) - { - QLayoutItem* item = m_item_list.at(i); - if (!item) - continue; - - const QWidget* wid = item->widget(); - if (!wid) - continue; - - int spaceX = horizontalSpacing(); - if (spaceX == -1) - spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); - - int spaceY = verticalSpacing(); - if (spaceY == -1) - spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); - - int nextX = x + item->sizeHint().width() + spaceX; - if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) - { - x = effectiveRect.x(); - y = y + lineHeight + spaceY; - nextX = x + item->sizeHint().width() + spaceX; - lineHeight = 0; - col = 0; - row++; - } - - position& pos = m_positions[i]; - pos.row = row; - pos.col = col++; - - row_count = std::max(row_count, pos.row + 1); - col_count = std::max(col_count, pos.col + 1); - - if (!testOnly) - item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); - - x = nextX; - lineHeight = qMax(lineHeight, item->sizeHint().height()); - } - - m_rows = row_count; - m_cols = col_count; - - return y + lineHeight - rect.y() + bottom; -} - -int flow_layout::smartSpacing(QStyle::PixelMetric pm) const -{ - const QObject* parent = this->parent(); - if (!parent) - { - return -1; - } - - if (parent->isWidgetType()) - { - const QWidget* pw = static_cast(parent); - return pw->style()->pixelMetric(pm, nullptr, pw); - } - - return static_cast(parent)->spacing(); -} diff --git a/rpcs3qt-legacy/flow_layout.h b/rpcs3qt-legacy/flow_layout.h deleted file mode 100644 index f120a097c..000000000 --- a/rpcs3qt-legacy/flow_layout.h +++ /dev/null @@ -1,115 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#pragma once - -#include -#include -#include - -class flow_layout : public QLayout -{ -public: - struct position - { - int row{}; - int col{}; - }; - - explicit flow_layout(QWidget* parent, int margin = -1, bool dynamic_spacing = false, int hSpacing = -1, int vSpacing = -1); - explicit flow_layout(int margin = -1, bool dynamic_spacing = false, int hSpacing = -1, int vSpacing = -1); - ~flow_layout(); - - void clear(); - const QList& item_list() const - { - return m_item_list; - } - const QList& positions() const - { - return m_positions; - } - int rows() const - { - return m_rows; - } - int cols() const - { - return m_cols; - } - - void addItem(QLayoutItem* item) override; - Qt::Orientations expandingDirections() const override; - bool hasHeightForWidth() const override; - int heightForWidth(int) const override; - int count() const override; - QLayoutItem* itemAt(int index) const override; - QSize minimumSize() const override; - void setGeometry(const QRect& rect) override; - QSize sizeHint() const override; - QLayoutItem* takeAt(int index) override; - -private: - int horizontalSpacing() const; - int verticalSpacing() const; - int doLayout(const QRect& rect, bool testOnly) const; - int smartSpacing(QStyle::PixelMetric pm) const; - - QList m_item_list; - bool m_dynamic_spacing{}; - int m_h_space_initial{-1}; - int m_v_space_initial{-1}; - mutable int m_h_space{-1}; - mutable int m_v_space{-1}; - - mutable QList m_positions; - mutable int m_rows{}; - mutable int m_cols{}; -}; diff --git a/rpcs3qt-legacy/flow_widget.cpp b/rpcs3qt-legacy/flow_widget.cpp deleted file mode 100644 index 971d9e076..000000000 --- a/rpcs3qt-legacy/flow_widget.cpp +++ /dev/null @@ -1,257 +0,0 @@ -#include "stdafx.h" -#include "flow_widget.h" - -#include -#include -#include -#include - -flow_widget::flow_widget(QWidget* parent) - : QWidget(parent) -{ - m_flow_layout = new flow_layout(); - - QWidget* widget = new QWidget(this); - widget->setLayout(m_flow_layout); - widget->setObjectName("flow_widget_content"); - widget->setFocusProxy(this); - - m_scroll_area = new QScrollArea(this); - m_scroll_area->setWidget(widget); - m_scroll_area->setWidgetResizable(true); - - QVBoxLayout* layout = new QVBoxLayout(this); - layout->addWidget(m_scroll_area); - layout->setContentsMargins(0, 0, 0, 0); - setLayout(layout); -} - -flow_widget::~flow_widget() -{ -} - -void flow_widget::add_widget(flow_widget_item* widget) -{ - if (widget) - { - m_widgets.push_back(widget); - m_flow_layout->addWidget(widget); - - connect(widget, &flow_widget_item::navigate, this, &flow_widget::on_navigate); - connect(widget, &flow_widget_item::focused, this, &flow_widget::on_item_focus); - } -} - -void flow_widget::clear() -{ - m_widgets.clear(); - m_flow_layout->clear(); -} - -flow_widget_item* flow_widget::selected_item() const -{ - if (m_selected_index >= 0 && static_cast(m_selected_index) < m_widgets.size()) - { - return ::at32(m_widgets, m_selected_index); - } - - return nullptr; -} - -void flow_widget::paintEvent(QPaintEvent* /*event*/) -{ - // Needed for stylesheets to apply to QWidgets - QStyleOption option; - option.initFrom(this); - QPainter painter(this); - style()->drawPrimitive(QStyle::PE_Widget, &option, &painter, this); -} - -s64 flow_widget::find_item(const flow_layout::position& pos) -{ - if (pos.row < 0 || pos.col < 0) - { - return -1; - } - - const auto& positions = m_flow_layout->positions(); - - for (s64 i = 0; i < positions.size(); i++) - { - if (const auto& other = ::at32(positions, i); other.row == pos.row && other.col == pos.col) - { - return i; - } - } - - return -1; -} - -flow_layout::position flow_widget::find_item(flow_widget_item* item) -{ - if (item) - { - const auto& item_list = m_flow_layout->item_list(); - const auto& positions = m_flow_layout->positions(); - ensure(item_list.size() == positions.size()); - - for (s64 i = 0; i < item_list.size(); i++) - { - if (const auto& layout_item = ::at32(item_list, i); layout_item && layout_item->widget() == item) - { - return ::at32(positions, i); - } - } - } - - return flow_layout::position{.row = -1, .col = -1}; -} - -flow_layout::position flow_widget::find_next_item(flow_layout::position current_pos, flow_navigation value) -{ - if (current_pos.row >= 0 && current_pos.col >= 0 && m_flow_layout->rows() > 0 && m_flow_layout->cols() > 0) - { - switch (value) - { - case flow_navigation::up: - // Go up one row. - if (current_pos.row > 0) - { - current_pos.row--; - } - break; - case flow_navigation::down: - // Go down one row. Beware of last row which might have less columns. - for (const auto& pos : m_flow_layout->positions()) - { - ; - if (pos.col != current_pos.col) - continue; - if (pos.row == current_pos.row + 1) - { - current_pos.row = pos.row; - break; - } - } - break; - case flow_navigation::left: - // Go left one column. - if (current_pos.col > 0) - { - current_pos.col--; - } - break; - case flow_navigation::right: - // Go right one column. Beware of last row which might have less columns. - for (const auto& pos : m_flow_layout->positions()) - { - if (pos.row > current_pos.row) - break; - if (pos.row < current_pos.row) - continue; - if (pos.col == current_pos.col + 1) - { - current_pos.col = pos.col; - break; - } - } - break; - case flow_navigation::home: - // Go to leftmost column. - current_pos.col = 0; - break; - case flow_navigation::end: - // Go to last column. Beware of last row which might have less columns. - for (const auto& pos : m_flow_layout->positions()) - { - if (pos.row > current_pos.row) - break; - if (pos.row < current_pos.row) - continue; - current_pos.col = std::max(current_pos.col, pos.col); - } - break; - case flow_navigation::page_up: - // Go to top row. - current_pos.row = 0; - break; - case flow_navigation::page_down: - // Go to bottom row. Beware of last row which might have less columns. - for (const auto& pos : m_flow_layout->positions()) - { - if (pos.col != current_pos.col) - continue; - current_pos.row = std::max(current_pos.row, pos.row); - } - break; - } - } - - return current_pos; -} - -void flow_widget::select_item(flow_widget_item* item) -{ - const flow_layout::position selected_pos = find_item(item); - const s64 selected_index = find_item(selected_pos); - - if (selected_index < 0 || static_cast(selected_index) >= items().size()) - { - m_selected_index = -1; - return; - } - - m_selected_index = selected_index; - Q_EMIT ItemSelectionChanged(m_selected_index); - - for (usz i = 0; i < items().size(); i++) - { - if (flow_widget_item* item = items().at(i)) - { - // We need to polish the widgets in order to re-apply any stylesheet changes for the selected property. - item->selected = m_selected_index >= 0 && i == static_cast(m_selected_index); - item->polish_style(); - } - } - - // Make sure we see the focused widget - m_scroll_area->ensureWidgetVisible(::at32(items(), m_selected_index)); -} - -void flow_widget::on_item_focus() -{ - select_item(static_cast(QObject::sender())); -} - -void flow_widget::on_navigate(flow_navigation value) -{ - const flow_layout::position selected_pos = find_next_item(find_item(static_cast(QObject::sender())), value); - const s64 selected_index = find_item(selected_pos); - if (selected_index < 0 || static_cast(selected_index) >= items().size()) - { - return; - } - - if (flow_widget_item* item = items().at(selected_index)) - { - item->setFocus(); - } - - m_selected_index = selected_index; -} - -void flow_widget::mouseDoubleClickEvent(QMouseEvent* ev) -{ - if (!ev) - return; - - // Qt's itemDoubleClicked signal doesn't distinguish between mouse buttons and there is no simple way to get the pressed button. - // So we have to ignore this event when another button is pressed. - if (ev->button() != Qt::LeftButton) - { - ev->ignore(); - return; - } - - QWidget::mouseDoubleClickEvent(ev); -} diff --git a/rpcs3qt-legacy/flow_widget.h b/rpcs3qt-legacy/flow_widget.h deleted file mode 100644 index 34f167d52..000000000 --- a/rpcs3qt-legacy/flow_widget.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include "util/types.hpp" -#include "flow_widget_item.h" -#include "flow_layout.h" - -#include -#include -#include - -class flow_widget : public QWidget -{ - Q_OBJECT - -public: - flow_widget(QWidget* parent); - virtual ~flow_widget(); - - void add_widget(flow_widget_item* widget); - void clear(); - - std::vector& items() - { - return m_widgets; - } - flow_widget_item* selected_item() const; - QScrollArea* scroll_area() const - { - return m_scroll_area; - } - - void paintEvent(QPaintEvent* event) override; - -Q_SIGNALS: - void ItemSelectionChanged(int index); - -private Q_SLOTS: - void on_item_focus(); - void on_navigate(flow_navigation value); - -protected: - void select_item(flow_widget_item* item); - void mouseDoubleClickEvent(QMouseEvent* event) override; - -private: - s64 find_item(const flow_layout::position& pos); - flow_layout::position find_item(flow_widget_item* item); - flow_layout::position find_next_item(flow_layout::position current_pos, flow_navigation value); - - flow_layout* m_flow_layout{}; - QScrollArea* m_scroll_area{}; - std::vector m_widgets; - s64 m_selected_index = -1; -}; diff --git a/rpcs3qt-legacy/flow_widget_item.cpp b/rpcs3qt-legacy/flow_widget_item.cpp deleted file mode 100644 index 8b766d302..000000000 --- a/rpcs3qt-legacy/flow_widget_item.cpp +++ /dev/null @@ -1,125 +0,0 @@ -#include "flow_widget_item.h" - -#include -#include -#include - -flow_widget_item::flow_widget_item(QWidget* parent) : QWidget(parent) -{ - setAttribute(Qt::WA_Hover); // We need to enable the hover attribute to ensure that hover events are handled. -} - -flow_widget_item::~flow_widget_item() -{ -} - -void flow_widget_item::polish_style() -{ - style()->unpolish(this); - style()->polish(this); -} - -void flow_widget_item::paintEvent(QPaintEvent* /*event*/) -{ - // Needed for stylesheets to apply to QWidgets - QStyleOption option; - option.initFrom(this); - QPainter painter(this); - style()->drawPrimitive(QStyle::PE_Widget, &option, &painter, this); - - if (!got_visible && cb_on_first_visibility) - { - if (QWidget* widget = static_cast(parent())) - { - if (widget->visibleRegion().intersects(geometry())) - { - got_visible = true; - cb_on_first_visibility(); - } - } - } -} - -void flow_widget_item::focusInEvent(QFocusEvent* event) -{ - QWidget::focusInEvent(event); - - // We need to polish the widgets in order to re-apply any stylesheet changes for the focus property. - polish_style(); - - Q_EMIT focused(); -} - -void flow_widget_item::focusOutEvent(QFocusEvent* event) -{ - QWidget::focusOutEvent(event); - - // We need to polish the widgets in order to re-apply any stylesheet changes for the focus property. - polish_style(); -} - -void flow_widget_item::keyPressEvent(QKeyEvent* event) -{ - if (!event) - { - return; - } - - switch (event->key()) - { - case Qt::Key_Left: - Q_EMIT navigate(flow_navigation::left); - return; - case Qt::Key_Right: - Q_EMIT navigate(flow_navigation::right); - return; - case Qt::Key_Up: - Q_EMIT navigate(flow_navigation::up); - return; - case Qt::Key_Down: - Q_EMIT navigate(flow_navigation::down); - return; - case Qt::Key_Home: - Q_EMIT navigate(flow_navigation::home); - return; - case Qt::Key_End: - Q_EMIT navigate(flow_navigation::end); - return; - case Qt::Key_PageUp: - Q_EMIT navigate(flow_navigation::page_up); - return; - case Qt::Key_PageDown: - Q_EMIT navigate(flow_navigation::page_down); - return; - default: - break; - } - - QWidget::keyPressEvent(event); -} - -bool flow_widget_item::event(QEvent* event) -{ - bool hover_changed = false; - - switch (event->type()) - { - case QEvent::HoverEnter: - hover_changed = setProperty("hover", "true"); - break; - case QEvent::HoverLeave: - hover_changed = setProperty("hover", "false"); - break; - default: - break; - } - - if (hover_changed) - { - // We need to polish the widgets in order to re-apply any stylesheet changes for the custom hover property. - // :hover does not work if we add descendants in the qss, so we need to use a custom property. - polish_style(); - } - - return QWidget::event(event); -} diff --git a/rpcs3qt-legacy/flow_widget_item.h b/rpcs3qt-legacy/flow_widget_item.h deleted file mode 100644 index 6b7078e1a..000000000 --- a/rpcs3qt-legacy/flow_widget_item.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include - -#include - -enum class flow_navigation -{ - up, - down, - left, - right, - home, - end, - page_up, - page_down -}; - -class flow_widget_item : public QWidget -{ - Q_OBJECT - - Q_PROPERTY(bool hover MEMBER m_hover) // Stylesheet workaround for descendants with parent pseudo state - Q_PROPERTY(bool selected MEMBER selected) // Stylesheet workaround for descendants with parent pseudo state - -public: - flow_widget_item(QWidget* parent); - virtual ~flow_widget_item(); - - virtual void polish_style(); - - void paintEvent(QPaintEvent* event) override; - void focusInEvent(QFocusEvent* event) override; - void focusOutEvent(QFocusEvent* event) override; - void keyPressEvent(QKeyEvent* event) override; - bool event(QEvent* event) override; - - bool got_visible{}; - bool selected{}; - std::function cb_on_first_visibility{}; - -protected: - bool m_hover{}; - -Q_SIGNALS: - void navigate(flow_navigation value); - void focused(); -}; diff --git a/rpcs3qt-legacy/frame_icon.xpm b/rpcs3qt-legacy/frame_icon.xpm deleted file mode 100644 index 08ac79c27..000000000 --- a/rpcs3qt-legacy/frame_icon.xpm +++ /dev/null @@ -1,139 +0,0 @@ -/* XPM */ -static char * frame_icon_xpm[] = { -"128 128 8 1", -" c None", -". c #0064E7", -"+ c #0065E8", -"@ c #0063E7", -"# c #0065E7", -"$ c #0064E8", -"% c #0065E6", -"& c #0064E6", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ..........................................................................................................+ ", -" ............................................................................................................. ", -" ............................................................................................................... ", -" ................................................................................................................@ ", -" .................................................................................................................. ", -" .................................................................................................................. ", -" ................................................................................................................... ", -" ...................................................................................................................@ ", -" .................................................................................................................... ", -" .................................................................................................................... ", -" ....................................................................................................................@ ", -" ..................................................................................................................... ", -" ..................................................................................................................... ", -" ..................... ", -" .................... ", -" ................... ", -" ................... ", -" .................. ", -" .................. ", -" #................. ", -" .................. ", -" $................. ", -" %................. ", -" .................. ", -" &................. ", -" .................. ", -" $................. ", -" .................. ", -" .................. ", -" $.................. ", -" ..................& ", -" $..................# ", -" #&.................... ", -" .................................................................................................................... ", -" ...................................................................................................................# ", -" ................................................................................................................... ", -" .................................................................................................................. ", -" ................................................................................................................ ", -" ................................................................................................................. ", -" ..................................................................................................................$ ", -" ...................................................................................................................@ ", -" .................................................................................................................... ", -" .................................................................................................................... ", -" ....................................................................................................................+ ", -" ....................................................................................................................@ ", -" ..................................................................................................................... ", -" ..................... ", -" &................... ", -" ................... ", -" .................. ", -" .................. ", -" @................. ", -" ................. ", -" ................. ", -" ................. ", -" ................. ", -" ................. ", -" ................. ", -" ................. ", -" ................. ", -" .................. ", -" .................. ", -" .................. ", -" &.................. ", -" ................... ", -" ...................$ ", -" .................................................................................................................... ", -" &................................................................................................................... ", -" ...................................................................................................................& ", -" @.................................................................................................................. ", -" &.................................................................................................................# ", -" .................................................................................................................. ", -" ................................................................................................................. ", -" +............................................................................................................... ", -" ............................................................................................................... ", -" .............................................................................................................. ", -" @..........................................................................................................$ ", -" .......................................................................................................... ", -" .....................................................................................................#@ ", -" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" "}; diff --git a/rpcs3qt-legacy/game_compatibility.cpp b/rpcs3qt-legacy/game_compatibility.cpp deleted file mode 100644 index b6089878a..000000000 --- a/rpcs3qt-legacy/game_compatibility.cpp +++ /dev/null @@ -1,347 +0,0 @@ -#include "game_compatibility.h" -#include "gui_settings.h" -#include "downloader.h" -#include "localized.h" - -#include "Crypto/unpkg.h" -#include "Loader/PSF.h" - -#include -#include -#include -#include - -LOG_CHANNEL(compat_log, "Compat"); - -constexpr auto qstr = QString::fromStdString; - -game_compatibility::game_compatibility(std::shared_ptr gui_settings, QWidget* parent) - : QObject(parent), m_gui_settings(std::move(gui_settings)) -{ - m_filepath = m_gui_settings->GetSettingsDir() + "/compat_database.dat"; - m_downloader = new downloader(parent); - RequestCompatibility(); - - connect(m_downloader, &downloader::signal_download_error, this, &game_compatibility::handle_download_error); - connect(m_downloader, &downloader::signal_download_finished, this, &game_compatibility::handle_download_finished); - connect(m_downloader, &downloader::signal_download_canceled, this, &game_compatibility::handle_download_canceled); -} - -void game_compatibility::handle_download_error(const QString& error) -{ - Q_EMIT DownloadError(error); -} - -void game_compatibility::handle_download_finished(const QByteArray& content) -{ - compat_log.notice("Database download finished"); - - // Create new map from database and write database to file if database was valid - if (ReadJSON(QJsonDocument::fromJson(content).object(), true)) - { - // Write database to file - QFile file(m_filepath); - - if (file.exists()) - { - compat_log.notice("Database file found: %s", m_filepath); - } - - if (!file.open(QIODevice::WriteOnly)) - { - compat_log.error("Database Error - Could not write database to file: %s", m_filepath); - return; - } - - file.write(content); - file.close(); - - compat_log.success("Wrote database to file: %s", m_filepath); - } - - // We have a new database in map, therefore refresh gamelist to new state - Q_EMIT DownloadFinished(); -} - -void game_compatibility::handle_download_canceled() -{ - Q_EMIT DownloadCanceled(); -} - -bool game_compatibility::ReadJSON(const QJsonObject& json_data, bool after_download) -{ - const int return_code = json_data["return_code"].toInt(); - - if (return_code < 0) - { - if (after_download) - { - std::string error_message; - switch (return_code) - { - case -1: - error_message = "Server Error - Internal Error"; - break; - case -2: - error_message = "Server Error - Maintenance Mode"; - break; - default: - error_message = "Server Error - Unknown Error"; - break; - } - compat_log.error("%s: return code %d", error_message, return_code); - Q_EMIT DownloadError(qstr(error_message) + " " + QString::number(return_code)); - } - else - { - compat_log.error("Database Error - Invalid: return code %d", return_code); - } - return false; - } - - if (!json_data["results"].isObject()) - { - compat_log.error("Database Error - No Results found"); - return false; - } - - m_compat_database.clear(); - - QJsonObject json_results = json_data["results"].toObject(); - - // Retrieve status data for every valid entry - for (const auto& key : json_results.keys()) - { - if (!json_results[key].isObject()) - { - compat_log.error("Database Error - Unusable object %s", key); - continue; - } - - QJsonObject json_result = json_results[key].toObject(); - - // Retrieve compatibility information from json - compat::status status = ::at32(Status_Data, json_result.value("status").toString("NoResult")); - - // Add date if possible - status.date = json_result.value("date").toString(); - - // Add latest version if possible - status.latest_version = json_result.value("update").toString(); - - // Add patchsets if possible - if (const QJsonValue patchsets_value = json_result.value("patchsets"); patchsets_value.isArray()) - { - for (const QJsonValue& patch_set : patchsets_value.toArray()) - { - compat::pkg_patchset set; - set.tag_id = patch_set["tag_id"].toString().toStdString(); - set.popup = patch_set["popup"].toBool(); - set.signoff = patch_set["signoff"].toBool(); - set.popup_delay = patch_set["popup_delay"].toInt(); - set.min_system_ver = patch_set["min_system_ver"].toString().toStdString(); - - if (const QJsonValue packages_value = patch_set["packages"]; packages_value.isArray()) - { - for (const QJsonValue& package : packages_value.toArray()) - { - compat::pkg_package pkg; - pkg.version = package["version"].toString().toStdString(); - pkg.size = package["size"].toInt(); - pkg.sha1sum = package["sha1sum"].toString().toStdString(); - pkg.ps3_system_ver = package["ps3_system_ver"].toString().toStdString(); - pkg.drm_type = package["drm_type"].toString().toStdString(); - - if (const QJsonValue changelogs_value = package["changelogs"]; changelogs_value.isArray()) - { - for (const QJsonValue& changelog : changelogs_value.toArray()) - { - compat::pkg_changelog chl; - chl.type = changelog["type"].toString().toStdString(); - chl.content = changelog["content"].toString().toStdString(); - - pkg.changelogs.push_back(std::move(chl)); - } - } - - if (const QJsonValue titles_value = package["titles"]; titles_value.isArray()) - { - for (const QJsonValue& title : titles_value.toArray()) - { - compat::pkg_title ttl; - ttl.type = title["type"].toString().toStdString(); - ttl.title = title["title"].toString().toStdString(); - - pkg.titles.push_back(std::move(ttl)); - } - } - - set.packages.push_back(std::move(pkg)); - } - } - - status.patch_sets.push_back(std::move(set)); - } - } - - // Add status to map - m_compat_database.emplace(key.toStdString(), std::move(status)); - } - - return true; -} - -void game_compatibility::RequestCompatibility(bool online) -{ - if (!online) - { - // Retrieve database from file - QFile file(m_filepath); - - if (!file.exists()) - { - compat_log.notice("Database file not found: %s", m_filepath); - return; - } - - if (!file.open(QIODevice::ReadOnly)) - { - compat_log.error("Database Error - Could not read database from file: %s", m_filepath); - return; - } - - const QByteArray content = file.readAll(); - file.close(); - - compat_log.notice("Finished reading database from file: %s", m_filepath); - - // Create new map from database - ReadJSON(QJsonDocument::fromJson(content).object(), online); - - return; - } - - const std::string url = "https://rpcs3.net/compatibility?api=v1&export"; - compat_log.notice("Beginning compatibility database download from: %s", url); - - m_downloader->start(url, true, true, tr("Downloading Database")); - - // We want to retrieve a new database, therefore refresh gamelist and indicate that - Q_EMIT DownloadStarted(); -} - -compat::status game_compatibility::GetCompatibility(const std::string& title_id) -{ - if (m_compat_database.empty()) - { - return ::at32(Status_Data, "NoData"); - } - - if (const auto it = m_compat_database.find(title_id); it != m_compat_database.cend()) - { - return it->second; - } - - return ::at32(Status_Data, "NoResult"); -} - -compat::status game_compatibility::GetStatusData(const QString& status) const -{ - return ::at32(Status_Data, status); -} - -compat::package_info game_compatibility::GetPkgInfo(const QString& pkg_path, game_compatibility* compat) -{ - compat::package_info info; - - const package_reader reader(pkg_path.toStdString()); - if (!reader.is_valid()) - { - info.is_valid = false; - return info; - } - - const psf::registry& psf = reader.get_psf(); - - // TODO: localization of title and changelog - const std::string title_key = "TITLE"; - const std::string changelog_key = "paramhip"; - - info.path = pkg_path; - info.title = qstr(std::string(psf::get_string(psf, title_key))); // Let's read this from the psf first - info.title_id = qstr(std::string(psf::get_string(psf, "TITLE_ID"))); - info.category = qstr(std::string(psf::get_string(psf, "CATEGORY"))); - info.version = qstr(std::string(psf::get_string(psf, "APP_VER"))); - - if (!info.category.isEmpty()) - { - const Localized localized; - - if (const auto boot_cat = localized.category.cat_boot.find(info.category); boot_cat != localized.category.cat_boot.end()) - { - info.local_cat = boot_cat->second; - } - else if (const auto data_cat = localized.category.cat_data.find(info.category); data_cat != localized.category.cat_data.end()) - { - info.local_cat = data_cat->second; - } - - if (info.category == "GD") - { - // For now let's assume that PS3 Game Data packages are always updates or DLC. - // Update packages always seem to have an APP_VER, so let's say it's a DLC otherwise. - // Ideally this would simply be the package content type, but I am too lazy to implement this right now. - if (info.version.isEmpty()) - { - info.type = compat::package_type::dlc; - } - else - { - info.type = compat::package_type::update; - } - } - } - - if (info.version.isEmpty()) - { - // Fallback to VERSION - info.version = qstr(std::string(psf::get_string(psf, "VERSION"))); - } - - if (compat) - { - compat::status stat = compat->GetCompatibility(info.title_id.toStdString()); - if (!stat.patch_sets.empty()) - { - // We currently only handle the first patch set - for (const auto& package : stat.patch_sets.front().packages) - { - if (info.version.toStdString() == package.version) - { - if (const std::string localized_title = package.get_title(title_key); !localized_title.empty()) - { - info.title = qstr(localized_title); - } - - if (const std::string localized_changelog = package.get_changelog(changelog_key); !localized_changelog.empty()) - { - info.changelog = qstr(localized_changelog); - } - - // This should be an update since it was found in a patch set - info.type = compat::package_type::update; - - break; - } - } - } - } - - if (info.title.isEmpty()) - { - const QFileInfo file_info(pkg_path); - info.title = file_info.fileName(); - } - - return info; -} diff --git a/rpcs3qt-legacy/game_compatibility.h b/rpcs3qt-legacy/game_compatibility.h deleted file mode 100644 index 5bc97514c..000000000 --- a/rpcs3qt-legacy/game_compatibility.h +++ /dev/null @@ -1,177 +0,0 @@ -#pragma once - -#include - -#include - -class downloader; -class gui_settings; - -namespace compat -{ - /** Represents the "title" json object */ - struct pkg_title - { - std::string type; // TITLE or TITLE_08 etc. (system languages) - std::string title; // The Last of Arse - }; - - /** Represents the "changelog" json object */ - struct pkg_changelog - { - std::string type; // paramhip or paramhip_08 etc. (system languages) - std::string content; // "This system software update improves system performance." - }; - - /** Represents the "package" json object */ - struct pkg_package - { - std::string version; // 01.04 - int size = 0; - std::string sha1sum; // a5c83b88394ea3ae99974caedd38690981a80f3e - std::string ps3_system_ver; // 04.4000 - std::string drm_type; // local or mbind etc. - std::vector changelogs; - std::vector titles; - - std::string get_changelog(const std::string& type) const - { - if (const auto it = std::find_if(changelogs.cbegin(), changelogs.cend(), [type](const pkg_changelog& cl) - { - return cl.type == type; - }); - it != changelogs.cend()) - { - return it->content; - } - if (const auto it = std::find_if(changelogs.cbegin(), changelogs.cend(), [](const pkg_changelog& cl) - { - return cl.type == "paramhip"; - }); - it != changelogs.cend()) - { - return it->content; - } - return ""; - } - - std::string get_title(const std::string& type) const - { - if (const auto it = std::find_if(titles.cbegin(), titles.cend(), [type](const pkg_title& t) - { - return t.type == type; - }); - it != titles.cend()) - { - return it->title; - } - if (const auto it = std::find_if(titles.cbegin(), titles.cend(), [](const pkg_title& t) - { - return t.type == "TITLE"; - }); - it != titles.end()) - { - return it->title; - } - return ""; - } - }; - - /** Represents the "patchset" json object */ - struct pkg_patchset - { - std::string tag_id; // BLES01269_T7 - bool popup = false; - bool signoff = false; - int popup_delay = 0; - std::string min_system_ver; // 03.60 - std::vector packages; - }; - - /** Represents the json object that contains an app's information and some additional info that is used in the GUI */ - struct status - { - int index = 0; - QString date; - QString color; - QString text; - QString tooltip; - QString latest_version; - std::vector patch_sets; - }; - - // The type of the package. In the future this should signify the proper PKG_CONTENT_TYPE. - enum class package_type - { - update, - dlc, - other - }; - - /** Concicely represents a specific pkg's localized information for use in the GUI */ - struct package_info - { - bool is_valid = true; - - QString path; // File path - QString title_id; // TEST12345 - QString title; // Localized - QString changelog; // Localized, may be empty - QString version; // May be empty - QString category; // HG, DG, GD etc. - QString local_cat; // Localized category - - package_type type = package_type::other; // The type of package (Update, DLC or other) - }; -} // namespace compat - -class game_compatibility : public QObject -{ - Q_OBJECT - -private: - const std::map Status_Data = - { - {"Playable", {0, "", "#1ebc61", tr("Playable"), tr("Games that can be properly played from start to finish")}}, - {"Ingame", {1, "", "#f9b32f", tr("Ingame"), tr("Games that either can't be finished, have serious glitches or have insufficient performance")}}, - {"Intro", {2, "", "#e08a1e", tr("Intro"), tr("Games that display image but don't make it past the menus")}}, - {"Loadable", {3, "", "#e74c3c", tr("Loadable"), tr("Games that display a black screen with a framerate on the window's title")}}, - {"Nothing", {4, "", "#455556", tr("Nothing"), tr("Games that don't initialize properly, not loading at all and/or crashing the emulator")}}, - {"NoResult", {5, "", "", tr("No results found"), tr("There is no entry for this game or application in the compatibility database yet.")}}, - {"NoData", {6, "", "", tr("Database missing"), tr("Right click here and download the current database.\nMake sure you are connected to the internet.")}}, - {"Download", {7, "", "", tr("Retrieving..."), tr("Downloading the compatibility database. Please wait...")}}}; - std::shared_ptr m_gui_settings; - QString m_filepath; - downloader* m_downloader = nullptr; - std::map m_compat_database; - - /** Creates new map from the database */ - bool ReadJSON(const QJsonObject& json_data, bool after_download); - -public: - /** Handles reads, writes and downloads for the compatibility database */ - game_compatibility(std::shared_ptr settings, QWidget* parent); - - /** Reads database. If online set to true: Downloads and writes the database to file */ - void RequestCompatibility(bool online = false); - - /** Returns the compatibility status for the requested title */ - compat::status GetCompatibility(const std::string& title_id); - - /** Returns the data for the requested status */ - compat::status GetStatusData(const QString& status) const; - - /** Returns package information like title, version, changelog etc. */ - static compat::package_info GetPkgInfo(const QString& pkg_path, game_compatibility* compat); - -Q_SIGNALS: - void DownloadStarted(); - void DownloadFinished(); - void DownloadCanceled(); - void DownloadError(const QString& error); - -private Q_SLOTS: - void handle_download_error(const QString& error); - void handle_download_finished(const QByteArray& content); - void handle_download_canceled(); -}; diff --git a/rpcs3qt-legacy/game_list.cpp b/rpcs3qt-legacy/game_list.cpp deleted file mode 100644 index 674fb62c2..000000000 --- a/rpcs3qt-legacy/game_list.cpp +++ /dev/null @@ -1,194 +0,0 @@ -#include "stdafx.h" -#include "game_list.h" -#include "movie_item.h" - -#include -#include -#include - -game_list::game_list() : QTableWidget(), game_list_base() -{ - m_icon_ready_callback = [this](const movie_item_base* item) - { - Q_EMIT IconReady(item); - }; -} - -void game_list::sync_header_actions(QList& actions, std::function get_visibility) -{ - ensure(get_visibility); - - bool is_dirty = false; - - for (int col = 0; col < actions.count(); ++col) - { - const bool is_hidden = !get_visibility(col); - actions[col]->setChecked(!is_hidden); - - if (isColumnHidden(col) != is_hidden) - { - setColumnHidden(col, is_hidden); - is_dirty = true; - } - } - - if (is_dirty) - { - fix_narrow_columns(); - } -} - -void game_list::create_header_actions(QList& actions, std::function get_visibility, std::function set_visibility) -{ - ensure(get_visibility); - ensure(set_visibility); - - horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); - - connect(horizontalHeader(), &QHeaderView::customContextMenuRequested, this, [this, &actions](const QPoint& pos) - { - QMenu* configure = new QMenu(this); - configure->addActions(actions); - configure->exec(horizontalHeader()->viewport()->mapToGlobal(pos)); - }); - - for (int col = 0; col < actions.count(); ++col) - { - actions[col]->setCheckable(true); - - connect(actions[col], &QAction::triggered, this, [this, &actions, get_visibility, set_visibility, col](bool checked) - { - if (!checked) // be sure to have at least one column left so you can call the context menu at all time - { - int c = 0; - for (int i = 0; i < actions.count(); ++i) - { - if (get_visibility(i) && ++c > 1) - break; - } - if (c < 2) - { - actions[col]->setChecked(true); // re-enable the checkbox if we don't change the actual state - return; - } - } - - setColumnHidden(col, !checked); // Negate because it's a set col hidden and we have menu say show. - set_visibility(col, checked); - - if (checked) // handle hidden columns that have zero width after showing them (stuck between others) - { - fix_narrow_columns(); - } - }); - } - - sync_header_actions(actions, get_visibility); -} - -void game_list::clear_list() -{ - m_last_hover_item = nullptr; - clearSelection(); - clearContents(); -} - -void game_list::fix_narrow_columns() -{ - QApplication::processEvents(); - - // handle columns (other than the icon column) that have zero width after showing them (stuck between others) - for (int col = 1; col < columnCount(); ++col) - { - if (isColumnHidden(col)) - { - continue; - } - - if (columnWidth(col) <= horizontalHeader()->minimumSectionSize()) - { - setColumnWidth(col, horizontalHeader()->minimumSectionSize()); - } - } -} - -void game_list::mousePressEvent(QMouseEvent* event) -{ - if (QTableWidgetItem* item = itemAt(event->pos()); !item || !item->data(Qt::UserRole).isValid()) - { - clearSelection(); - setCurrentItem(nullptr); // Needed for currentItemChanged - } - QTableWidget::mousePressEvent(event); -} - -void game_list::mouseMoveEvent(QMouseEvent* event) -{ - movie_item* new_item = static_cast(itemAt(event->pos())); - - if (new_item != m_last_hover_item) - { - if (m_last_hover_item) - { - m_last_hover_item->set_active(false); - } - if (new_item) - { - new_item->set_active(true); - } - } - - m_last_hover_item = new_item; - - QTableWidget::mouseMoveEvent(event); -} - -void game_list::mouseDoubleClickEvent(QMouseEvent* ev) -{ - if (!ev) - return; - - // Qt's itemDoubleClicked signal doesn't distinguish between mouse buttons and there is no simple way to get the pressed button. - // So we have to ignore this event when another button is pressed. - if (ev->button() != Qt::LeftButton) - { - ev->ignore(); - return; - } - - QTableWidget::mouseDoubleClickEvent(ev); -} - -void game_list::keyPressEvent(QKeyEvent* event) -{ - const auto modifiers = event->modifiers(); - - if (modifiers == Qt::ControlModifier && event->key() == Qt::Key_F && !event->isAutoRepeat()) - { - Q_EMIT FocusToSearchBar(); - return; - } - - QTableWidget::keyPressEvent(event); -} - -void game_list::leaveEvent(QEvent* event) -{ - if (m_last_hover_item) - { - m_last_hover_item->set_active(false); - m_last_hover_item = nullptr; - } - - QTableWidget::leaveEvent(event); -} - -void game_list::FocusAndSelectFirstEntryIfNoneIs() -{ - if (QTableWidgetItem* item = itemAt(0, 0); item && selectedIndexes().isEmpty()) - { - setCurrentItem(item); - } - - setFocus(); -} diff --git a/rpcs3qt-legacy/game_list.h b/rpcs3qt-legacy/game_list.h deleted file mode 100644 index 104047153..000000000 --- a/rpcs3qt-legacy/game_list.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "game_list_base.h" - -#include - -class movie_item; - -/* - class used in order to get deselection and hover change - if you know a simpler way, tell @Megamouse -*/ -class game_list : public QTableWidget, public game_list_base -{ - Q_OBJECT - -public: - game_list(); - - void sync_header_actions(QList& actions, std::function get_visibility); - void create_header_actions(QList& actions, std::function get_visibility, std::function set_visibility); - - void clear_list() override; // Use this instead of clearContents - - /** Fix columns with width smaller than the minimal section size */ - void fix_narrow_columns(); - -public Q_SLOTS: - void FocusAndSelectFirstEntryIfNoneIs(); - -Q_SIGNALS: - void FocusToSearchBar(); - void IconReady(const movie_item_base* item); - -protected: - movie_item* m_last_hover_item = nullptr; - - void mousePressEvent(QMouseEvent* event) override; - void mouseMoveEvent(QMouseEvent* event) override; - void mouseDoubleClickEvent(QMouseEvent* event) override; - void keyPressEvent(QKeyEvent* event) override; - void leaveEvent(QEvent* event) override; -}; diff --git a/rpcs3qt-legacy/game_list_base.cpp b/rpcs3qt-legacy/game_list_base.cpp deleted file mode 100644 index dec6926da..000000000 --- a/rpcs3qt-legacy/game_list_base.cpp +++ /dev/null @@ -1,216 +0,0 @@ -#include "stdafx.h" -#include "game_list_base.h" - -#include -#include - -#include -#include - -LOG_CHANNEL(game_list_log, "GameList"); - -game_list_base::game_list_base() -{ -} - -void game_list_base::repaint_icons(std::vector& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio) -{ - m_icon_size = icon_size; - m_icon_color = icon_color; - - QPixmap placeholder(icon_size * device_pixel_ratio); - placeholder.setDevicePixelRatio(device_pixel_ratio); - placeholder.fill(Qt::transparent); - - for (game_info& game : game_data) - { - game->pxmap = placeholder; - - if (movie_item_base* item = game->item) - { - item->set_icon_load_func([this, game, device_pixel_ratio, cancel = item->icon_loading_aborted()](int) - { - IconLoadFunction(game, device_pixel_ratio, cancel); - }); - - item->image_change_callback(); - } - } -} - -void game_list_base::IconLoadFunction(game_info game, qreal device_pixel_ratio, std::shared_ptr> cancel) -{ - if (cancel && cancel->load()) - { - return; - } - - static std::unordered_set warn_once_list; - static shared_mutex s_mtx; - - if (game->icon.isNull() && (game->info.icon_path.empty() || !game->icon.load(QString::fromStdString(game->info.icon_path)))) - { - if (game_list_log.warning) - { - bool logged = false; - { - std::lock_guard lock(s_mtx); - logged = !warn_once_list.emplace(game->info.icon_path).second; - } - - if (!logged) - { - game_list_log.warning("Could not load image from path %s", QDir(QString::fromStdString(game->info.icon_path)).absolutePath()); - } - } - } - - if (!game->item || (cancel && cancel->load())) - { - return; - } - - const QColor color = GetGridCompatibilityColor(game->compat.color); - { - std::lock_guard lock(game->item->pixmap_mutex); - game->pxmap = PaintedPixmap(game->icon, device_pixel_ratio, game->has_custom_config, game->has_custom_pad_config, color); - } - - if (!cancel || !cancel->load()) - { - if (m_icon_ready_callback) - m_icon_ready_callback(game->item); - } -} - -QPixmap game_list_base::PaintedPixmap(const QPixmap& icon, qreal device_pixel_ratio, bool paint_config_icon, bool paint_pad_config_icon, const QColor& compatibility_color) const -{ - QSize canvas_size(320, 176); - QSize icon_size(icon.size()); - QPoint target_pos; - - if (!icon.isNull()) - { - // Let's upscale the original icon to at least fit into the outer rect of the size of PS3's ICON0.PNG - if (icon_size.width() < 320 || icon_size.height() < 176) - { - icon_size.scale(320, 176, Qt::KeepAspectRatio); - } - - canvas_size = icon_size; - - // Calculate the centered size and position of the icon on our canvas. - if (icon_size.width() != 320 || icon_size.height() != 176) - { - ensure(icon_size.height() > 0); - constexpr double target_ratio = 320.0 / 176.0; // aspect ratio 20:11 - - if ((icon_size.width() / static_cast(icon_size.height())) > target_ratio) - { - canvas_size.setHeight(std::ceil(icon_size.width() / target_ratio)); - } - else - { - canvas_size.setWidth(std::ceil(icon_size.height() * target_ratio)); - } - - target_pos.setX(std::max(0, (canvas_size.width() - icon_size.width()) / 2.0)); - target_pos.setY(std::max(0, (canvas_size.height() - icon_size.height()) / 2.0)); - } - } - - // Create a canvas large enough to fit our entire scaled icon - QPixmap canvas(canvas_size * device_pixel_ratio); - canvas.setDevicePixelRatio(device_pixel_ratio); - canvas.fill(m_icon_color); - - // Create a painter for our canvas - QPainter painter(&canvas); - painter.setRenderHint(QPainter::SmoothPixmapTransform); - - // Draw the icon onto our canvas - if (!icon.isNull()) - { - painter.drawPixmap(target_pos.x(), target_pos.y(), icon_size.width(), icon_size.height(), icon); - } - - // Draw config icons if necessary - if (!m_is_list_layout && (paint_config_icon || paint_pad_config_icon)) - { - const int width = canvas_size.width() * 0.2; - const QPoint origin = QPoint(canvas_size.width() - width, 0); - QString icon_path; - - if (paint_config_icon && paint_pad_config_icon) - { - icon_path = ":/Icons/combo_config_bordered.png"; - } - else if (paint_config_icon) - { - icon_path = ":/Icons/custom_config.png"; - } - else if (paint_pad_config_icon) - { - icon_path = ":/Icons/controllers.png"; - } - - QPixmap custom_config_icon(icon_path); - custom_config_icon.setDevicePixelRatio(device_pixel_ratio); - painter.drawPixmap(origin, custom_config_icon.scaled(QSize(width, width) * device_pixel_ratio, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation)); - } - - // Draw game compatibility icons if necessary - if (compatibility_color.isValid()) - { - const int size = canvas_size.height() * 0.2; - const int spacing = canvas_size.height() * 0.05; - QColor copyColor = QColor(compatibility_color); - copyColor.setAlpha(215); // ~85% opacity - painter.setRenderHint(QPainter::Antialiasing); - painter.setBrush(QBrush(copyColor)); - painter.setPen(QPen(Qt::black, std::max(canvas_size.width() / 320, canvas_size.height() / 176))); - painter.drawEllipse(spacing, spacing, size, size); - } - - // Finish the painting - painter.end(); - - // Scale and return our final image - return canvas.scaled(m_icon_size * device_pixel_ratio, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation); -} - -QColor game_list_base::GetGridCompatibilityColor(const QString& string) const -{ - if (m_draw_compat_status_to_grid && !m_is_list_layout) - { - return QColor(string); - } - return QColor(); -} - -QIcon game_list_base::GetCustomConfigIcon(const game_info& game) -{ - if (!game) - return {}; - - static const QIcon icon_combo_config_bordered(":/Icons/combo_config_bordered.png"); - static const QIcon icon_custom_config(":/Icons/custom_config.png"); - static const QIcon icon_controllers(":/Icons/controllers.png"); - - if (game->has_custom_config && game->has_custom_pad_config) - { - return icon_combo_config_bordered; - } - - if (game->has_custom_config) - { - return icon_custom_config; - } - - if (game->has_custom_pad_config) - { - return icon_controllers; - } - - return {}; -} diff --git a/rpcs3qt-legacy/game_list_base.h b/rpcs3qt-legacy/game_list_base.h deleted file mode 100644 index 56fe8f822..000000000 --- a/rpcs3qt-legacy/game_list_base.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include "gui_game_info.h" - -#include -#include - -class game_list_base -{ -public: - game_list_base(); - - virtual void clear_list() {}; - virtual void populate( - [[maybe_unused]] const std::vector& game_data, - [[maybe_unused]] const std::map& notes_map, - [[maybe_unused]] const std::map& title_map, - [[maybe_unused]] const std::string& selected_item_id, - [[maybe_unused]] bool play_hover_movies) {}; - - void set_icon_size(QSize size) - { - m_icon_size = std::move(size); - } - void set_icon_color(QColor color) - { - m_icon_color = std::move(color); - } - void set_draw_compat_status_to_grid(bool enabled) - { - m_draw_compat_status_to_grid = enabled; - } - - virtual void repaint_icons(std::vector& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio); - - /** Sets the custom config icon. */ - static QIcon GetCustomConfigIcon(const game_info& game); - -protected: - void IconLoadFunction(game_info game, qreal device_pixel_ratio, std::shared_ptr> cancel); - QPixmap PaintedPixmap(const QPixmap& icon, qreal device_pixel_ratio, bool paint_config_icon = false, bool paint_pad_config_icon = false, const QColor& compatibility_color = {}) const; - QColor GetGridCompatibilityColor(const QString& string) const; - - std::function m_icon_ready_callback{}; - bool m_draw_compat_status_to_grid{}; - bool m_is_list_layout{}; - QSize m_icon_size{}; - QColor m_icon_color{}; -}; diff --git a/rpcs3qt-legacy/game_list_delegate.cpp b/rpcs3qt-legacy/game_list_delegate.cpp deleted file mode 100644 index 1498ccc4b..000000000 --- a/rpcs3qt-legacy/game_list_delegate.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "game_list_delegate.h" -#include "movie_item.h" -#include "gui_settings.h" - -#include - -game_list_delegate::game_list_delegate(QObject* parent) - : table_item_delegate(parent, true) -{ -} - -void game_list_delegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const -{ - table_item_delegate::paint(painter, option, index); - - // Find out if the icon or size items are visible - if (index.column() == static_cast(gui::game_list_columns::dir_size) || (m_has_icons && index.column() == static_cast(gui::game_list_columns::icon))) - { - if (const QTableWidget* table = static_cast(parent())) - { - // We need to remove the headers from our calculation. The visualItemRect starts at 0,0 while the visibleRegion doesn't. - QRegion visible_region = table->visibleRegion(); - visible_region.translate(-table->verticalHeader()->width(), -table->horizontalHeader()->height()); - - if (const QTableWidgetItem* current_item = table->item(index.row(), index.column()); - current_item && visible_region.intersects(table->visualItemRect(current_item))) - { - if (movie_item* item = static_cast(table->item(index.row(), static_cast(gui::game_list_columns::icon)))) - { - if (index.column() == static_cast(gui::game_list_columns::dir_size)) - { - if (!item->size_on_disk_loading()) - { - item->call_size_calc_func(); - } - } - else if (m_has_icons && index.column() == static_cast(gui::game_list_columns::icon)) - { - if (!item->icon_loading()) - { - item->call_icon_load_func(index.row()); - } - } - } - } - } - } -} diff --git a/rpcs3qt-legacy/game_list_delegate.h b/rpcs3qt-legacy/game_list_delegate.h deleted file mode 100644 index 60d818625..000000000 --- a/rpcs3qt-legacy/game_list_delegate.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "table_item_delegate.h" - -class game_list_delegate : public table_item_delegate -{ -public: - explicit game_list_delegate(QObject* parent); - - void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; -}; diff --git a/rpcs3qt-legacy/game_list_frame.cpp b/rpcs3qt-legacy/game_list_frame.cpp deleted file mode 100644 index 61e945e93..000000000 --- a/rpcs3qt-legacy/game_list_frame.cpp +++ /dev/null @@ -1,3070 +0,0 @@ -#include "game_list_frame.h" -#include "qt_utils.h" -#include "settings_dialog.h" -#include "pad_settings_dialog.h" -#include "input_dialog.h" -#include "localized.h" -#include "progress_dialog.h" -#include "persistent_settings.h" -#include "emu_settings.h" -#include "gui_settings.h" -#include "gui_application.h" -#include "game_list_table.h" -#include "game_list_grid.h" -#include "game_list_grid_item.h" -#include "patch_manager_dialog.h" - -#include "Emu/System.h" -#include "Emu/vfs_config.h" -#include "Emu/system_utils.hpp" -#include "Loader/PSF.h" -#include "util/types.hpp" -#include "util/File.h" -#include "util/sysinfo.hpp" -#include "Input/pad_thread.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -LOG_CHANNEL(game_list_log, "GameList"); -LOG_CHANNEL(sys_log, "SYS"); - -extern atomic_t g_system_progress_canceled; - -std::string get_savestate_file(std::string_view title_id, std::string_view boot_pat, s64 abs_id, s64 rel_id); - -game_list_frame::game_list_frame(std::shared_ptr gui_settings, std::shared_ptr emu_settings, std::shared_ptr persistent_settings, QWidget* parent) - : custom_dock_widget(tr("Game List"), parent), m_gui_settings(std::move(gui_settings)), m_emu_settings(std::move(emu_settings)), m_persistent_settings(std::move(persistent_settings)) -{ - m_icon_size = gui::gl_icon_size_min; // ensure a valid size - m_is_list_layout = m_gui_settings->GetValue(gui::gl_listMode).toBool(); - m_margin_factor = m_gui_settings->GetValue(gui::gl_marginFactor).toReal(); - m_text_factor = m_gui_settings->GetValue(gui::gl_textFactor).toReal(); - m_icon_color = m_gui_settings->GetValue(gui::gl_iconColor).value(); - m_col_sort_order = m_gui_settings->GetValue(gui::gl_sortAsc).toBool() ? Qt::AscendingOrder : Qt::DescendingOrder; - m_sort_column = m_gui_settings->GetValue(gui::gl_sortCol).toInt(); - m_hidden_list = gui::utils::list_to_set(m_gui_settings->GetValue(gui::gl_hidden_list).toStringList()); - - m_old_layout_is_list = m_is_list_layout; - - // Save factors for first setup - m_gui_settings->SetValue(gui::gl_iconColor, m_icon_color, false); - m_gui_settings->SetValue(gui::gl_marginFactor, m_margin_factor, false); - m_gui_settings->SetValue(gui::gl_textFactor, m_text_factor, true); - - m_game_dock = new QMainWindow(this); - m_game_dock->setWindowFlags(Qt::Widget); - setWidget(m_game_dock); - - m_game_grid = new game_list_grid(); - m_game_grid->installEventFilter(this); - m_game_grid->scroll_area()->verticalScrollBar()->installEventFilter(this); - - m_game_list = new game_list_table(this, m_persistent_settings); - m_game_list->installEventFilter(this); - m_game_list->verticalScrollBar()->installEventFilter(this); - - m_game_compat = new game_compatibility(m_gui_settings, this); - - m_central_widget = new QStackedWidget(this); - m_central_widget->addWidget(m_game_list); - m_central_widget->addWidget(m_game_grid); - - if (m_is_list_layout) - { - m_central_widget->setCurrentWidget(m_game_list); - } - else - { - m_central_widget->setCurrentWidget(m_game_grid); - } - - m_game_dock->setCentralWidget(m_central_widget); - - // Actions regarding showing/hiding columns - auto add_column = [this](gui::game_list_columns col, const QString& header_text, const QString& action_text) - { - m_game_list->setHorizontalHeaderItem(static_cast(col), new QTableWidgetItem(header_text)); - m_columnActs.append(new QAction(action_text, this)); - }; - - add_column(gui::game_list_columns::icon, tr("Icon"), tr("Show Icons")); - add_column(gui::game_list_columns::name, tr("Name"), tr("Show Names")); - add_column(gui::game_list_columns::serial, tr("Serial"), tr("Show Serials")); - add_column(gui::game_list_columns::firmware, tr("Firmware"), tr("Show Firmwares")); - add_column(gui::game_list_columns::version, tr("Version"), tr("Show Versions")); - add_column(gui::game_list_columns::category, tr("Category"), tr("Show Categories")); - add_column(gui::game_list_columns::path, tr("Path"), tr("Show Paths")); - add_column(gui::game_list_columns::move, tr("PlayStation Move"), tr("Show PlayStation Move")); - add_column(gui::game_list_columns::resolution, tr("Supported Resolutions"), tr("Show Supported Resolutions")); - add_column(gui::game_list_columns::sound, tr("Sound Formats"), tr("Show Sound Formats")); - add_column(gui::game_list_columns::parental, tr("Parental Level"), tr("Show Parental Levels")); - add_column(gui::game_list_columns::last_play, tr("Last Played"), tr("Show Last Played")); - add_column(gui::game_list_columns::playtime, tr("Time Played"), tr("Show Time Played")); - add_column(gui::game_list_columns::compat, tr("Compatibility"), tr("Show Compatibility")); - add_column(gui::game_list_columns::dir_size, tr("Space On Disk"), tr("Show Space On Disk")); - - m_progress_dialog = new progress_dialog(tr("Loading games"), tr("Loading games, please wait..."), tr("Cancel"), 0, 0, false, this, Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint); - m_progress_dialog->setMinimumDuration(200); // Only show the progress dialog after some time has passed - - // Events - connect(m_progress_dialog, &QProgressDialog::canceled, this, [this]() - { - gui::utils::stop_future_watcher(m_parsing_watcher, true); - gui::utils::stop_future_watcher(m_refresh_watcher, true); - - m_path_entries.clear(); - m_path_list.clear(); - m_serials.clear(); - m_game_data.clear(); - m_notes.clear(); - m_games.pop_all(); - }); - connect(&m_parsing_watcher, &QFutureWatcher::finished, this, &game_list_frame::OnParsingFinished); - connect(&m_parsing_watcher, &QFutureWatcher::canceled, this, [this]() - { - WaitAndAbortSizeCalcThreads(); - WaitAndAbortRepaintThreads(); - - m_path_entries.clear(); - m_path_list.clear(); - m_game_data.clear(); - m_serials.clear(); - m_games.pop_all(); - }); - connect(&m_refresh_watcher, &QFutureWatcher::finished, this, &game_list_frame::OnRefreshFinished); - connect(&m_refresh_watcher, &QFutureWatcher::canceled, this, [this]() - { - WaitAndAbortSizeCalcThreads(); - WaitAndAbortRepaintThreads(); - - m_path_entries.clear(); - m_path_list.clear(); - m_game_data.clear(); - m_serials.clear(); - m_games.pop_all(); - - if (m_progress_dialog) - { - m_progress_dialog->accept(); - } - }); - connect(&m_refresh_watcher, &QFutureWatcher::progressRangeChanged, this, [this](int minimum, int maximum) - { - if (m_progress_dialog) - { - m_progress_dialog->SetRange(minimum, maximum); - } - }); - connect(&m_refresh_watcher, &QFutureWatcher::progressValueChanged, this, [this](int value) - { - if (m_progress_dialog) - { - m_progress_dialog->SetValue(value); - } - }); - - connect(m_game_list, &QTableWidget::customContextMenuRequested, this, &game_list_frame::ShowContextMenu); - connect(m_game_list, &QTableWidget::itemSelectionChanged, this, &game_list_frame::ItemSelectionChangedSlot); - connect(m_game_list, &QTableWidget::itemDoubleClicked, this, QOverload::of(&game_list_frame::doubleClickedSlot)); - - connect(m_game_list->horizontalHeader(), &QHeaderView::sectionClicked, this, &game_list_frame::OnColClicked); - - connect(m_game_grid, &QWidget::customContextMenuRequested, this, &game_list_frame::ShowContextMenu); - connect(m_game_grid, &game_list_grid::ItemSelectionChanged, this, &game_list_frame::NotifyGameSelection); - connect(m_game_grid, &game_list_grid::ItemDoubleClicked, this, QOverload::of(&game_list_frame::doubleClickedSlot)); - - connect(m_game_compat, &game_compatibility::DownloadStarted, this, [this]() - { - for (const auto& game : m_game_data) - { - game->compat = m_game_compat->GetStatusData("Download"); - } - Refresh(); - }); - connect(m_game_compat, &game_compatibility::DownloadFinished, this, &game_list_frame::OnCompatFinished); - connect(m_game_compat, &game_compatibility::DownloadCanceled, this, &game_list_frame::OnCompatFinished); - connect(m_game_compat, &game_compatibility::DownloadError, this, [this](const QString& error) - { - OnCompatFinished(); - QMessageBox::warning(this, tr("Warning!"), tr("Failed to retrieve the online compatibility database!\nFalling back to local database.\n\n%0").arg(error)); - }); - - connect(m_game_list, &game_list::FocusToSearchBar, this, &game_list_frame::FocusToSearchBar); - connect(m_game_grid, &game_list_grid::FocusToSearchBar, this, &game_list_frame::FocusToSearchBar); - - m_game_list->create_header_actions(m_columnActs, [this](int col) - { - return m_gui_settings->GetGamelistColVisibility(static_cast(col)); - }, - [this](int col, bool visible) - { - m_gui_settings->SetGamelistColVisibility(static_cast(col), visible); - }); -} - -void game_list_frame::LoadSettings() -{ - m_col_sort_order = m_gui_settings->GetValue(gui::gl_sortAsc).toBool() ? Qt::AscendingOrder : Qt::DescendingOrder; - m_sort_column = m_gui_settings->GetValue(gui::gl_sortCol).toInt(); - m_category_filters = m_gui_settings->GetGameListCategoryFilters(true); - m_grid_category_filters = m_gui_settings->GetGameListCategoryFilters(false); - m_draw_compat_status_to_grid = m_gui_settings->GetValue(gui::gl_draw_compat).toBool(); - m_prefer_game_data_icons = m_gui_settings->GetValue(gui::gl_pref_gd_icon).toBool(); - m_show_custom_icons = m_gui_settings->GetValue(gui::gl_custom_icon).toBool(); - m_play_hover_movies = m_gui_settings->GetValue(gui::gl_hover_gifs).toBool(); - - m_game_list->sync_header_actions(m_columnActs, [this](int col) - { - return m_gui_settings->GetGamelistColVisibility(static_cast(col)); - }); -} - -game_list_frame::~game_list_frame() -{ - WaitAndAbortSizeCalcThreads(); - WaitAndAbortRepaintThreads(); - gui::utils::stop_future_watcher(m_parsing_watcher, true); - gui::utils::stop_future_watcher(m_refresh_watcher, true); -} - -void game_list_frame::OnColClicked(int col) -{ - if (col == static_cast(gui::game_list_columns::icon)) - return; // Don't "sort" icons. - - if (col == m_sort_column) - { - m_col_sort_order = (m_col_sort_order == Qt::AscendingOrder) ? Qt::DescendingOrder : Qt::AscendingOrder; - } - else - { - m_col_sort_order = Qt::AscendingOrder; - } - m_sort_column = col; - - m_gui_settings->SetValue(gui::gl_sortAsc, m_col_sort_order == Qt::AscendingOrder, false); - m_gui_settings->SetValue(gui::gl_sortCol, col, true); - - m_game_list->sort(m_game_data.size(), m_sort_column, m_col_sort_order); -} - -// Get visibility of entries -bool game_list_frame::IsEntryVisible(const game_info& game, bool search_fallback) const -{ - const auto matches_category = [&]() - { - if (m_is_list_layout) - { - return m_category_filters.contains(qstr(game->info.category)); - } - - return m_grid_category_filters.contains(qstr(game->info.category)); - }; - - const QString serial = qstr(game->info.serial); - const bool is_visible = m_show_hidden || !m_hidden_list.contains(serial); - return is_visible && matches_category() && SearchMatchesApp(qstr(game->info.name), serial, search_fallback); -} - -bool game_list_frame::RemoveContentPath(const std::string& path, const std::string& desc) -{ - if (!fs::exists(path)) - { - return true; - } - - if (fs::is_dir(path)) - { - if (fs::remove_all(path)) - { - game_list_log.notice("Removed '%s' directory: '%s'", desc, path); - } - else - { - game_list_log.error("Could not remove '%s' directory: '%s' (%s)", desc, path, fs::g_tls_error); - - return false; - } - } - else // If file - { - if (fs::remove_file(path)) - { - game_list_log.notice("Removed '%s' file: '%s'", desc, path); - } - else - { - game_list_log.error("Could not remove '%s' file: '%s' (%s)", desc, path, fs::g_tls_error); - - return false; - } - } - - return true; -} - -u32 game_list_frame::RemoveContentPathList(const std::vector& path_list, const std::string& desc) -{ - u32 paths_removed = 0; - - for (const std::string& path : path_list) - { - if (RemoveContentPath(path, desc)) - { - paths_removed++; - } - } - - return paths_removed; -} - -bool game_list_frame::RemoveContentBySerial(const std::string& base_dir, const std::string& serial, const std::string& desc) -{ - bool success = true; - - for (const auto& entry : fs::dir(base_dir)) - { - // Search for any path starting with serial (e.g. BCES01118_BCES01118) - if (!entry.name.starts_with(serial)) - { - continue; - } - - if (!RemoveContentPath(base_dir + entry.name, desc)) - { - success = false; // Mark as failed if there is at least one failure - } - } - - return success; -} - -std::vector game_list_frame::GetDirListBySerial(const std::string& base_dir, const std::string& serial) -{ - std::vector dir_list; - - for (const auto& entry : fs::dir(base_dir)) - { - // Check for sub folder starting with serial (e.g. BCES01118_BCES01118) - if (entry.is_directory && entry.name.starts_with(serial)) - { - dir_list.push_back(base_dir + entry.name); - } - } - - return dir_list; -} - -std::string game_list_frame::GetCacheDirBySerial(const std::string& serial) -{ - return rpcs3::utils::get_cache_dir() + (serial == "vsh.self" ? "vsh" : serial); -} - -std::string game_list_frame::GetDataDirBySerial(const std::string& serial) -{ - return fs::get_config_dir() + "data/" + serial; -} - -void game_list_frame::push_path(const std::string& path, std::vector& legit_paths) -{ - { - std::lock_guard lock(m_path_mutex); - if (!m_path_list.insert(path).second) - { - return; - } - } - legit_paths.push_back(path); -} - -void game_list_frame::Refresh(const bool from_drive, const std::vector& serials_to_remove_from_yml, const bool scroll_after) -{ - if (from_drive) - { - WaitAndAbortSizeCalcThreads(); - } - WaitAndAbortRepaintThreads(); - gui::utils::stop_future_watcher(m_parsing_watcher, from_drive); - gui::utils::stop_future_watcher(m_refresh_watcher, from_drive); - - if (m_progress_dialog && m_progress_dialog->isVisible()) - { - m_progress_dialog->SetValue(m_progress_dialog->maximum()); - m_progress_dialog->accept(); - } - - if (from_drive) - { - m_path_entries.clear(); - m_path_list.clear(); - m_serials.clear(); - m_game_data.clear(); - m_notes.clear(); - m_games.pop_all(); - - if (m_progress_dialog) - { - m_progress_dialog->SetValue(0); - } - - const std::string games_dir = rpcs3::utils::get_games_dir(); - - // List of serials (title id) to remove in "games.yml" file (if any) - std::vector serials_to_remove = serials_to_remove_from_yml; // Initialize the list with the specified serials (if any) - - // Scan game list to detect the titles belonging to auto-detection "games" folder - for (const auto& [serial, path] : Emu.GetGamesConfig().get_games()) // Loop on game list file - { - // NOTE: Used starts_with(games_dir) instead of Emu.IsPathInsideDir(path, games_dir) due the latter would check also the existence of the paths - // - if (path.starts_with(games_dir)) // If game path belongs to auto-detection "games" folder, add the serial to the removal list - { - serials_to_remove.push_back(serial); - } - } - - // Remove the specified and detected serials (title id) only from the game list in memory (not yet in "games.yml" file) - Emu.RemoveGames(serials_to_remove, false); - - // Scan auto-detection "games" folder adding the detected titles to the game list plus flushing also all the other changes in "games.yml" file - if (const u32 games_added = Emu.AddGamesFromDir(games_dir); games_added != 0) - { - game_list_log.notice("Refresh added %d new entries found in %s", games_added, games_dir); - } - - const std::string _hdd = Emu.GetCallbacks().resolve_path(rpcs3::utils::get_hdd0_dir()) + '/'; - - m_parsing_watcher.setFuture(QtConcurrent::map(m_parsing_threads, [this, _hdd](int index) - { - if (index > 0) - { - game_list_log.error("Unexpected thread index: %d", index); - return; - } - - const auto add_dir = [this](const std::string& path, bool is_disc) - { - for (const auto& entry : fs::dir(path)) - { - if (m_parsing_watcher.isCanceled()) - { - break; - } - - if (!entry.is_directory || entry.name == "." || entry.name == "..") - { - continue; - } - - std::lock_guard lock(m_path_mutex); - m_path_entries.emplace_back(path_entry{path + entry.name, is_disc, false}); - } - }; - - const std::string hdd0_game = _hdd + "game/"; - - add_dir(hdd0_game, false); - add_dir(_hdd + "disc/", true); // Deprecated - - for (const auto& [serial, path] : Emu.GetGamesConfig().get_games()) - { - if (m_parsing_watcher.isCanceled()) - { - break; - } - - std::string game_dir = path; - game_dir.resize(game_dir.find_last_not_of('/') + 1); - - if (game_dir.empty() || path.starts_with(hdd0_game)) - { - continue; - } - - // Don't use the C00 subdirectory in our game list - if (game_dir.ends_with("/C00") || game_dir.ends_with("\\C00")) - { - game_dir = game_dir.substr(0, game_dir.size() - 4); - } - - std::lock_guard lock(m_path_mutex); - m_path_entries.emplace_back(path_entry{game_dir, false, true}); - } - })); - - return; - } - - // Fill Game List / Game Grid - - const std::string selected_item = CurrentSelectionPath(); - - // Release old data - for (const auto& game : m_game_data) - { - game->item = nullptr; - } - - // Get list of matching apps - std::vector matching_apps; - - for (const auto& app : m_game_data) - { - if (IsEntryVisible(app)) - { - matching_apps.push_back(app); - } - } - - // Fallback is not needed when at least one entry is visible - if (matching_apps.empty()) - { - for (const auto& app : m_game_data) - { - if (IsEntryVisible(app, true)) - { - matching_apps.push_back(app); - } - } - } - - if (m_is_list_layout) - { - m_game_grid->clear_list(); - const int scroll_position = m_game_list->verticalScrollBar()->value(); - m_game_list->populate(matching_apps, m_notes, m_titles, selected_item, m_play_hover_movies); - m_game_list->sort(m_game_data.size(), m_sort_column, m_col_sort_order); - RepaintIcons(); - - if (scroll_after) - { - m_game_list->scrollTo(m_game_list->currentIndex(), QAbstractItemView::PositionAtCenter); - } - else - { - m_game_list->verticalScrollBar()->setValue(scroll_position); - } - } - else - { - m_game_list->clear_list(); - m_game_grid->populate(matching_apps, m_notes, m_titles, selected_item, m_play_hover_movies); - RepaintIcons(); - } -} - -void game_list_frame::OnParsingFinished() -{ - const Localized localized; - const std::string dev_flash = g_cfg_vfs.get_dev_flash(); - const std::string _hdd = rpcs3::utils::get_hdd0_dir(); - - m_path_entries.emplace_back(path_entry{dev_flash + "vsh/module/vsh.self", false, false}); - - // Remove duplicates - sort(m_path_entries.begin(), m_path_entries.end(), [](const path_entry& l, const path_entry& r) - { - return l.path < r.path; - }); - m_path_entries.erase(unique(m_path_entries.begin(), m_path_entries.end(), [](const path_entry& l, const path_entry& r) - { - return l.path == r.path; - }), - m_path_entries.end()); - - const s32 language_index = gui_application::get_language_id(); - const std::string game_icon_path = fs::get_config_dir() + "/Icons/game_icons/"; - const std::string localized_title = fmt::format("TITLE_%02d", language_index); - const std::string localized_icon = fmt::format("ICON0_%02d.PNG", language_index); - const std::string localized_movie = fmt::format("ICON1_%02d.PAM", language_index); - - const auto add_game = [this, localized_title, localized_icon, localized_movie, dev_flash, cat_unknown_localized = localized.category.unknown.toStdString(), cat_unknown = cat::cat_unknown.toStdString(), game_icon_path, _hdd, play_hover_movies = m_play_hover_movies, show_custom_icons = m_show_custom_icons](const std::string& dir_or_elf) - { - gui_game_info game{}; - game.info.path = dir_or_elf; - - const Localized thread_localized; - - const std::string sfo_dir = rpcs3::utils::get_sfo_dir_from_game_path(dir_or_elf); - const psf::registry psf = psf::load_object(sfo_dir + "/PARAM.SFO"); - const std::string_view title_id = psf::get_string(psf, "TITLE_ID", ""); - - if (title_id.empty()) - { - if (!fs::is_file(dir_or_elf)) - { - // Do not care about invalid entries - return; - } - - game.info.serial = dir_or_elf.substr(dir_or_elf.find_last_of(fs::delim) + 1); - game.info.category = cat::cat_ps3_os.toStdString(); // Key for operating system executables - game.info.version = utils::get_firmware_version(); - game.info.app_ver = game.info.version; - game.info.fw = game.info.version; - game.info.bootable = 1; - game.info.icon_path = dev_flash + "vsh/resource/explore/icon/icon_home.png"; - - if (dir_or_elf.starts_with(dev_flash)) - { - std::string path_vfs = dir_or_elf.substr(dev_flash.size()); - - if (const usz pos = path_vfs.find_first_not_of(fs::delim); pos != umax && pos != 0) - { - path_vfs = path_vfs.substr(pos); - } - - if (const auto it = thread_localized.title.titles.find(path_vfs); it != thread_localized.title.titles.cend()) - { - game.info.name = it->second.toStdString(); - } - } - - if (game.info.name.empty()) - { - game.info.name = game.info.serial; - } - } - else - { - std::string_view name = psf::get_string(psf, localized_title); - if (name.empty()) - name = psf::get_string(psf, "TITLE", cat_unknown_localized); - - game.info.serial = std::string(title_id); - game.info.name = std::string(name); - game.info.app_ver = std::string(psf::get_string(psf, "APP_VER", cat_unknown_localized)); - game.info.version = std::string(psf::get_string(psf, "VERSION", cat_unknown_localized)); - game.info.category = std::string(psf::get_string(psf, "CATEGORY", cat_unknown)); - game.info.fw = std::string(psf::get_string(psf, "PS3_SYSTEM_VER", cat_unknown_localized)); - game.info.parental_lvl = psf::get_integer(psf, "PARENTAL_LEVEL", 0); - game.info.resolution = psf::get_integer(psf, "RESOLUTION", 0); - game.info.sound_format = psf::get_integer(psf, "SOUND_FORMAT", 0); - game.info.bootable = psf::get_integer(psf, "BOOTABLE", 0); - game.info.attr = psf::get_integer(psf, "ATTRIBUTE", 0); - } - - if (show_custom_icons) - { - if (std::string icon_path = game_icon_path + game.info.serial + "/ICON0.PNG"; fs::is_file(icon_path)) - { - game.info.icon_path = std::move(icon_path); - game.has_custom_icon = true; - } - } - - if (game.info.icon_path.empty()) - { - if (std::string icon_path = sfo_dir + "/" + localized_icon; fs::is_file(icon_path)) - { - game.info.icon_path = std::move(icon_path); - } - else - { - game.info.icon_path = sfo_dir + "/ICON0.PNG"; - } - } - - if (std::string movie_path = game_icon_path + game.info.serial + "/hover.gif"; fs::is_file(movie_path)) - { - game.info.movie_path = std::move(movie_path); - game.has_hover_gif = true; - } - else if (std::string movie_path = sfo_dir + "/" + localized_movie; fs::is_file(movie_path)) - { - game.info.movie_path = std::move(movie_path); - game.has_hover_pam = true; - } - else if (std::string movie_path = sfo_dir + "/ICON1.PAM"; fs::is_file(movie_path)) - { - game.info.movie_path = std::move(movie_path); - game.has_hover_pam = true; - } - - const QString serial = QString::fromStdString(game.info.serial); - - m_games_mutex.lock(); - - // Read persistent_settings values - const QString last_played = m_persistent_settings->GetValue(gui::persistent::last_played, serial, "").toString(); - const quint64 playtime = m_persistent_settings->GetValue(gui::persistent::playtime, serial, 0).toULongLong(); - - // Set persistent_settings values if values exist - if (!last_played.isEmpty()) - { - m_persistent_settings->SetLastPlayed(serial, last_played, false); // No need to sync here. It would slow down the refresh anyway. - } - if (playtime > 0) - { - m_persistent_settings->SetPlaytime(serial, playtime, false); // No need to sync here. It would slow down the refresh anyway. - } - - m_serials.insert(serial); - - if (QString note = m_persistent_settings->GetValue(gui::persistent::notes, serial, "").toString(); !note.isEmpty()) - { - m_notes.insert_or_assign(serial, std::move(note)); - } - - if (QString title = m_persistent_settings->GetValue(gui::persistent::titles, serial, "").toString().simplified(); !title.isEmpty()) - { - m_titles.insert_or_assign(serial, std::move(title)); - } - - m_games_mutex.unlock(); - - QString qt_cat = QString::fromStdString(game.info.category); - - if (const auto boot_cat = thread_localized.category.cat_boot.find(qt_cat); boot_cat != thread_localized.category.cat_boot.cend()) - { - qt_cat = boot_cat->second; - } - else if (const auto data_cat = thread_localized.category.cat_data.find(qt_cat); data_cat != thread_localized.category.cat_data.cend()) - { - qt_cat = data_cat->second; - } - else if (game.info.category == cat_unknown) - { - qt_cat = thread_localized.category.unknown; - } - else - { - qt_cat = thread_localized.category.other; - } - - game.localized_category = std::move(qt_cat); - game.compat = m_game_compat->GetCompatibility(game.info.serial); - game.has_custom_config = fs::is_file(rpcs3::utils::get_custom_config_path(game.info.serial)); - game.has_custom_pad_config = fs::is_file(rpcs3::utils::get_custom_input_config_path(game.info.serial)); - - m_games.push(std::make_shared(std::move(game))); - }; - - const auto add_disc_dir = [this](const std::string& path, std::vector& legit_paths) - { - for (const auto& entry : fs::dir(path)) - { - if (m_refresh_watcher.isCanceled()) - { - break; - } - - if (!entry.is_directory || entry.name == "." || entry.name == "..") - { - continue; - } - - if (entry.name == "PS3_GAME" || std::regex_match(entry.name, std::regex("^PS3_GM[[:digit:]]{2}$"))) - { - push_path(path + "/" + entry.name, legit_paths); - } - } - }; - - m_refresh_watcher.setFuture(QtConcurrent::map(m_path_entries, [this, _hdd, add_disc_dir, add_game](const path_entry& entry) - { - std::vector legit_paths; - - if (entry.is_from_yml) - { - if (fs::is_file(entry.path + "/PARAM.SFO")) - { - push_path(entry.path, legit_paths); - } - else if (fs::is_file(entry.path + "/PS3_DISC.SFB")) - { - // Check if a path loaded from games.yml is already registered in add_dir(_hdd + "disc/"); - if (entry.path.starts_with(_hdd)) - { - std::string_view frag = std::string_view(entry.path).substr(_hdd.size()); - - if (frag.starts_with("disc/")) - { - // Our path starts from _hdd + 'disc/' - frag.remove_prefix(5); - - // Check if the remaining part is the only path component - if (frag.find_first_of('/') + 1 == 0) - { - game_list_log.trace("Removed duplicate: %s", entry.path); - - if (static std::unordered_set warn_once_list; warn_once_list.emplace(entry.path).second) - { - game_list_log.todo("Game at '%s' is using deprecated directory '/dev_hdd0/disc/'.\nConsider moving into '%s'.", entry.path, rpcs3::utils::get_games_dir()); - } - - return; - } - } - } - - add_disc_dir(entry.path, legit_paths); - } - else - { - game_list_log.trace("Invalid game path registered: %s", entry.path); - return; - } - } - else if (fs::is_file(entry.path + "/PS3_DISC.SFB")) - { - if (!entry.is_disc) - { - game_list_log.error("Invalid game path found in %s", entry.path); - return; - } - - add_disc_dir(entry.path, legit_paths); - } - else - { - if (entry.is_disc) - { - game_list_log.error("Invalid disc path found in %s", entry.path); - return; - } - - push_path(entry.path, legit_paths); - } - - for (const std::string& path : legit_paths) - { - add_game(path); - } - })); -} - -void game_list_frame::OnRefreshFinished() -{ - WaitAndAbortSizeCalcThreads(); - WaitAndAbortRepaintThreads(); - - for (auto&& g : m_games.pop_all()) - { - m_game_data.push_back(g); - } - - const Localized localized; - const std::string cat_unknown_localized = localized.category.unknown.toStdString(); - const s32 language_index = gui_application::get_language_id(); - const std::string localized_icon = fmt::format("ICON0_%02d.PNG", language_index); - const std::string localized_movie = fmt::format("ICON1_%02d.PAM", language_index); - - // Try to update the app version for disc games if there is a patch - // Also try to find updated game icons and movies - for (const game_info& entry : m_game_data) - { - if (entry->info.category != "DG") - continue; - - for (const auto& other : m_game_data) - { - if (other->info.category == "DG") - continue; - if (entry->info.serial != other->info.serial) - continue; - - // The patch is game data and must have the same serial and an app version - if (other->info.app_ver != cat_unknown_localized) - { - // Update the app version if it's higher than the disc's version (old games may not have an app version) - if (entry->info.app_ver == cat_unknown_localized || rpcs3::utils::version_is_bigger(other->info.app_ver, entry->info.app_ver, entry->info.serial, false)) - { - entry->info.app_ver = other->info.app_ver; - } - // Update the firmware version if possible and if it's higher than the disc's version - if (other->info.fw != cat_unknown_localized && rpcs3::utils::version_is_bigger(other->info.fw, entry->info.fw, entry->info.serial, true)) - { - entry->info.fw = other->info.fw; - } - // Update the parental level if possible and if it's higher than the disc's level - if (other->info.parental_lvl != 0 && other->info.parental_lvl > entry->info.parental_lvl) - { - entry->info.parental_lvl = other->info.parental_lvl; - } - } - - // Let's fetch the game data icon if preferred or if the path was empty for some reason - if ((m_prefer_game_data_icons && !entry->has_custom_icon) || entry->info.icon_path.empty()) - { - if (std::string icon_path = other->info.path + "/" + localized_icon; fs::is_file(icon_path)) - { - entry->info.icon_path = std::move(icon_path); - } - else if (std::string icon_path = other->info.path + "/ICON0.PNG"; fs::is_file(icon_path)) - { - entry->info.icon_path = std::move(icon_path); - } - } - - // Let's fetch the game data movie if preferred or if the path was empty - if (m_prefer_game_data_icons || entry->info.movie_path.empty()) - { - if (std::string movie_path = other->info.path + "/" + localized_movie; fs::is_file(movie_path)) - { - entry->info.movie_path = std::move(movie_path); - } - else if (std::string movie_path = other->info.path + "/ICON1.PAM"; fs::is_file(movie_path)) - { - entry->info.movie_path = std::move(movie_path); - } - } - } - } - - // Sort by name at the very least. - std::sort(m_game_data.begin(), m_game_data.end(), [&](const game_info& game1, const game_info& game2) - { - const QString serial1 = QString::fromStdString(game1->info.serial); - const QString serial2 = QString::fromStdString(game2->info.serial); - const QString& title1 = m_titles.contains(serial1) ? m_titles.at(serial1) : QString::fromStdString(game1->info.name); - const QString& title2 = m_titles.contains(serial2) ? m_titles.at(serial2) : QString::fromStdString(game2->info.name); - return title1.toLower() < title2.toLower(); - }); - - // clean up hidden games list - m_hidden_list.intersect(m_serials); - m_gui_settings->SetValue(gui::gl_hidden_list, QStringList(m_hidden_list.values())); - m_serials.clear(); - m_path_list.clear(); - m_path_entries.clear(); - - Refresh(); - - if (!std::exchange(m_initial_refresh_done, true)) - { - m_game_list->restore_layout(m_gui_settings->GetValue(gui::gl_state).toByteArray()); - m_game_list->sync_header_actions(m_columnActs, [this](int col) - { - return m_gui_settings->GetGamelistColVisibility(static_cast(col)); - }); - } - - // Emit signal and remove slots - Q_EMIT Refreshed(); - m_refresh_funcs_manage_type.reset(); - m_refresh_funcs_manage_type.emplace(); -} - -void game_list_frame::OnCompatFinished() -{ - for (const auto& game : m_game_data) - { - game->compat = m_game_compat->GetCompatibility(game->info.serial); - } - Refresh(); -} - -void game_list_frame::ToggleCategoryFilter(const QStringList& categories, bool show) -{ - QStringList& filters = m_is_list_layout ? m_category_filters : m_grid_category_filters; - - if (show) - { - filters.append(categories); - } - else - { - for (const QString& cat : categories) - { - filters.removeAll(cat); - } - } - - Refresh(); -} - -void game_list_frame::SaveSettings() -{ - for (int col = 0; col < m_columnActs.count(); ++col) - { - m_gui_settings->SetGamelistColVisibility(static_cast(col), m_columnActs[col]->isChecked()); - } - m_gui_settings->SetValue(gui::gl_sortCol, m_sort_column, false); - m_gui_settings->SetValue(gui::gl_sortAsc, m_col_sort_order == Qt::AscendingOrder, false); - m_gui_settings->SetValue(gui::gl_state, m_game_list->horizontalHeader()->saveState(), true); -} - -void game_list_frame::doubleClickedSlot(QTableWidgetItem* item) -{ - if (!item) - { - return; - } - - doubleClickedSlot(GetGameInfoByMode(item)); -} - -void game_list_frame::doubleClickedSlot(const game_info& game) -{ - if (!game) - { - return; - } - - sys_log.notice("Booting from gamelist per doubleclick..."); - Q_EMIT RequestBoot(game); -} - -void game_list_frame::ItemSelectionChangedSlot() -{ - game_info game = nullptr; - - if (m_is_list_layout) - { - if (const auto item = m_game_list->item(m_game_list->currentRow(), static_cast(gui::game_list_columns::icon)); item && item->isSelected()) - { - game = GetGameInfoByMode(item); - } - } - - Q_EMIT NotifyGameSelection(game); -} - -void game_list_frame::CreateShortcuts(const std::vector& games, const std::set& locations) -{ - if (games.empty()) - { - game_list_log.notice("Skip creating shortcuts. No games selected."); - return; - } - - if (locations.empty()) - { - game_list_log.error("Failed to create shortcuts. No locations selected."); - return; - } - - bool success = true; - - for (const game_info& gameinfo : games) - { - std::string gameid_token_value; - - const std::string dev_flash = g_cfg_vfs.get_dev_flash(); - - if (gameinfo->info.category == "DG" && !fs::is_file(rpcs3::utils::get_hdd0_dir() + "/game/" + gameinfo->info.serial + "/USRDIR/EBOOT.BIN")) - { - const usz ps3_game_dir_pos = fs::get_parent_dir(gameinfo->info.path).size(); - std::string relative_boot_dir = gameinfo->info.path.substr(ps3_game_dir_pos); - - if (usz char_pos = relative_boot_dir.find_first_not_of(fs::delim); char_pos != umax) - { - relative_boot_dir = relative_boot_dir.substr(char_pos); - } - else - { - relative_boot_dir.clear(); - } - - if (!relative_boot_dir.empty()) - { - if (relative_boot_dir != "PS3_GAME") - { - gameid_token_value = gameinfo->info.serial + "/" + relative_boot_dir; - } - else - { - gameid_token_value = gameinfo->info.serial; - } - } - } - else - { - gameid_token_value = gameinfo->info.serial; - } - -#ifdef __linux__ - const std::string target_cli_args = gameinfo->info.path.starts_with(dev_flash) ? fmt::format("--no-gui \"%%%%RPCS3_VFS%%%%:dev_flash/%s\"", gameinfo->info.path.substr(dev_flash.size())) : fmt::format("--no-gui \"%%%%RPCS3_GAMEID%%%%:%s\"", gameid_token_value); -#else - const std::string target_cli_args = gameinfo->info.path.starts_with(dev_flash) ? fmt::format("--no-gui \"%%RPCS3_VFS%%:dev_flash/%s\"", gameinfo->info.path.substr(dev_flash.size())) : fmt::format("--no-gui \"%%RPCS3_GAMEID%%:%s\"", gameid_token_value); -#endif - const std::string target_icon_dir = fmt::format("%sIcons/game_icons/%s/", fs::get_config_dir(), gameinfo->info.serial); - - if (!fs::create_path(target_icon_dir)) - { - game_list_log.error("Failed to create shortcut path %s (%s)", qstr(gameinfo->info.name).simplified(), target_icon_dir, fs::g_tls_error); - success = false; - continue; - } - - for (const gui::utils::shortcut_location& location : locations) - { - std::string destination; - - switch (location) - { - case gui::utils::shortcut_location::desktop: - destination = "desktop"; - break; - case gui::utils::shortcut_location::applications: - destination = "application menu"; - break; -#ifdef _WIN32 - case gui::utils::shortcut_location::rpcs3_shortcuts: - destination = "/games/shortcuts/"; - break; -#endif - } - - if (!gameid_token_value.empty() && gui::utils::create_shortcut(gameinfo->info.name, gameinfo->info.serial, target_cli_args, gameinfo->info.name, gameinfo->info.icon_path, target_icon_dir, location)) - { - game_list_log.success("Created %s shortcut for %s", destination, qstr(gameinfo->info.name).simplified()); - } - else - { - game_list_log.error("Failed to create %s shortcut for %s", destination, qstr(gameinfo->info.name).simplified()); - success = false; - } - } - } - -#ifdef _WIN32 - if (locations.size() == 1 && locations.contains(gui::utils::shortcut_location::rpcs3_shortcuts)) - { - return; - } -#endif - - if (success) - { - QMessageBox::information(this, tr("Success!"), tr("Successfully created shortcut(s).")); - } - else - { - QMessageBox::warning(this, tr("Warning!"), tr("Failed to create one or more shortcuts!")); - } -} - -void game_list_frame::ShowContextMenu(const QPoint& pos) -{ - QPoint global_pos; - game_info gameinfo; - - if (m_is_list_layout) - { - QTableWidgetItem* item = m_game_list->item(m_game_list->indexAt(pos).row(), static_cast(gui::game_list_columns::icon)); - global_pos = m_game_list->viewport()->mapToGlobal(pos); - gameinfo = GetGameInfoFromItem(item); - } - else if (game_list_grid_item* item = static_cast(m_game_grid->selected_item())) - { - gameinfo = item->game(); - global_pos = m_game_grid->mapToGlobal(pos); - } - - if (!gameinfo) - { - return; - } - - GameInfo current_game = gameinfo->info; - const QString serial = qstr(current_game.serial); - const QString name = qstr(current_game.name).simplified(); - - const std::string cache_base_dir = GetCacheDirBySerial(current_game.serial); - const std::string config_data_base_dir = GetDataDirBySerial(current_game.serial); - - // Make Actions - QMenu menu; - - static const auto is_game_running = [](const std::string& serial) - { - return !Emu.IsStopped(true) && (serial == Emu.GetTitleID() || (serial == "vsh.self" && Emu.IsVsh())); - }; - - const bool is_current_running_game = is_game_running(current_game.serial); - - QAction* boot = new QAction(gameinfo->has_custom_config ? (is_current_running_game ? tr("&Reboot with global configuration") : tr("&Boot with global configuration")) : (is_current_running_game ? tr("&Reboot") : tr("&Boot"))); - - QFont font = boot->font(); - font.setBold(true); - - if (gameinfo->has_custom_config) - { - QAction* boot_custom = menu.addAction(is_current_running_game ? tr("&Reboot with custom configuration") : tr("&Boot with custom configuration")); - boot_custom->setFont(font); - connect(boot_custom, &QAction::triggered, [this, gameinfo] - { - sys_log.notice("Booting from gamelist per context menu..."); - Q_EMIT RequestBoot(gameinfo); - }); - } - else - { - boot->setFont(font); - } - - menu.addAction(boot); - - { - QAction* boot_default = menu.addAction(is_current_running_game ? tr("&Reboot with default configuration") : tr("&Boot with default configuration")); - - connect(boot_default, &QAction::triggered, [this, gameinfo] - { - sys_log.notice("Booting from gamelist per context menu..."); - Q_EMIT RequestBoot(gameinfo, cfg_mode::default_config); - }); - - QAction* boot_manual = menu.addAction(is_current_running_game ? tr("&Reboot with manually selected configuration") : tr("&Boot with manually selected configuration")); - - connect(boot_manual, &QAction::triggered, [this, gameinfo] - { - if (const std::string file_path = QFileDialog::getOpenFileName(this, "Select Config File", "", tr("Config Files (*.yml);;All files (*.*)")).toStdString(); !file_path.empty()) - { - sys_log.notice("Booting from gamelist per context menu..."); - Q_EMIT RequestBoot(gameinfo, cfg_mode::custom_selection, file_path); - } - else - { - sys_log.notice("Manual config selection aborted."); - } - }); - } - - extern bool is_savestate_compatible(const std::string& filepath); - - if (const std::string sstate = get_savestate_file(current_game.serial, current_game.path, 0, 0); is_savestate_compatible(sstate)) - { - QAction* boot_state = menu.addAction(is_current_running_game ? tr("&Reboot with savestate") : tr("&Boot with savestate")); - connect(boot_state, &QAction::triggered, [this, gameinfo, sstate] - { - sys_log.notice("Booting savestate from gamelist per context menu..."); - Q_EMIT RequestBoot(gameinfo, cfg_mode::custom, "", sstate); - }); - } - - menu.addSeparator(); - - QAction* configure = menu.addAction(gameinfo->has_custom_config ? tr("&Change Custom Configuration") : tr("&Create Custom Configuration From Global Settings")); - QAction* create_game_default_config = gameinfo->has_custom_config ? nullptr : menu.addAction(tr("&Create Custom Configuration From Default Settings")); - QAction* pad_configure = menu.addAction(gameinfo->has_custom_pad_config ? tr("&Change Custom Gamepad Configuration") : tr("&Create Custom Gamepad Configuration")); - QAction* configure_patches = menu.addAction(tr("&Manage Game Patches")); - - menu.addSeparator(); - - QAction* create_cpu_cache = menu.addAction(tr("&Create LLVM Cache")); - - // Remove menu - QMenu* remove_menu = menu.addMenu(tr("&Remove")); - - if (gameinfo->has_custom_config) - { - QAction* remove_custom_config = remove_menu->addAction(tr("&Remove Custom Configuration")); - connect(remove_custom_config, &QAction::triggered, [this, current_game, gameinfo]() - { - if (RemoveCustomConfiguration(current_game.serial, gameinfo, true)) - { - ShowCustomConfigIcon(gameinfo); - } - }); - } - if (gameinfo->has_custom_pad_config) - { - QAction* remove_custom_pad_config = remove_menu->addAction(tr("&Remove Custom Gamepad Configuration")); - connect(remove_custom_pad_config, &QAction::triggered, [this, current_game, gameinfo]() - { - if (RemoveCustomPadConfiguration(current_game.serial, gameinfo, true)) - { - ShowCustomConfigIcon(gameinfo); - } - }); - } - - const bool has_cache_dir = fs::is_dir(cache_base_dir); - - if (has_cache_dir) - { - remove_menu->addSeparator(); - - QAction* remove_shaders_cache = remove_menu->addAction(tr("&Remove Shaders Cache")); - remove_shaders_cache->setEnabled(!is_current_running_game); - connect(remove_shaders_cache, &QAction::triggered, [this, cache_base_dir]() - { - RemoveShadersCache(cache_base_dir, true); - }); - QAction* remove_ppu_cache = remove_menu->addAction(tr("&Remove PPU Cache")); - remove_ppu_cache->setEnabled(!is_current_running_game); - connect(remove_ppu_cache, &QAction::triggered, [this, cache_base_dir]() - { - RemovePPUCache(cache_base_dir, true); - }); - QAction* remove_spu_cache = remove_menu->addAction(tr("&Remove SPU Cache")); - remove_spu_cache->setEnabled(!is_current_running_game); - connect(remove_spu_cache, &QAction::triggered, [this, cache_base_dir]() - { - RemoveSPUCache(cache_base_dir, true); - }); - } - - const std::string hdd1_cache_base_dir = rpcs3::utils::get_hdd1_dir() + "caches/"; - const bool has_hdd1_cache_dir = !GetDirListBySerial(hdd1_cache_base_dir, current_game.serial).empty(); - - if (has_hdd1_cache_dir) - { - QAction* remove_hdd1_cache = remove_menu->addAction(tr("&Remove HDD1 Cache")); - remove_hdd1_cache->setEnabled(!is_current_running_game); - connect(remove_hdd1_cache, &QAction::triggered, [this, hdd1_cache_base_dir, serial = current_game.serial]() - { - RemoveHDD1Cache(hdd1_cache_base_dir, serial, true); - }); - } - - if (has_cache_dir || has_hdd1_cache_dir) - { - QAction* remove_all_caches = remove_menu->addAction(tr("&Remove All Caches")); - remove_all_caches->setEnabled(!is_current_running_game); - connect(remove_all_caches, &QAction::triggered, [this, current_game, cache_base_dir, hdd1_cache_base_dir]() - { - if (is_game_running(current_game.serial)) - return; - - if (QMessageBox::question(this, tr("Confirm Removal"), tr("Remove all caches?")) != QMessageBox::Yes) - return; - - RemoveContentPath(cache_base_dir, "cache"); - RemoveHDD1Cache(hdd1_cache_base_dir, current_game.serial); - }); - } - - const std::string savestate_dir = fs::get_config_dir() + "savestates/" + current_game.serial; - - if (fs::is_dir(savestate_dir)) - { - remove_menu->addSeparator(); - - QAction* remove_savestate = remove_menu->addAction(tr("&Remove Savestates")); - remove_savestate->setEnabled(!is_current_running_game); - connect(remove_savestate, &QAction::triggered, [this, current_game, savestate_dir]() - { - if (is_game_running(current_game.serial)) - return; - - if (QMessageBox::question(this, tr("Confirm Removal"), tr("Remove savestates?")) != QMessageBox::Yes) - return; - - RemoveContentPath(savestate_dir, "savestate"); - }); - } - - // Disable the Remove menu if empty - remove_menu->setEnabled(!remove_menu->isEmpty()); - - menu.addSeparator(); - - // Manage Game menu - QMenu* manage_game_menu = menu.addMenu(tr("&Manage Game")); - - // Create game shortcuts - QAction* create_desktop_shortcut = manage_game_menu->addAction(tr("&Create Desktop Shortcut")); - connect(create_desktop_shortcut, &QAction::triggered, this, [this, gameinfo]() - { - CreateShortcuts({gameinfo}, {gui::utils::shortcut_location::desktop}); - }); -#ifdef _WIN32 - QAction* create_start_menu_shortcut = manage_game_menu->addAction(tr("&Create Start Menu Shortcut")); -#elif defined(__APPLE__) - QAction* create_start_menu_shortcut = manage_game_menu->addAction(tr("&Create Launchpad Shortcut")); -#else - QAction* create_start_menu_shortcut = manage_game_menu->addAction(tr("&Create Application Menu Shortcut")); -#endif - connect(create_start_menu_shortcut, &QAction::triggered, this, [this, gameinfo]() - { - CreateShortcuts({gameinfo}, {gui::utils::shortcut_location::applications}); - }); - - manage_game_menu->addSeparator(); - - // Hide/rename game in game list - QAction* hide_serial = manage_game_menu->addAction(tr("&Hide From Game List")); - hide_serial->setCheckable(true); - hide_serial->setChecked(m_hidden_list.contains(serial)); - QAction* rename_title = manage_game_menu->addAction(tr("&Rename In Game List")); - - // Edit tooltip notes/reset time played - QAction* edit_notes = manage_game_menu->addAction(tr("&Edit Tooltip Notes")); - QAction* reset_time_played = manage_game_menu->addAction(tr("&Reset Time Played")); - - manage_game_menu->addSeparator(); - - // Remove game - QAction* remove_game = manage_game_menu->addAction(tr("&Remove %1").arg(gameinfo->localized_category)); - remove_game->setEnabled(!is_current_running_game); - - // Custom Images menu - QMenu* icon_menu = menu.addMenu(tr("&Custom Images")); - const std::array custom_icon_actions = - { - icon_menu->addAction(tr("&Import Custom Icon")), - icon_menu->addAction(tr("&Replace Custom Icon")), - icon_menu->addAction(tr("&Remove Custom Icon"))}; - icon_menu->addSeparator(); - const std::array custom_gif_actions = - { - icon_menu->addAction(tr("&Import Hover Gif")), - icon_menu->addAction(tr("&Replace Hover Gif")), - icon_menu->addAction(tr("&Remove Hover Gif"))}; - icon_menu->addSeparator(); - const std::array custom_shader_icon_actions = - { - icon_menu->addAction(tr("&Import Custom Shader Loading Background")), - icon_menu->addAction(tr("&Replace Custom Shader Loading Background")), - icon_menu->addAction(tr("&Remove Custom Shader Loading Background"))}; - - if (const std::string custom_icon_dir_path = fs::get_config_dir() + "/Icons/game_icons/" + current_game.serial; - fs::create_path(custom_icon_dir_path)) - { - enum class icon_action - { - add, - replace, - remove - }; - enum class icon_type - { - game_list, - hover_gif, - shader_load - }; - - const auto handle_icon = [this, serial](const QString& game_icon_path, const QString& suffix, icon_action action, icon_type type) - { - QString icon_path; - - if (action != icon_action::remove) - { - QString msg; - switch (type) - { - case icon_type::game_list: - msg = tr("Select Custom Icon"); - break; - case icon_type::hover_gif: - msg = tr("Select Custom Hover Gif"); - break; - case icon_type::shader_load: - msg = tr("Select Custom Shader Loading Background"); - break; - } - icon_path = QFileDialog::getOpenFileName(this, msg, "", tr("%0 (*.%0);;All files (*.*)").arg(suffix)); - } - if (action == icon_action::remove || !icon_path.isEmpty()) - { - bool refresh = false; - - QString msg; - switch (type) - { - case icon_type::game_list: - msg = tr("Remove Custom Icon of %0?").arg(serial); - break; - case icon_type::hover_gif: - msg = tr("Remove Custom Hover Gif of %0?").arg(serial); - break; - case icon_type::shader_load: - msg = tr("Remove Custom Shader Loading Background of %0?").arg(serial); - break; - } - - if (action == icon_action::replace || (action == icon_action::remove && - QMessageBox::question(this, tr("Confirm Removal"), msg) == QMessageBox::Yes)) - { - if (QFile file(game_icon_path); file.exists() && !file.remove()) - { - game_list_log.error("Could not remove old file: '%s'", game_icon_path, file.errorString()); - QMessageBox::warning(this, tr("Warning!"), tr("Failed to remove the old file!")); - return; - } - - game_list_log.success("Removed file: '%s'", game_icon_path); - if (action == icon_action::remove) - { - refresh = true; - } - } - - if (action != icon_action::remove) - { - if (!QFile::copy(icon_path, game_icon_path)) - { - game_list_log.error("Could not import file '%s' to '%s'.", icon_path, game_icon_path); - QMessageBox::warning(this, tr("Warning!"), tr("Failed to import the new file!")); - } - else - { - game_list_log.success("Imported file '%s' to '%s'", icon_path, game_icon_path); - refresh = true; - } - } - - if (refresh) - { - Refresh(true); - } - } - }; - - const std::vector&>> icon_map = - { - {icon_type::game_list, "/ICON0.PNG", "png", custom_icon_actions}, - {icon_type::hover_gif, "/hover.gif", "gif", custom_gif_actions}, - {icon_type::shader_load, "/PIC1.PNG", "png", custom_shader_icon_actions}, - }; - - for (const auto& [type, icon_name, suffix, actions] : icon_map) - { - const QString icon_path = qstr(custom_icon_dir_path) + icon_name; - - if (QFile::exists(icon_path)) - { - actions[static_cast(icon_action::add)]->setVisible(false); - connect(actions[static_cast(icon_action::replace)], &QAction::triggered, this, [handle_icon, icon_path, t = type, s = suffix] - { - handle_icon(icon_path, s, icon_action::replace, t); - }); - connect(actions[static_cast(icon_action::remove)], &QAction::triggered, this, [handle_icon, icon_path, t = type, s = suffix] - { - handle_icon(icon_path, s, icon_action::remove, t); - }); - } - else - { - connect(actions[static_cast(icon_action::add)], &QAction::triggered, this, [handle_icon, icon_path, t = type, s = suffix] - { - handle_icon(icon_path, s, icon_action::add, t); - }); - actions[static_cast(icon_action::replace)]->setVisible(false); - actions[static_cast(icon_action::remove)]->setEnabled(false); - } - } - } - else - { - game_list_log.error("Could not create path '%s'", custom_icon_dir_path); - icon_menu->setEnabled(false); - } - - menu.addSeparator(); - - // Open Folder menu - QMenu* open_folder_menu = menu.addMenu(tr("&Open Folder")); - - const bool is_disc_game = qstr(current_game.category) == cat::cat_disc_game; - const std::string captures_dir = fs::get_config_dir() + "/captures/"; - const std::string recordings_dir = fs::get_config_dir() + "/recordings/" + current_game.serial; - const std::string screenshots_dir = fs::get_config_dir() + "/screenshots/" + current_game.serial; - std::vector data_dir_list; - - if (is_disc_game) - { - QAction* open_disc_game_folder = open_folder_menu->addAction(tr("&Open Disc Game Folder")); - connect(open_disc_game_folder, &QAction::triggered, [current_game]() - { - gui::utils::open_dir(current_game.path); - }); - - data_dir_list = GetDirListBySerial(rpcs3::utils::get_hdd0_dir() + "game/", current_game.serial); // It could be absent for a disc game - } - else - { - data_dir_list.push_back(current_game.path); - } - - if (!data_dir_list.empty()) // "true" if data path is present (it could be absent for a disc game) - { - QAction* open_data_folder = open_folder_menu->addAction(tr("&Open %0 Folder").arg(is_disc_game ? tr("Game Data") : gameinfo->localized_category)); - connect(open_data_folder, &QAction::triggered, [data_dir_list]() - { - for (const std::string& data_dir : data_dir_list) - { - gui::utils::open_dir(data_dir); - } - }); - } - - if (gameinfo->has_custom_config) - { - QAction* open_config_dir = open_folder_menu->addAction(tr("&Open Custom Config Folder")); - connect(open_config_dir, &QAction::triggered, [current_game]() - { - const std::string config_path = rpcs3::utils::get_custom_config_path(current_game.serial); - - if (fs::is_file(config_path)) - gui::utils::open_dir(config_path); - }); - } - - // This is a debug feature, let's hide it by reusing debug tab protection - if (m_gui_settings->GetValue(gui::m_showDebugTab).toBool() && has_cache_dir) - { - QAction* open_cache_folder = open_folder_menu->addAction(tr("&Open Cache Folder")); - connect(open_cache_folder, &QAction::triggered, [cache_base_dir]() - { - gui::utils::open_dir(cache_base_dir); - }); - } - - if (fs::is_dir(config_data_base_dir)) - { - QAction* open_config_data_dir = open_folder_menu->addAction(tr("&Open Config Data Folder")); - connect(open_config_data_dir, &QAction::triggered, [config_data_base_dir]() - { - gui::utils::open_dir(config_data_base_dir); - }); - } - - if (fs::is_dir(savestate_dir)) - { - QAction* open_savestate_dir = open_folder_menu->addAction(tr("&Open Savestate Folder")); - connect(open_savestate_dir, &QAction::triggered, [savestate_dir]() - { - gui::utils::open_dir(savestate_dir); - }); - } - - QAction* open_captures_dir = open_folder_menu->addAction(tr("&Open Captures Folder")); - connect(open_captures_dir, &QAction::triggered, [captures_dir]() - { - gui::utils::open_dir(captures_dir); - }); - - if (fs::is_dir(recordings_dir)) - { - QAction* open_recordings_dir = open_folder_menu->addAction(tr("&Open Recordings Folder")); - connect(open_recordings_dir, &QAction::triggered, [recordings_dir]() - { - gui::utils::open_dir(recordings_dir); - }); - } - - if (fs::is_dir(screenshots_dir)) - { - QAction* open_screenshots_dir = open_folder_menu->addAction(tr("&Open Screenshots Folder")); - connect(open_screenshots_dir, &QAction::triggered, [screenshots_dir]() - { - gui::utils::open_dir(screenshots_dir); - }); - } - - // Copy Info menu - QMenu* info_menu = menu.addMenu(tr("&Copy Info")); - QAction* copy_info = info_menu->addAction(tr("&Copy Name + Serial")); - QAction* copy_name = info_menu->addAction(tr("&Copy Name")); - QAction* copy_serial = info_menu->addAction(tr("&Copy Serial")); - - menu.addSeparator(); - - QAction* check_compat = menu.addAction(tr("&Check Game Compatibility")); - QAction* download_compat = menu.addAction(tr("&Download Compatibility Database")); - - connect(boot, &QAction::triggered, this, [this, gameinfo]() - { - sys_log.notice("Booting from gamelist per context menu..."); - Q_EMIT RequestBoot(gameinfo, cfg_mode::global); - }); - - auto configure_l = [this, current_game, gameinfo](bool create_cfg_from_global_cfg) - { - settings_dialog dlg(m_gui_settings, m_emu_settings, 0, this, ¤t_game, create_cfg_from_global_cfg); - - connect(&dlg, &settings_dialog::EmuSettingsApplied, [this, gameinfo]() - { - if (!gameinfo->has_custom_config) - { - gameinfo->has_custom_config = true; - ShowCustomConfigIcon(gameinfo); - } - Q_EMIT NotifyEmuSettingsChange(); - }); - - dlg.exec(); - }; - - if (create_game_default_config) - { - connect(configure, &QAction::triggered, this, [configure_l]() - { - configure_l(true); - }); - connect(create_game_default_config, &QAction::triggered, this, [configure_l = std::move(configure_l)]() - { - configure_l(false); - }); - } - else - { - connect(configure, &QAction::triggered, this, [configure_l = std::move(configure_l)]() - { - configure_l(true); - }); - } - - connect(pad_configure, &QAction::triggered, this, [this, current_game, gameinfo]() - { - pad_settings_dialog dlg(m_gui_settings, this, ¤t_game); - - if (dlg.exec() == QDialog::Accepted && !gameinfo->has_custom_pad_config) - { - gameinfo->has_custom_pad_config = true; - ShowCustomConfigIcon(gameinfo); - } - }); - connect(hide_serial, &QAction::triggered, this, [serial, this](bool checked) - { - if (checked) - m_hidden_list.insert(serial); - else - m_hidden_list.remove(serial); - - m_gui_settings->SetValue(gui::gl_hidden_list, QStringList(m_hidden_list.values())); - Refresh(); - }); - connect(create_cpu_cache, &QAction::triggered, this, [gameinfo, this] - { - if (m_gui_settings->GetBootConfirmation(this)) - { - CreateCPUCaches(gameinfo); - } - }); - connect(remove_game, &QAction::triggered, this, [this, current_game, gameinfo, cache_base_dir, hdd1_cache_base_dir, name] - { - if (is_game_running(current_game.serial)) - { - QMessageBox::critical(this, tr("Cannot Remove Game"), tr("The PS3 application is still running, it cannot be removed!")); - return; - } - - const bool is_disc_game = qstr(current_game.category) == cat::cat_disc_game; - const bool is_in_games_dir = is_disc_game && Emu.IsPathInsideDir(current_game.path, rpcs3::utils::get_games_dir()); - std::vector data_dir_list; - - if (is_disc_game) - { - data_dir_list = GetDirListBySerial(rpcs3::utils::get_hdd0_dir() + "game/", current_game.serial); - } - else - { - data_dir_list.push_back(current_game.path); - } - - const bool has_data_dir = !data_dir_list.empty(); // "true" if data path is present (it could be absent for a disc game) - QString text = tr("%0 - %1\n").arg(qstr(current_game.serial)).arg(name); - - if (is_disc_game) - { - text += tr("\nDisc Game Info:\nPath: %0\n").arg(qstr(current_game.path)); - - if (current_game.size_on_disk != umax) // If size was properly detected - { - text += tr("Size: %0\n").arg(gui::utils::format_byte_size(current_game.size_on_disk)); - } - } - - if (has_data_dir) - { - u64 total_data_size = 0; - - text += tr("\n%0 Info:\n").arg(is_disc_game ? tr("Game Data") : gameinfo->localized_category); - - for (const std::string& data_dir : data_dir_list) - { - text += tr("Path: %0\n").arg(qstr(data_dir)); - - if (const u64 data_size = fs::get_dir_size(data_dir, 1); data_size != umax) // If size was properly detected - { - total_data_size += data_size; - text += tr("Size: %0\n").arg(gui::utils::format_byte_size(data_size)); - } - } - - if (data_dir_list.size() > 1) - { - text += tr("Total size: %0\n").arg(gui::utils::format_byte_size(total_data_size)); - } - } - - if (fs::device_stat stat{}; fs::statfs(rpcs3::utils::get_hdd0_dir(), stat)) // Retrieve disk space info on data path's drive - { - text += tr("\nCurrent free disk space: %0\n").arg(gui::utils::format_byte_size(stat.avail_free)); - } - - if (has_data_dir) - { - text += tr("\nPermanently remove %0 and selected (optional) contents from drive?\n").arg(is_disc_game ? tr("Game Data") : gameinfo->localized_category); - } - else - { - text += tr("\nPermanently remove selected (optional) contents from drive?\n"); - } - - QMessageBox mb(QMessageBox::Question, tr("Confirm %0 Removal").arg(gameinfo->localized_category), text, QMessageBox::Yes | QMessageBox::No, this); - QCheckBox* disc = new QCheckBox(tr("Remove title from game list (Disc Game path is not removed!)")); - QCheckBox* caches = new QCheckBox(tr("Remove caches and custom configs")); - QCheckBox* icons = new QCheckBox(tr("Remove icons and shortcuts")); - QCheckBox* savestate = new QCheckBox(tr("Remove savestates")); - QCheckBox* captures = new QCheckBox(tr("Remove captures")); - QCheckBox* recordings = new QCheckBox(tr("Remove recordings")); - QCheckBox* screenshots = new QCheckBox(tr("Remove screenshots")); - - if (is_disc_game) - { - if (is_in_games_dir) - { - disc->setToolTip(tr("Title located under auto-detection \"games\" folder cannot be removed")); - disc->setDisabled(true); - } - else - { - disc->setChecked(true); - } - } - else - { - disc->setVisible(false); - } - - caches->setChecked(true); - icons->setChecked(true); - mb.setCheckBox(disc); - - QGridLayout* grid = qobject_cast(mb.layout()); - int row, column, rowSpan, columnSpan; - - grid->getItemPosition(grid->indexOf(disc), &row, &column, &rowSpan, &columnSpan); - grid->addWidget(caches, row + 3, column, rowSpan, columnSpan); - grid->addWidget(icons, row + 4, column, rowSpan, columnSpan); - grid->addWidget(savestate, row + 5, column, rowSpan, columnSpan); - grid->addWidget(captures, row + 6, column, rowSpan, columnSpan); - grid->addWidget(recordings, row + 7, column, rowSpan, columnSpan); - grid->addWidget(screenshots, row + 8, column, rowSpan, columnSpan); - - if (mb.exec() == QMessageBox::Yes) - { - const bool remove_caches = caches->isChecked(); - - // Remove data path in "dev_hdd0/game" folder (if any) - if (has_data_dir && RemoveContentPathList(data_dir_list, gameinfo->localized_category.toStdString()) != data_dir_list.size()) - { - QMessageBox::critical(this, tr("Failure!"), remove_caches ? tr("Failed to remove %0 from drive!\nPath: %1\nCaches and custom configs have been left intact.").arg(name).arg(qstr(data_dir_list[0])) : tr("Failed to remove %0 from drive!\nPath: %1").arg(name).arg(qstr(data_dir_list[0]))); - - return; - } - - // Remove lock file in "dev_hdd0/game/$locks" folder (if any) - RemoveContentBySerial(rpcs3::utils::get_hdd0_dir() + "game/$locks/", current_game.serial, "lock"); - - // Remove caches in "cache" and "dev_hdd1/caches" folders (if any) and custom configs in "config/custom_config" folder (if any) - if (remove_caches) - { - RemoveContentPath(cache_base_dir, "cache"); - RemoveHDD1Cache(hdd1_cache_base_dir, current_game.serial); - - RemoveCustomConfiguration(current_game.serial); - RemoveCustomPadConfiguration(current_game.serial); - } - - // Remove icons in "Icons/game_icons" folder, shortcuts in "games/shortcuts" folder and from desktop/start menu - if (icons->isChecked()) - { - RemoveContentBySerial(fs::get_config_dir() + "Icons/game_icons/", current_game.serial, "icons"); - RemoveContentBySerial(fs::get_config_dir() + "games/shortcuts/", name.toStdString() + ".lnk", "link"); - // TODO: Remove shortcuts from desktop/start menu - } - - if (savestate->isChecked()) - { - RemoveContentBySerial(fs::get_config_dir() + "savestates/", current_game.serial, "savestate"); - } - - if (captures->isChecked()) - { - RemoveContentBySerial(fs::get_config_dir() + "captures/", current_game.serial, "captures"); - } - - if (recordings->isChecked()) - { - RemoveContentBySerial(fs::get_config_dir() + "recordings/", current_game.serial, "recordings"); - } - - if (screenshots->isChecked()) - { - RemoveContentBySerial(fs::get_config_dir() + "screenshots/", current_game.serial, "screenshots"); - } - - m_game_data.erase(std::remove(m_game_data.begin(), m_game_data.end(), gameinfo), m_game_data.end()); - game_list_log.success("Removed %s - %s", gameinfo->localized_category, current_game.name); - - std::vector serials_to_remove_from_yml{}; - - // Prepare list of serials (title id) to remove in "games.yml" file (if any) - if (is_disc_game && disc->isChecked()) - { - serials_to_remove_from_yml.push_back(current_game.serial); - } - - // Finally, refresh the game list. - // Hidden list in "GuiConfigs/CurrentSettings.ini" file is also properly updated (title removed) if needed - Refresh(true, serials_to_remove_from_yml); - } - }); - connect(configure_patches, &QAction::triggered, this, [this, gameinfo]() - { - patch_manager_dialog patch_manager(m_gui_settings, m_game_data, gameinfo->info.serial, gameinfo->GetGameVersion(), this); - patch_manager.exec(); - }); - connect(check_compat, &QAction::triggered, this, [serial] - { - const QString link = "https://rpcs3.net/compatibility?g=" + serial; - QDesktopServices::openUrl(QUrl(link)); - }); - connect(download_compat, &QAction::triggered, this, [this] - { - m_game_compat->RequestCompatibility(true); - }); - connect(rename_title, &QAction::triggered, this, [this, name, serial, global_pos] - { - const QString custom_title = m_persistent_settings->GetValue(gui::persistent::titles, serial, "").toString(); - const QString old_title = custom_title.isEmpty() ? name : custom_title; - - input_dialog dlg(128, old_title, tr("Rename Title"), tr("%0\n%1\n\nYou can clear the line in order to use the original title.").arg(name).arg(serial), name, this); - dlg.move(global_pos); - - if (dlg.exec() == QDialog::Accepted) - { - const QString new_title = dlg.get_input_text().simplified(); - - if (new_title.isEmpty() || new_title == name) - { - m_titles.erase(serial); - m_persistent_settings->RemoveValue(gui::persistent::titles, serial); - } - else - { - m_titles.insert_or_assign(serial, new_title); - m_persistent_settings->SetValue(gui::persistent::titles, serial, new_title); - } - Refresh(true); // full refresh in order to reliably sort the list - } - }); - connect(edit_notes, &QAction::triggered, this, [this, name, serial] - { - bool accepted; - const QString old_notes = m_persistent_settings->GetValue(gui::persistent::notes, serial, "").toString(); - const QString new_notes = QInputDialog::getMultiLineText(this, tr("Edit Tooltip Notes"), tr("%0\n%1").arg(name).arg(serial), old_notes, &accepted); - - if (accepted) - { - if (new_notes.simplified().isEmpty()) - { - m_notes.erase(serial); - m_persistent_settings->RemoveValue(gui::persistent::notes, serial); - } - else - { - m_notes.insert_or_assign(serial, new_notes); - m_persistent_settings->SetValue(gui::persistent::notes, serial, new_notes); - } - Refresh(); - } - }); - connect(reset_time_played, &QAction::triggered, this, [this, name, serial] - { - if (QMessageBox::question(this, tr("Confirm Reset"), tr("Reset time played?\n\n%0 [%1]").arg(name).arg(serial)) == QMessageBox::Yes) - { - m_persistent_settings->SetPlaytime(serial, 0, false); - m_persistent_settings->SetLastPlayed(serial, 0, true); - Refresh(); - } - }); - connect(copy_info, &QAction::triggered, this, [name, serial] - { - QApplication::clipboard()->setText(name % QStringLiteral(" [") % serial % QStringLiteral("]")); - }); - connect(copy_name, &QAction::triggered, this, [name] - { - QApplication::clipboard()->setText(name); - }); - connect(copy_serial, &QAction::triggered, this, [serial] - { - QApplication::clipboard()->setText(serial); - }); - - // Disable options depending on software category - const QString category = qstr(current_game.category); - - if (category == cat::cat_ps3_os) - { - remove_game->setEnabled(false); - } - else if (category != cat::cat_disc_game && category != cat::cat_hdd_game) - { - check_compat->setEnabled(false); - } - - menu.exec(global_pos); -} - -bool game_list_frame::CreateCPUCaches(const std::string& path, const std::string& serial) -{ - Emu.GracefulShutdown(false); - Emu.SetForceBoot(true); - - if (const auto error = Emu.BootGame(fs::is_file(path) ? fs::get_parent_dir(path) : path, serial, true); error != game_boot_result::no_errors) - { - game_list_log.error("Could not create LLVM caches for %s, error: %s", path, error); - return false; - } - - game_list_log.warning("Creating LLVM Caches for %s", path); - return true; -} - -bool game_list_frame::CreateCPUCaches(const game_info& game) -{ - return game && CreateCPUCaches(game->info.path, game->info.serial); -} - -bool game_list_frame::RemoveCustomConfiguration(const std::string& title_id, const game_info& game, bool is_interactive) -{ - const std::string path = rpcs3::utils::get_custom_config_path(title_id); - - if (!fs::is_file(path)) - return true; - - if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), tr("Remove custom game configuration?")) != QMessageBox::Yes) - return true; - - bool result = true; - - if (fs::is_file(path)) - { - if (fs::remove_file(path)) - { - if (game) - { - game->has_custom_config = false; - } - game_list_log.success("Removed configuration file: %s", path); - } - else - { - game_list_log.fatal("Failed to remove configuration file: %s\nError: %s", path, fs::g_tls_error); - result = false; - } - } - - if (is_interactive && !result) - { - QMessageBox::warning(this, tr("Warning!"), tr("Failed to remove configuration file!")); - } - - return result; -} - -bool game_list_frame::RemoveCustomPadConfiguration(const std::string& title_id, const game_info& game, bool is_interactive) -{ - if (title_id.empty()) - return true; - - const std::string config_dir = rpcs3::utils::get_input_config_dir(title_id); - - if (!fs::is_dir(config_dir)) - return true; - - if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), (!Emu.IsStopped(true) && Emu.GetTitleID() == title_id) ? tr("Remove custom pad configuration?\nYour configuration will revert to the global pad settings.") : tr("Remove custom pad configuration?")) != QMessageBox::Yes) - return true; - - g_cfg_input_configs.load(); - g_cfg_input_configs.active_configs.erase(title_id); - g_cfg_input_configs.save(); - game_list_log.notice("Removed active input configuration entry for key '%s'", title_id); - - if (QDir(qstr(config_dir)).removeRecursively()) - { - if (game) - { - game->has_custom_pad_config = false; - } - if (!Emu.IsStopped(true) && Emu.GetTitleID() == title_id) - { - pad::set_enabled(false); - pad::reset(title_id); - pad::set_enabled(true); - } - game_list_log.notice("Removed pad configuration directory: %s", config_dir); - return true; - } - - if (is_interactive) - { - QMessageBox::warning(this, tr("Warning!"), tr("Failed to completely remove pad configuration directory!")); - game_list_log.fatal("Failed to completely remove pad configuration directory: %s\nError: %s", config_dir, fs::g_tls_error); - } - return false; -} - -bool game_list_frame::RemoveShadersCache(const std::string& base_dir, bool is_interactive) -{ - if (!fs::is_dir(base_dir)) - return true; - - if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), tr("Remove shaders cache?")) != QMessageBox::Yes) - return true; - - u32 caches_removed = 0; - u32 caches_total = 0; - - const QStringList filter{QStringLiteral("shaders_cache")}; - const QString q_base_dir = qstr(base_dir); - - QDirIterator dir_iter(q_base_dir, filter, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); - - while (dir_iter.hasNext()) - { - const QString filepath = dir_iter.next(); - - if (QDir(filepath).removeRecursively()) - { - ++caches_removed; - game_list_log.notice("Removed shaders cache dir: %s", filepath); - } - else - { - game_list_log.warning("Could not completely remove shaders cache dir: %s", filepath); - } - - ++caches_total; - } - - const bool success = caches_total == caches_removed; - - if (success) - game_list_log.success("Removed shaders cache in %s", base_dir); - else - game_list_log.fatal("Only %d/%d shaders cache dirs could be removed in %s", caches_removed, caches_total, base_dir); - - if (QDir(q_base_dir).isEmpty()) - { - if (fs::remove_dir(base_dir)) - game_list_log.notice("Removed empty shader cache directory: %s", base_dir); - else - game_list_log.error("Could not remove empty shader cache directory: '%s' (%s)", base_dir, fs::g_tls_error); - } - - return success; -} - -bool game_list_frame::RemovePPUCache(const std::string& base_dir, bool is_interactive) -{ - if (!fs::is_dir(base_dir)) - return true; - - if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), tr("Remove PPU cache?")) != QMessageBox::Yes) - return true; - - u32 files_removed = 0; - u32 files_total = 0; - - const QStringList filter{QStringLiteral("v*.obj"), QStringLiteral("v*.obj.gz")}; - const QString q_base_dir = qstr(base_dir); - - QDirIterator dir_iter(q_base_dir, filter, QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); - - while (dir_iter.hasNext()) - { - const QString filepath = dir_iter.next(); - - if (QFile::remove(filepath)) - { - ++files_removed; - game_list_log.notice("Removed PPU cache file: %s", filepath); - } - else - { - game_list_log.warning("Could not remove PPU cache file: %s", filepath); - } - - ++files_total; - } - - const bool success = files_total == files_removed; - - if (success) - game_list_log.success("Removed PPU cache in %s", base_dir); - else - game_list_log.fatal("Only %d/%d PPU cache files could be removed in %s", files_removed, files_total, base_dir); - - if (QDir(q_base_dir).isEmpty()) - { - if (fs::remove_dir(base_dir)) - game_list_log.notice("Removed empty PPU cache directory: %s", base_dir); - else - game_list_log.error("Could not remove empty PPU cache directory: '%s' (%s)", base_dir, fs::g_tls_error); - } - - return success; -} - -bool game_list_frame::RemoveSPUCache(const std::string& base_dir, bool is_interactive) -{ - if (!fs::is_dir(base_dir)) - return true; - - if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), tr("Remove SPU cache?")) != QMessageBox::Yes) - return true; - - u32 files_removed = 0; - u32 files_total = 0; - - const QStringList filter{QStringLiteral("spu*.dat"), QStringLiteral("spu*.dat.gz"), QStringLiteral("spu*.obj"), QStringLiteral("spu*.obj.gz")}; - const QString q_base_dir = qstr(base_dir); - - QDirIterator dir_iter(q_base_dir, filter, QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); - - while (dir_iter.hasNext()) - { - const QString filepath = dir_iter.next(); - - if (QFile::remove(filepath)) - { - ++files_removed; - game_list_log.notice("Removed SPU cache file: %s", filepath); - } - else - { - game_list_log.warning("Could not remove SPU cache file: %s", filepath); - } - - ++files_total; - } - - const bool success = files_total == files_removed; - - if (success) - game_list_log.success("Removed SPU cache in %s", base_dir); - else - game_list_log.fatal("Only %d/%d SPU cache files could be removed in %s", files_removed, files_total, base_dir); - - if (QDir(q_base_dir).isEmpty()) - { - if (fs::remove_dir(base_dir)) - game_list_log.notice("Removed empty SPU cache directory: %s", base_dir); - else - game_list_log.error("Could not remove empty SPU cache directory: '%s' (%s)", base_dir, fs::g_tls_error); - } - - return success; -} - -void game_list_frame::RemoveHDD1Cache(const std::string& base_dir, const std::string& title_id, bool is_interactive) -{ - if (!fs::is_dir(base_dir)) - return; - - if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), tr("Remove HDD1 cache?")) != QMessageBox::Yes) - return; - - u32 dirs_removed = 0; - u32 dirs_total = 0; - - const QString q_base_dir = qstr(base_dir); - - const QStringList filter{qstr(title_id + "_*")}; - - QDirIterator dir_iter(q_base_dir, filter, QDir::Dirs | QDir::NoDotAndDotDot); - - while (dir_iter.hasNext()) - { - const QString filepath = dir_iter.next(); - - if (fs::remove_all(filepath.toStdString())) - { - ++dirs_removed; - game_list_log.notice("Removed HDD1 cache directory: %s", filepath); - } - else - { - game_list_log.warning("Could not remove HDD1 cache directory: %s", filepath); - } - - ++dirs_total; - } - - const bool success = dirs_removed == dirs_total; - - if (success) - game_list_log.success("Removed HDD1 cache in %s (%s)", base_dir, title_id); - else - game_list_log.fatal("Only %d/%d HDD1 cache directories could be removed in %s (%s)", dirs_removed, dirs_total, base_dir, title_id); -} - -void game_list_frame::BatchActionBySerials(progress_dialog* pdlg, const std::set& serials, QString progressLabel, std::function action, std::function cancel_log, bool refresh_on_finish, bool can_be_concurrent, std::function should_wait_cb) -{ - // Concurrent tasks should not wait (at least not in current implementation) - ensure(!should_wait_cb || !can_be_concurrent); - - g_system_progress_canceled = false; - - const std::shared_ptr> iterate_over_serial = std::make_shared>(); - - const std::shared_ptr> index = std::make_shared>(0); - - const int serials_size = ::narrow(serials.size()); - - *iterate_over_serial = [=, this, index_ptr = index](int index) - { - if (index == serials_size) - { - return false; - } - - const std::string& serial = *std::next(serials.begin(), index); - - if (pdlg->wasCanceled() || g_system_progress_canceled.exchange(false)) - { - if (cancel_log) - { - cancel_log(index, serials_size); - } - return false; - } - - if (action(serial)) - { - const int done = index_ptr->load(); - pdlg->setLabelText(progressLabel.arg(done + 1).arg(serials_size)); - pdlg->SetValue(done + 1); - } - - (*index_ptr)++; - return true; - }; - - if (can_be_concurrent) - { - // Unused currently - - QList indices; - - for (int i = 0; i < serials_size; i++) - { - indices.append(i); - } - - QFutureWatcher* future_watcher = new QFutureWatcher(this); - - future_watcher->setFuture(QtConcurrent::map(std::move(indices), *iterate_over_serial)); - - connect(future_watcher, &QFutureWatcher::finished, this, [=, this]() - { - pdlg->setLabelText(progressLabel.arg(*index).arg(serials_size)); - pdlg->setCancelButtonText(tr("OK")); - QApplication::beep(); - - if (refresh_on_finish && index) - { - Refresh(true); - } - - future_watcher->deleteLater(); - }); - - return; - } - - const std::shared_ptr> periodic_func = std::make_shared>(); - - *periodic_func = [=, this]() - { - if (should_wait_cb && should_wait_cb()) - { - // Conditions are not met for execution - // Check again later - QTimer::singleShot(5, this, *periodic_func); - return; - } - - if ((*iterate_over_serial)(*index)) - { - QTimer::singleShot(1, this, *periodic_func); - return; - } - - pdlg->setLabelText(progressLabel.arg(*index).arg(serials_size)); - pdlg->setCancelButtonText(tr("OK")); - connect(pdlg, &progress_dialog::canceled, this, [pdlg]() - { - pdlg->deleteLater(); - }); - QApplication::beep(); - - if (refresh_on_finish && index) - { - Refresh(true); - } - }; - - // Invoked on the next event loop processing iteration - QTimer::singleShot(1, this, *periodic_func); -} - -void game_list_frame::BatchCreateCPUCaches(const std::vector& game_data) -{ - std::set serials; - - if (game_data.empty()) - { - serials.emplace("vsh.self"); - } - - for (const auto& game : (game_data.empty() ? m_game_data : game_data)) - { - serials.emplace(game->info.serial); - } - - const usz total = serials.size(); - - if (total == 0) - { - QMessageBox::information(this, tr("LLVM Cache Batch Creation"), tr("No titles found"), QMessageBox::Ok); - return; - } - - if (!m_gui_settings->GetBootConfirmation(this)) - { - return; - } - - const QString main_label = tr("Creating all LLVM caches"); - - progress_dialog* pdlg = new progress_dialog(tr("LLVM Cache Batch Creation"), main_label, tr("Cancel"), 0, ::narrow(total), false, this); - pdlg->setAutoClose(false); - pdlg->setAutoReset(false); - pdlg->open(); - - connect(pdlg, &progress_dialog::canceled, this, []() - { - if (!Emu.IsStopped()) - { - Emu.GracefulShutdown(false, true); - } - }); - - BatchActionBySerials(pdlg, serials, tr("%0\nProgress: %1/%2 caches compiled").arg(main_label), [&, game_data](const std::string& serial) - { - if (Emu.IsStopped(true)) - { - const auto it = std::find_if(m_game_data.begin(), m_game_data.end(), FN(x->info.serial == serial)); - - if (it != m_game_data.end()) - { - return CreateCPUCaches((*it)->info.path, serial); - } - } - - return false; - }, - [this](u32, u32) - { - game_list_log.notice("LLVM Cache Batch Creation was canceled"); - }, - false, false, []() - { - return !Emu.IsStopped(true); - }); -} - -void game_list_frame::BatchRemovePPUCaches() -{ - if (Emu.GetStatus(false) != system_state::stopped) - { - return; - } - - std::set serials; - serials.emplace("vsh.self"); - - for (const auto& game : m_game_data) - { - serials.emplace(game->info.serial); - } - - const u32 total = ::size32(serials); - - if (total == 0) - { - QMessageBox::information(this, tr("PPU Cache Batch Removal"), tr("No files found"), QMessageBox::Ok); - return; - } - - progress_dialog* pdlg = new progress_dialog(tr("PPU Cache Batch Removal"), tr("Removing all PPU caches"), tr("Cancel"), 0, total, false, this); - pdlg->setAutoClose(false); - pdlg->setAutoReset(false); - pdlg->open(); - - BatchActionBySerials(pdlg, serials, tr("%0/%1 caches cleared"), [this](const std::string& serial) - { - return Emu.IsStopped(true) && RemovePPUCache(GetCacheDirBySerial(serial)); - }, - [this](u32, u32) - { - game_list_log.notice("PPU Cache Batch Removal was canceled"); - }, - false); -} - -void game_list_frame::BatchRemoveSPUCaches() -{ - if (Emu.GetStatus(false) != system_state::stopped) - { - return; - } - - std::set serials; - serials.emplace("vsh.self"); - - for (const auto& game : m_game_data) - { - serials.emplace(game->info.serial); - } - - const u32 total = ::size32(serials); - - if (total == 0) - { - QMessageBox::information(this, tr("SPU Cache Batch Removal"), tr("No files found"), QMessageBox::Ok); - return; - } - - progress_dialog* pdlg = new progress_dialog(tr("SPU Cache Batch Removal"), tr("Removing all SPU caches"), tr("Cancel"), 0, total, false, this); - pdlg->setAutoClose(false); - pdlg->setAutoReset(false); - pdlg->open(); - - BatchActionBySerials(pdlg, serials, tr("%0/%1 caches cleared"), [this](const std::string& serial) - { - return Emu.IsStopped(true) && RemoveSPUCache(GetCacheDirBySerial(serial)); - }, - [this](u32 removed, u32 total) - { - game_list_log.notice("SPU Cache Batch Removal was canceled. %d/%d folders cleared", removed, total); - }, - false); -} - -void game_list_frame::BatchRemoveCustomConfigurations() -{ - std::set serials; - for (const auto& game : m_game_data) - { - if (game->has_custom_config && !serials.count(game->info.serial)) - { - serials.emplace(game->info.serial); - } - } - - const u32 total = ::size32(serials); - - if (total == 0) - { - QMessageBox::information(this, tr("Custom Configuration Batch Removal"), tr("No files found"), QMessageBox::Ok); - return; - } - - progress_dialog* pdlg = new progress_dialog(tr("Custom Configuration Batch Removal"), tr("Removing all custom configurations"), tr("Cancel"), 0, total, false, this); - pdlg->setAutoClose(false); - pdlg->setAutoReset(false); - pdlg->open(); - - BatchActionBySerials(pdlg, serials, tr("%0/%1 custom configurations cleared"), [this](const std::string& serial) - { - return Emu.IsStopped(true) && RemoveCustomConfiguration(serial); - }, - [this](u32 removed, u32 total) - { - game_list_log.notice("Custom Configuration Batch Removal was canceled. %d/%d custom configurations cleared", removed, total); - }, - true); -} - -void game_list_frame::BatchRemoveCustomPadConfigurations() -{ - std::set serials; - for (const auto& game : m_game_data) - { - if (game->has_custom_pad_config && !serials.count(game->info.serial)) - { - serials.emplace(game->info.serial); - } - } - const u32 total = ::size32(serials); - - if (total == 0) - { - QMessageBox::information(this, tr("Custom Pad Configuration Batch Removal"), tr("No files found"), QMessageBox::Ok); - return; - } - - progress_dialog* pdlg = new progress_dialog(tr("Custom Pad Configuration Batch Removal"), tr("Removing all custom pad configurations"), tr("Cancel"), 0, total, false, this); - pdlg->setAutoClose(false); - pdlg->setAutoReset(false); - pdlg->open(); - - BatchActionBySerials(pdlg, serials, tr("%0/%1 custom pad configurations cleared"), [this](const std::string& serial) - { - return Emu.IsStopped(true) && RemoveCustomPadConfiguration(serial); - }, - [this](u32 removed, u32 total) - { - game_list_log.notice("Custom Pad Configuration Batch Removal was canceled. %d/%d custom pad configurations cleared", removed, total); - }, - true); -} - -void game_list_frame::BatchRemoveShaderCaches() -{ - if (Emu.GetStatus(false) != system_state::stopped) - { - return; - } - - std::set serials; - serials.emplace("vsh.self"); - - for (const auto& game : m_game_data) - { - serials.emplace(game->info.serial); - } - - const u32 total = ::size32(serials); - - if (total == 0) - { - QMessageBox::information(this, tr("Shader Cache Batch Removal"), tr("No files found"), QMessageBox::Ok); - return; - } - - progress_dialog* pdlg = new progress_dialog(tr("Shader Cache Batch Removal"), tr("Removing all shader caches"), tr("Cancel"), 0, total, false, this); - pdlg->setAutoClose(false); - pdlg->setAutoReset(false); - pdlg->open(); - - BatchActionBySerials(pdlg, serials, tr("%0/%1 shader caches cleared"), [this](const std::string& serial) - { - return Emu.IsStopped(true) && RemoveShadersCache(GetCacheDirBySerial(serial)); - }, - [this](u32 removed, u32 total) - { - game_list_log.notice("Shader Cache Batch Removal was canceled. %d/%d cleared", removed, total); - }, - false); -} - -void game_list_frame::ShowCustomConfigIcon(const game_info& game) -{ - if (!game) - { - return; - } - - const std::string serial = game->info.serial; - const bool has_custom_config = game->has_custom_config; - const bool has_custom_pad_config = game->has_custom_pad_config; - - for (const auto& other_game : m_game_data) - { - if (other_game->info.serial == serial) - { - other_game->has_custom_config = has_custom_config; - other_game->has_custom_pad_config = has_custom_pad_config; - } - } - - m_game_list->set_custom_config_icon(game); - - RepaintIcons(); -} - -void game_list_frame::ResizeIcons(const int& slider_pos) -{ - m_icon_size_index = slider_pos; - m_icon_size = gui_settings::SizeFromSlider(slider_pos); - - RepaintIcons(); -} - -void game_list_frame::RepaintIcons(const bool& from_settings) -{ - gui::utils::stop_future_watcher(m_parsing_watcher, false); - gui::utils::stop_future_watcher(m_refresh_watcher, false); - WaitAndAbortRepaintThreads(); - - if (from_settings) - { - if (m_gui_settings->GetValue(gui::m_enableUIColors).toBool()) - { - m_icon_color = m_gui_settings->GetValue(gui::gl_iconColor).value(); - } - else - { - m_icon_color = gui::utils::get_label_color("gamelist_icon_background_color", Qt::transparent, Qt::transparent); - } - } - - if (m_is_list_layout) - { - m_game_list->repaint_icons(m_game_data, m_icon_color, m_icon_size, devicePixelRatioF()); - } - else - { - m_game_grid->set_draw_compat_status_to_grid(m_draw_compat_status_to_grid); - m_game_grid->repaint_icons(m_game_data, m_icon_color, m_icon_size, devicePixelRatioF()); - } -} - -void game_list_frame::SetShowHidden(bool show) -{ - m_show_hidden = show; -} - -void game_list_frame::SetListMode(const bool& is_list) -{ - m_old_layout_is_list = m_is_list_layout; - m_is_list_layout = is_list; - - m_gui_settings->SetValue(gui::gl_listMode, is_list); - - Refresh(); - - if (m_is_list_layout) - { - m_central_widget->setCurrentWidget(m_game_list); - } - else - { - m_central_widget->setCurrentWidget(m_game_grid); - } -} - -void game_list_frame::SetSearchText(const QString& text) -{ - m_search_text = text; - Refresh(); -} - -void game_list_frame::FocusAndSelectFirstEntryIfNoneIs() -{ - if (m_is_list_layout) - { - if (m_game_list) - { - m_game_list->FocusAndSelectFirstEntryIfNoneIs(); - } - } - else - { - if (m_game_grid) - { - m_game_grid->FocusAndSelectFirstEntryIfNoneIs(); - } - } -} - -void game_list_frame::closeEvent(QCloseEvent* event) -{ - SaveSettings(); - - QDockWidget::closeEvent(event); - Q_EMIT GameListFrameClosed(); -} - -bool game_list_frame::eventFilter(QObject* object, QEvent* event) -{ - // Zoom gamelist/gamegrid - if (event->type() == QEvent::Wheel && (object == m_game_list->verticalScrollBar() || object == m_game_grid->scroll_area()->verticalScrollBar())) - { - QWheelEvent* wheel_event = static_cast(event); - - if (wheel_event->modifiers() & Qt::ControlModifier) - { - const QPoint num_steps = wheel_event->angleDelta() / 8 / 15; // http://doc.qt.io/qt-5/qwheelevent.html#pixelDelta - const int value = num_steps.y(); - Q_EMIT RequestIconSizeChange(value); - return true; - } - } - else if (event->type() == QEvent::KeyPress && (object == m_game_list || object == m_game_grid)) - { - QKeyEvent* key_event = static_cast(event); - - if (key_event->modifiers() & Qt::ControlModifier) - { - if (key_event->key() == Qt::Key_Plus) - { - Q_EMIT RequestIconSizeChange(1); - return true; - } - if (key_event->key() == Qt::Key_Minus) - { - Q_EMIT RequestIconSizeChange(-1); - return true; - } - } - else if (!key_event->isAutoRepeat()) - { - if (key_event->key() == Qt::Key_Enter || key_event->key() == Qt::Key_Return) - { - game_info gameinfo{}; - - if (object == m_game_list) - { - QTableWidgetItem* item = m_game_list->item(m_game_list->currentRow(), static_cast(gui::game_list_columns::icon)); - - if (!item || !item->isSelected()) - return false; - - gameinfo = GetGameInfoFromItem(item); - } - else if (game_list_grid_item* item = static_cast(m_game_grid->selected_item())) - { - gameinfo = item->game(); - } - - if (!gameinfo) - return false; - - sys_log.notice("Booting from gamelist by pressing %s...", key_event->key() == Qt::Key_Enter ? "Enter" : "Return"); - Q_EMIT RequestBoot(gameinfo); - - return true; - } - } - } - - return QDockWidget::eventFilter(object, event); -} - -/** - * Returns false if the game should be hidden because it doesn't match search term in toolbar. - */ -bool game_list_frame::SearchMatchesApp(const QString& name, const QString& serial, bool fallback) const -{ - if (!m_search_text.isEmpty()) - { - QString search_text = m_search_text.toLower(); - QString title_name; - - if (const auto it = m_titles.find(serial); it != m_titles.cend()) - { - title_name = it->second.toLower(); - } - else - { - title_name = name.toLower(); - } - - // Ignore trademarks when no search results have been yielded by unmodified search - static const QRegularExpression s_ignored_on_fallback(reinterpret_cast(u8"[:\\-®©™]+")); - - if (fallback) - { - search_text = search_text.simplified(); - title_name = title_name.simplified(); - - QString title_name_replaced_trademarks_with_spaces = title_name; - QString title_name_simplified = title_name; - - search_text.remove(s_ignored_on_fallback); - title_name.remove(s_ignored_on_fallback); - title_name_replaced_trademarks_with_spaces.replace(s_ignored_on_fallback, " "); - - // Before simplify to allow spaces in the beginning and end where ignored characters may have been - if (title_name_replaced_trademarks_with_spaces.contains(search_text)) - { - return true; - } - - title_name_replaced_trademarks_with_spaces = title_name_replaced_trademarks_with_spaces.simplified(); - - if (title_name_replaced_trademarks_with_spaces.contains(search_text)) - { - return true; - } - - // Initials-only search - if (search_text.size() >= 2 && search_text.count(QRegularExpression(QStringLiteral("[a-z0-9]"))) >= 2 && !search_text.contains(QRegularExpression(QStringLiteral("[^a-z0-9 ]")))) - { - QString initials = QStringLiteral("\\b"); - - for (auto it = search_text.begin(); it != search_text.end(); it++) - { - if (it->isSpace()) - { - continue; - } - - initials += *it; - initials += QStringLiteral("\\w*\\b "); - } - - initials += QChar('?'); - - if (title_name_replaced_trademarks_with_spaces.contains(QRegularExpression(initials))) - { - return true; - } - } - } - - return title_name.contains(search_text) || serial.toLower().contains(search_text); - } - return true; -} - -std::string game_list_frame::CurrentSelectionPath() -{ - std::string selection; - - game_info game{}; - - if (m_old_layout_is_list) - { - if (!m_game_list->selectedItems().isEmpty()) - { - if (QTableWidgetItem* item = m_game_list->item(m_game_list->currentRow(), 0)) - { - if (const QVariant var = item->data(gui::game_role); var.canConvert()) - { - game = var.value(); - } - } - } - } - else if (m_game_grid) - { - if (game_list_grid_item* item = static_cast(m_game_grid->selected_item())) - { - game = item->game(); - } - } - - if (game) - { - selection = game->info.path + game->info.icon_path; - } - - m_old_layout_is_list = m_is_list_layout; - - return selection; -} - -game_info game_list_frame::GetGameInfoByMode(const QTableWidgetItem* item) const -{ - if (!item) - { - return nullptr; - } - - if (m_is_list_layout) - { - return GetGameInfoFromItem(m_game_list->item(item->row(), static_cast(gui::game_list_columns::icon))); - } - - return GetGameInfoFromItem(item); -} - -game_info game_list_frame::GetGameInfoFromItem(const QTableWidgetItem* item) -{ - if (!item) - { - return nullptr; - } - - const QVariant var = item->data(gui::game_role); - if (!var.canConvert()) - { - return nullptr; - } - - return var.value(); -} - -void game_list_frame::SetShowCompatibilityInGrid(bool show) -{ - m_draw_compat_status_to_grid = show; - RepaintIcons(); - m_gui_settings->SetValue(gui::gl_draw_compat, show); -} - -void game_list_frame::SetPreferGameDataIcons(bool enabled) -{ - if (m_prefer_game_data_icons != enabled) - { - m_prefer_game_data_icons = enabled; - m_gui_settings->SetValue(gui::gl_pref_gd_icon, enabled); - Refresh(true); - } -} - -void game_list_frame::SetShowCustomIcons(bool show) -{ - if (m_show_custom_icons != show) - { - m_show_custom_icons = show; - m_gui_settings->SetValue(gui::gl_custom_icon, show); - Refresh(true); - } -} - -void game_list_frame::SetPlayHoverGifs(bool play) -{ - if (m_play_hover_movies != play) - { - m_play_hover_movies = play; - m_gui_settings->SetValue(gui::gl_hover_gifs, play); - Refresh(true); - } -} - -const std::vector& game_list_frame::GetGameInfo() const -{ - return m_game_data; -} - -void game_list_frame::WaitAndAbortRepaintThreads() -{ - for (const game_info& game : m_game_data) - { - if (game && game->item) - { - game->item->wait_for_icon_loading(true); - } - } -} - -void game_list_frame::WaitAndAbortSizeCalcThreads() -{ - for (const game_info& game : m_game_data) - { - if (game && game->item) - { - game->item->wait_for_size_on_disk_loading(true); - } - } -} diff --git a/rpcs3qt-legacy/game_list_frame.h b/rpcs3qt-legacy/game_list_frame.h deleted file mode 100644 index 5326a0989..000000000 --- a/rpcs3qt-legacy/game_list_frame.h +++ /dev/null @@ -1,225 +0,0 @@ -#pragma once - -#include "game_list.h" -#include "custom_dock_widget.h" -#include "shortcut_utils.h" -#include "util/lockless.h" -#include "util/mutex.h" -#include "util/auto_typemap.hpp" -#include "Emu/config_mode.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -class game_list_table; -class game_list_grid; -class gui_settings; -class emu_settings; -class persistent_settings; -class progress_dialog; - -class game_list_frame : public custom_dock_widget -{ - Q_OBJECT - -public: - explicit game_list_frame(std::shared_ptr gui_settings, std::shared_ptr emu_settings, std::shared_ptr persistent_settings, QWidget* parent = nullptr); - ~game_list_frame(); - - /** Refresh the gamelist with/without loading game data from files. Public so that main frame can refresh after vfs or install */ - void Refresh(const bool from_drive = false, const std::vector& serials_to_remove_from_yml = {}, const bool scroll_after = true); - - /** Adds/removes categories that should be shown on gamelist. Public so that main frame menu actions can apply them */ - void ToggleCategoryFilter(const QStringList& categories, bool show); - - /** Loads from settings. Public so that main frame can easily reset these settings if needed. */ - void LoadSettings(); - - /** Saves settings. Public so that main frame can save this when a caching of column widths is needed for settings backup */ - void SaveSettings(); - - /** Resize Gamelist Icons to size given by slider position */ - void ResizeIcons(const int& slider_pos); - - /** Repaint Gamelist Icons with new background color */ - void RepaintIcons(const bool& from_settings = false); - - void SetShowHidden(bool show); - - game_compatibility* GetGameCompatibility() const - { - return m_game_compat; - } - - const std::vector& GetGameInfo() const; - - void CreateShortcuts(const std::vector& games, const std::set& locations); - - bool IsEntryVisible(const game_info& game, bool search_fallback = false) const; - -public Q_SLOTS: - void BatchCreateCPUCaches(const std::vector& game_data = {}); - void BatchRemovePPUCaches(); - void BatchRemoveSPUCaches(); - void BatchRemoveCustomConfigurations(); - void BatchRemoveCustomPadConfigurations(); - void BatchRemoveShaderCaches(); - void SetListMode(const bool& is_list); - void SetSearchText(const QString& text); - void SetShowCompatibilityInGrid(bool show); - void SetPreferGameDataIcons(bool enabled); - void SetShowCustomIcons(bool show); - void SetPlayHoverGifs(bool play); - void FocusAndSelectFirstEntryIfNoneIs(); - -private Q_SLOTS: - void OnParsingFinished(); - void OnRefreshFinished(); - void OnCompatFinished(); - void OnColClicked(int col); - void ShowContextMenu(const QPoint& pos); - void doubleClickedSlot(QTableWidgetItem* item); - void doubleClickedSlot(const game_info& game); - void ItemSelectionChangedSlot(); -Q_SIGNALS: - void GameListFrameClosed(); - void NotifyGameSelection(const game_info& game); - void RequestBoot(const game_info& game, cfg_mode config_mode = cfg_mode::custom, const std::string& config_path = "", const std::string& savestate = ""); - void RequestIconSizeChange(const int& val); - void NotifyEmuSettingsChange(); - void FocusToSearchBar(); - void Refreshed(); - -public: - template - struct GameIdsTable - { - // List of game paths an operation has been done on for the use of the slot function - std::set m_done_paths; - }; - - // Enqueue slot for refreshed signal - // Allowing for an individual container for each distinct use case (currently disabled and contains only one such entry) - template - void AddRefreshedSlot(Func&& func) - { - // NOTE: Remove assert when the need for individual containers arises - static_assert(std::is_void_v); - - connect(this, &game_list_frame::Refreshed, this, [this, func = std::move(func)]() mutable - { - func(m_refresh_funcs_manage_type->get>().m_done_paths); - }, - Qt::SingleShotConnection); - } - -protected: - /** Override inherited method from Qt to allow signalling when close happened.*/ - void closeEvent(QCloseEvent* event) override; - bool eventFilter(QObject* object, QEvent* event) override; - -private: - void push_path(const std::string& path, std::vector& legit_paths); - - void ShowCustomConfigIcon(const game_info& game); - bool SearchMatchesApp(const QString& name, const QString& serial, bool fallback = false) const; - - bool RemoveCustomConfiguration(const std::string& title_id, const game_info& game = nullptr, bool is_interactive = false); - bool RemoveCustomPadConfiguration(const std::string& title_id, const game_info& game = nullptr, bool is_interactive = false); - bool RemoveShadersCache(const std::string& base_dir, bool is_interactive = false); - bool RemovePPUCache(const std::string& base_dir, bool is_interactive = false); - bool RemoveSPUCache(const std::string& base_dir, bool is_interactive = false); - void RemoveHDD1Cache(const std::string& base_dir, const std::string& title_id, bool is_interactive = false); - static bool CreateCPUCaches(const std::string& path, const std::string& serial = {}); - static bool CreateCPUCaches(const game_info& game); - - static bool RemoveContentPath(const std::string& path, const std::string& desc); - static u32 RemoveContentPathList(const std::vector& path_list, const std::string& desc); - static bool RemoveContentBySerial(const std::string& base_dir, const std::string& serial, const std::string& desc); - static std::vector GetDirListBySerial(const std::string& base_dir, const std::string& serial); - void BatchActionBySerials(progress_dialog* pdlg, const std::set& serials, QString progressLabel, std::function action, std::function cancel_log, bool refresh_on_finish, bool can_be_concurrent = false, std::function should_wait_cb = {}); - static std::string GetCacheDirBySerial(const std::string& serial); - static std::string GetDataDirBySerial(const std::string& serial); - std::string CurrentSelectionPath(); - - game_info GetGameInfoByMode(const QTableWidgetItem* item) const; - static game_info GetGameInfoFromItem(const QTableWidgetItem* item); - - void WaitAndAbortRepaintThreads(); - void WaitAndAbortSizeCalcThreads(); - - // Which widget we are displaying depends on if we are in grid or list mode. - QMainWindow* m_game_dock = nullptr; - QStackedWidget* m_central_widget = nullptr; - - // Game Grid - game_list_grid* m_game_grid = nullptr; - - // Game List - game_list_table* m_game_list = nullptr; - game_compatibility* m_game_compat = nullptr; - progress_dialog* m_progress_dialog = nullptr; - QList m_columnActs; - Qt::SortOrder m_col_sort_order{}; - int m_sort_column{}; - bool m_initial_refresh_done = false; - std::map m_notes; - std::map m_titles; - - // Categories - QStringList m_category_filters; - QStringList m_grid_category_filters; - - // List Mode - bool m_is_list_layout = true; - bool m_old_layout_is_list = true; - - // Data - std::shared_ptr m_gui_settings; - std::shared_ptr m_emu_settings; - std::shared_ptr m_persistent_settings; - std::vector m_game_data; - struct path_entry - { - std::string path; - bool is_disc{}; - bool is_from_yml{}; - }; - std::vector m_path_entries; - shared_mutex m_path_mutex; - std::set m_path_list; - QSet m_serials; - QMutex m_games_mutex; - lf_queue m_games; - const std::array m_parsing_threads{0}; - QFutureWatcher m_parsing_watcher; - QFutureWatcher m_refresh_watcher; - QSet m_hidden_list; - bool m_show_hidden{false}; - - // Search - QString m_search_text; - - // Icon Size - int m_icon_size_index = 0; - - // Icons - QColor m_icon_color; - QSize m_icon_size; - qreal m_margin_factor; - qreal m_text_factor; - bool m_draw_compat_status_to_grid = false; - bool m_prefer_game_data_icons = false; - bool m_show_custom_icons = true; - bool m_play_hover_movies = true; - std::optional> m_refresh_funcs_manage_type{std::in_place}; -}; diff --git a/rpcs3qt-legacy/game_list_grid.cpp b/rpcs3qt-legacy/game_list_grid.cpp deleted file mode 100644 index c43855989..000000000 --- a/rpcs3qt-legacy/game_list_grid.cpp +++ /dev/null @@ -1,217 +0,0 @@ -#include "stdafx.h" -#include "game_list_grid.h" -#include "game_list_grid_item.h" -#include "gui_settings.h" -#include "qt_utils.h" -#include "util/File.h" - -#include -#include - -game_list_grid::game_list_grid() - : flow_widget(nullptr), game_list_base() -{ - setObjectName("game_list_grid"); - setContextMenuPolicy(Qt::CustomContextMenu); - - m_icon_ready_callback = [this](const movie_item_base* item) - { - Q_EMIT IconReady(item); - }; - - connect(this, &game_list_grid::IconReady, this, [this](const movie_item_base* item) - { - if (item) - item->image_change_callback(); - }, - Qt::QueuedConnection); // The default 'AutoConnection' doesn't seem to work in this specific case... - - connect(this, &flow_widget::ItemSelectionChanged, this, [this](int index) - { - if (game_list_grid_item* item = static_cast(::at32(items(), index))) - { - Q_EMIT ItemSelectionChanged(item->game()); - } - }); -} - -void game_list_grid::clear_list() -{ - clear(); -} - -void game_list_grid::populate( - const std::vector& game_data, - const std::map& notes_map, - const std::map& title_map, - const std::string& selected_item_id, - bool play_hover_movies) -{ - clear_list(); - - game_list_grid_item* selected_item = nullptr; - - blockSignals(true); - - const auto get_title = [&title_map](const QString& serial, const std::string& name) -> QString - { - if (const auto it = title_map.find(serial); it != title_map.cend()) - { - return it->second.simplified(); - } - - return QString::fromStdString(name).simplified(); - }; - - for (const auto& game : game_data) - { - const QString serial = QString::fromStdString(game->info.serial); - const QString title = get_title(serial, game->info.name); - - game_list_grid_item* item = new game_list_grid_item(this, game, title); - item->installEventFilter(this); - item->setFocusPolicy(Qt::StrongFocus); - - game->item = item; - - if (const auto it = notes_map.find(serial); it != notes_map.cend() && !it->second.isEmpty()) - { - item->setToolTip(tr("%0 [%1]\n\nNotes:\n%2").arg(title).arg(serial).arg(it->second)); - } - else - { - item->setToolTip(tr("%0 [%1]").arg(title).arg(serial)); - } - - item->set_image_change_callback([this, item, game](const QVideoFrame& frame) - { - if (!item || !game) - { - return; - } - - if (const QPixmap pixmap = item->get_movie_image(frame); item->get_active() && !pixmap.isNull()) - { - item->set_icon(gui::utils::get_centered_pixmap(pixmap, m_icon_size, 0, 0, 1.0, Qt::FastTransformation)); - } - else - { - std::lock_guard lock(item->pixmap_mutex); - - item->set_icon(game->pxmap); - - if (!game->has_hover_gif && !game->has_hover_pam) - { - game->pxmap = {}; - } - - item->stop_movie(); - } - }); - - if (play_hover_movies && (game->has_hover_gif || game->has_hover_pam)) - { - item->set_video_path(game->info.movie_path); - } - - if (selected_item_id == game->info.path + game->info.icon_path) - { - selected_item = item; - } - - add_widget(item); - } - - blockSignals(false); - - // Update layout before setting focus on the selected item - show(); - - QApplication::processEvents(); - - select_item(selected_item); -} - -void game_list_grid::repaint_icons(std::vector& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio) -{ - m_icon_size = icon_size; - m_icon_color = icon_color; - - QPixmap placeholder(icon_size * device_pixel_ratio); - placeholder.setDevicePixelRatio(device_pixel_ratio); - placeholder.fill(Qt::transparent); - - const bool show_title = m_icon_size.width() > (gui::gl_icon_size_medium.width() + gui::gl_icon_size_small.width()) / 2; - - for (game_info& game : game_data) - { - if (game_list_grid_item* item = static_cast(game->item)) - { - if (item->icon_loading()) - { - // We already have an icon. Simply set the icon size to let the label scale itself in a quick and dirty fashion. - item->set_icon_size(m_icon_size); - } - else - { - // We don't have an icon. Set a placeholder to initialize the layout. - game->pxmap = placeholder; - item->image_change_callback(); - } - - item->set_icon_load_func([this, game, device_pixel_ratio, cancel = item->icon_loading_aborted()](int) - { - IconLoadFunction(game, device_pixel_ratio, cancel); - }); - - item->adjust_size(); - item->show_title(show_title); - item->got_visible = false; - } - } -} - -void game_list_grid::FocusAndSelectFirstEntryIfNoneIs() -{ - if (!items().empty()) - { - items().front()->setFocus(); - } -} - -bool game_list_grid::eventFilter(QObject* watched, QEvent* event) -{ - if (!event) - { - return false; - } - - if (event->type() == QEvent::MouseButtonDblClick && static_cast(event)->button() == Qt::LeftButton) - { - if (game_list_grid_item* item = static_cast(watched)) - { - Q_EMIT ItemDoubleClicked(item->game()); - return true; - } - } - - return false; -} - -void game_list_grid::keyPressEvent(QKeyEvent* event) -{ - if (!event) - { - return; - } - - const auto modifiers = event->modifiers(); - - if (modifiers == Qt::ControlModifier && event->key() == Qt::Key_F && !event->isAutoRepeat()) - { - Q_EMIT FocusToSearchBar(); - return; - } - - flow_widget::keyPressEvent(event); -} diff --git a/rpcs3qt-legacy/game_list_grid.h b/rpcs3qt-legacy/game_list_grid.h deleted file mode 100644 index aaa1e267d..000000000 --- a/rpcs3qt-legacy/game_list_grid.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "game_list_base.h" -#include "flow_widget.h" - -#include - -class game_list_grid : public flow_widget, public game_list_base -{ - Q_OBJECT - -public: - explicit game_list_grid(); - - void clear_list() override; - - void populate( - const std::vector& game_data, - const std::map& notes_map, - const std::map& title_map, - const std::string& selected_item_id, - bool play_hover_movies) override; - - void repaint_icons(std::vector& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio) override; - - bool eventFilter(QObject* watched, QEvent* event) override; - void keyPressEvent(QKeyEvent* event) override; - -public Q_SLOTS: - void FocusAndSelectFirstEntryIfNoneIs(); - -Q_SIGNALS: - void FocusToSearchBar(); - void ItemDoubleClicked(const game_info& game); - void ItemSelectionChanged(const game_info& game); - void IconReady(const movie_item_base* item); -}; diff --git a/rpcs3qt-legacy/game_list_grid_item.cpp b/rpcs3qt-legacy/game_list_grid_item.cpp deleted file mode 100644 index 928a5fa26..000000000 --- a/rpcs3qt-legacy/game_list_grid_item.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "game_list_grid_item.h" - -#include -#include - -game_list_grid_item::game_list_grid_item(QWidget* parent, game_info game, const QString& title) - : flow_widget_item(parent), movie_item_base(), m_game(std::move(game)) -{ - setObjectName("game_list_grid_item"); - setAttribute(Qt::WA_Hover); // We need to enable the hover attribute to ensure that hover events are handled. - - cb_on_first_visibility = [this]() - { - if (!icon_loading()) - { - call_icon_load_func(0); - } - }; - - m_icon_label = new QLabel(this); - m_icon_label->setObjectName("game_list_grid_item_icon_label"); - m_icon_label->setAttribute(Qt::WA_TranslucentBackground); - m_icon_label->setScaledContents(true); - - m_title_label = new QLabel(title, this); - m_title_label->setObjectName("game_list_grid_item_title_label"); - m_title_label->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); - m_title_label->setWordWrap(true); - m_title_label->setVisible(false); - - QVBoxLayout* layout = new QVBoxLayout(this); - layout->addWidget(m_icon_label, 1); - layout->addWidget(m_title_label, 0); - - setLayout(layout); -} - -void game_list_grid_item::set_icon_size(const QSize& size) -{ - m_icon_size = size; -} - -void game_list_grid_item::set_icon(const QPixmap& pixmap) -{ - m_icon_size = pixmap.size() / devicePixelRatioF(); - m_icon_label->setPixmap(pixmap); -} - -void game_list_grid_item::adjust_size() -{ - m_icon_label->setMinimumSize(m_icon_size); - m_icon_label->setMaximumSize(m_icon_size); - m_title_label->setMaximumWidth(m_icon_size.width()); -} - -void game_list_grid_item::show_title(bool visible) -{ - if (m_title_label) - { - m_title_label->setVisible(visible); - } -} - -void game_list_grid_item::polish_style() -{ - flow_widget_item::polish_style(); - - m_title_label->style()->unpolish(m_title_label); - m_title_label->style()->polish(m_title_label); -} - -bool game_list_grid_item::event(QEvent* event) -{ - switch (event->type()) - { - case QEvent::HoverEnter: - set_active(true); - break; - case QEvent::HoverLeave: - set_active(false); - break; - default: - break; - } - - return flow_widget_item::event(event); -} diff --git a/rpcs3qt-legacy/game_list_grid_item.h b/rpcs3qt-legacy/game_list_grid_item.h deleted file mode 100644 index 773854823..000000000 --- a/rpcs3qt-legacy/game_list_grid_item.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include "flow_widget_item.h" -#include "movie_item_base.h" -#include "game_list_base.h" - -#include - -class game_list_grid_item : public flow_widget_item, public movie_item_base -{ - Q_OBJECT - -public: - game_list_grid_item(QWidget* parent, game_info game, const QString& title); - - void set_icon_size(const QSize& size); - void set_icon(const QPixmap& pixmap); - void adjust_size(); - - const game_info& game() const - { - return m_game; - } - - void show_title(bool visible); - - void polish_style() override; - - bool event(QEvent* event) override; - -private: - QSize m_icon_size{}; - QLabel* m_icon_label{}; - QLabel* m_title_label{}; - game_info m_game{}; -}; diff --git a/rpcs3qt-legacy/game_list_table.cpp b/rpcs3qt-legacy/game_list_table.cpp deleted file mode 100644 index 3e639537a..000000000 --- a/rpcs3qt-legacy/game_list_table.cpp +++ /dev/null @@ -1,398 +0,0 @@ -#include "stdafx.h" -#include "game_list_table.h" -#include "game_list_delegate.h" -#include "game_list_frame.h" -#include "gui_settings.h" -#include "localized.h" -#include "custom_table_widget_item.h" -#include "persistent_settings.h" -#include "qt_utils.h" - -#include "Emu/vfs_config.h" -#include "util/StrUtil.h" - -#include -#include -#include - -game_list_table::game_list_table(game_list_frame* frame, std::shared_ptr persistent_settings) - : game_list(), m_game_list_frame(frame), m_persistent_settings(std::move(persistent_settings)) -{ - m_is_list_layout = true; - - setShowGrid(false); - setItemDelegate(new game_list_delegate(this)); - setEditTriggers(QAbstractItemView::NoEditTriggers); - setSelectionBehavior(QAbstractItemView::SelectRows); - setSelectionMode(QAbstractItemView::SingleSelection); - setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); - setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); - verticalScrollBar()->setSingleStep(20); - horizontalScrollBar()->setSingleStep(20); - verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); - verticalHeader()->setVisible(false); - horizontalHeader()->setHighlightSections(false); - horizontalHeader()->setSortIndicatorShown(true); - horizontalHeader()->setStretchLastSection(true); - horizontalHeader()->setDefaultSectionSize(150); - horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); - setContextMenuPolicy(Qt::CustomContextMenu); - setAlternatingRowColors(true); - setColumnCount(static_cast(gui::game_list_columns::count)); - setMouseTracking(true); - - connect(this, &game_list_table::size_on_disk_ready, this, [this](const game_info& game) - { - if (!game || !game->item) - return; - if (QTableWidgetItem* size_item = item(static_cast(game->item)->row(), static_cast(gui::game_list_columns::dir_size))) - { - const u64& game_size = game->info.size_on_disk; - size_item->setText(game_size != umax ? gui::utils::format_byte_size(game_size) : tr("Unknown")); - size_item->setData(Qt::UserRole, QVariant::fromValue(game_size)); - } - }); - - connect(this, &game_list::IconReady, this, [this](const movie_item_base* item) - { - if (item) - item->image_change_callback(); - }); -} - -void game_list_table::restore_layout(const QByteArray& state) -{ - // Resize to fit and get the ideal icon column width - resize_columns_to_contents(); - const int icon_column_width = columnWidth(static_cast(gui::game_list_columns::icon)); - - // Restore header layout from last session - if (!horizontalHeader()->restoreState(state) && rowCount()) - { - // Nothing to do - } - - // Make sure no columns are squished - fix_narrow_columns(); - - // Make sure that the icon column is large enough for the actual items. - // This is important if the list appeared as empty when closing the software before. - horizontalHeader()->resizeSection(static_cast(gui::game_list_columns::icon), icon_column_width); - - // Save new header state - horizontalHeader()->restoreState(horizontalHeader()->saveState()); -} - -void game_list_table::resize_columns_to_contents(int spacing) -{ - verticalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents); - horizontalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents); - - // Make non-icon columns slighty bigger for better visuals - for (int i = 1; i < columnCount(); i++) - { - if (isColumnHidden(i)) - { - continue; - } - - const int size = horizontalHeader()->sectionSize(i) + spacing; - horizontalHeader()->resizeSection(i, size); - } -} - -void game_list_table::adjust_icon_column() -{ - // Fixate vertical header and row height - verticalHeader()->setMinimumSectionSize(m_icon_size.height()); - verticalHeader()->setMaximumSectionSize(m_icon_size.height()); - - // Resize the icon column - resizeColumnToContents(static_cast(gui::game_list_columns::icon)); - - // Shorten the last section to remove horizontal scrollbar if possible - resizeColumnToContents(static_cast(gui::game_list_columns::count) - 1); -} - -void game_list_table::sort(usz game_count, int sort_column, Qt::SortOrder col_sort_order) -{ - // Back-up old header sizes to handle unwanted column resize in case of zero search results - const int old_row_count = rowCount(); - const usz old_game_count = game_count; - - std::vector column_widths(columnCount()); - for (int i = 0; i < columnCount(); i++) - { - column_widths[i] = columnWidth(i); - } - - // Sorting resizes hidden columns, so unhide them as a workaround - std::vector columns_to_hide; - - for (int i = 0; i < columnCount(); i++) - { - if (isColumnHidden(i)) - { - setColumnHidden(i, false); - columns_to_hide.push_back(i); - } - } - - // Sort the list by column and sort order - sortByColumn(sort_column, col_sort_order); - - // Hide columns again - for (int col : columns_to_hide) - { - setColumnHidden(col, true); - } - - // Don't resize the columns if no game is shown to preserve the header settings - if (!rowCount()) - { - for (int i = 0; i < columnCount(); i++) - { - setColumnWidth(i, column_widths[i]); - } - - horizontalHeader()->setSectionResizeMode(static_cast(gui::game_list_columns::icon), QHeaderView::Fixed); - return; - } - - // Fixate vertical header and row height - verticalHeader()->setMinimumSectionSize(m_icon_size.height()); - verticalHeader()->setMaximumSectionSize(m_icon_size.height()); - resizeRowsToContents(); - - // Resize columns if the game list was empty before - if (!old_row_count && !old_game_count) - { - resize_columns_to_contents(); - } - else - { - resizeColumnToContents(static_cast(gui::game_list_columns::icon)); - } - - // Fixate icon column - horizontalHeader()->setSectionResizeMode(static_cast(gui::game_list_columns::icon), QHeaderView::Fixed); - - // Shorten the last section to remove horizontal scrollbar if possible - resizeColumnToContents(static_cast(gui::game_list_columns::count) - 1); -} - -void game_list_table::set_custom_config_icon(const game_info& game) -{ - if (!game) - { - return; - } - - const QString serial = QString::fromStdString(game->info.serial); - - for (int row = 0; row < rowCount(); ++row) - { - if (QTableWidgetItem* title_item = item(row, static_cast(gui::game_list_columns::name))) - { - if (const QTableWidgetItem* serial_item = item(row, static_cast(gui::game_list_columns::serial)); serial_item && serial_item->text() == serial) - { - title_item->setIcon(game_list_base::GetCustomConfigIcon(game)); - } - } - } -} - -void game_list_table::populate( - const std::vector& game_data, - const std::map& notes_map, - const std::map& title_map, - const std::string& selected_item_id, - bool play_hover_movies) -{ - clear_list(); - - setRowCount(::narrow(game_data.size())); - - // Default locale. Uses current Qt application language. - const QLocale locale{}; - const Localized localized; - - const std::string dev_flash = g_cfg_vfs.get_dev_flash(); - - int row = 0; - int index = -1; - int selected_row = -1; - - const auto get_title = [&title_map](const QString& serial, const std::string& name) -> QString - { - if (const auto it = title_map.find(serial); it != title_map.cend()) - { - return it->second; - } - - return QString::fromStdString(name); - }; - - for (const auto& game : game_data) - { - index++; - - const QString serial = QString::fromStdString(game->info.serial); - const QString title = get_title(serial, game->info.name); - - // Icon - custom_table_widget_item* icon_item = new custom_table_widget_item; - game->item = icon_item; - - icon_item->set_image_change_callback([this, icon_item, game](const QVideoFrame& frame) - { - if (!icon_item || !game) - { - return; - } - - if (const QPixmap pixmap = icon_item->get_movie_image(frame); icon_item->get_active() && !pixmap.isNull()) - { - icon_item->setData(Qt::DecorationRole, pixmap.scaled(m_icon_size, Qt::KeepAspectRatio)); - } - else - { - std::lock_guard lock(icon_item->pixmap_mutex); - - icon_item->setData(Qt::DecorationRole, game->pxmap); - - if (!game->has_hover_gif && !game->has_hover_pam) - { - game->pxmap = {}; - } - - icon_item->stop_movie(); - } - }); - - icon_item->set_size_calc_func([this, game, cancel = icon_item->size_on_disk_loading_aborted(), dev_flash]() - { - if (game && game->info.size_on_disk == umax && (!cancel || !cancel->load())) - { - if (game->info.path.starts_with(dev_flash)) - { - // Do not report size of apps inside /dev_flash (it does not make sense to do so) - game->info.size_on_disk = 0; - } - else - { - game->info.size_on_disk = fs::get_dir_size(game->info.path, 1, cancel.get()); - } - - if (!cancel || !cancel->load()) - { - Q_EMIT size_on_disk_ready(game); - return; - } - } - }); - - if (play_hover_movies && (game->has_hover_gif || game->has_hover_pam)) - { - icon_item->set_video_path(game->info.movie_path); - } - - icon_item->setData(Qt::UserRole, index, true); - icon_item->setData(gui::custom_roles::game_role, QVariant::fromValue(game)); - - // Title - custom_table_widget_item* title_item = new custom_table_widget_item(title); - title_item->setIcon(game_list_base::GetCustomConfigIcon(game)); - - // Serial - custom_table_widget_item* serial_item = new custom_table_widget_item(game->info.serial); - - if (const auto it = notes_map.find(serial); it != notes_map.cend() && !it->second.isEmpty()) - { - const QString tool_tip = tr("%0 [%1]\n\nNotes:\n%2").arg(title).arg(serial).arg(it->second); - title_item->setToolTip(tool_tip); - serial_item->setToolTip(tool_tip); - } - - // Move Support (http://www.psdevwiki.com/ps3/PARAM.SFO#ATTRIBUTE) - const bool supports_move = game->info.attr & 0x800000; - - // Compatibility - custom_table_widget_item* compat_item = new custom_table_widget_item; - compat_item->setText(game->compat.text % (game->compat.date.isEmpty() ? QStringLiteral("") : " (" % game->compat.date % ")")); - compat_item->setData(Qt::UserRole, game->compat.index, true); - compat_item->setToolTip(game->compat.tooltip); - if (!game->compat.color.isEmpty()) - { - compat_item->setData(Qt::DecorationRole, gui::utils::circle_pixmap(game->compat.color, devicePixelRatioF() * 2)); - } - - // Version - QString app_version = QString::fromStdString(game->GetGameVersion()); - - if (game->info.bootable && !game->compat.latest_version.isEmpty()) - { - f64 top_ver = 0.0, app_ver = 0.0; - const bool unknown = app_version == localized.category.unknown; - const bool ok_app = !unknown && try_to_float(&app_ver, app_version.toStdString(), ::std::numeric_limits::min(), ::std::numeric_limits::max()); - const bool ok_top = !unknown && try_to_float(&top_ver, game->compat.latest_version.toStdString(), ::std::numeric_limits::min(), ::std::numeric_limits::max()); - - // If the app is bootable and the compat database contains info about the latest patch version: - // add a hint for available software updates if the app version is unknown or lower than the latest version. - if (unknown || (ok_top && ok_app && top_ver > app_ver)) - { - app_version = tr("%0 (Update available: %1)").arg(app_version, game->compat.latest_version); - } - } - - // Playtimes - const quint64 elapsed_ms = m_persistent_settings->GetPlaytime(serial); - - // Last played (support outdated values) - QDateTime last_played; - const QString last_played_str = m_persistent_settings->GetLastPlayed(serial); - - if (!last_played_str.isEmpty()) - { - last_played = QDateTime::fromString(last_played_str, gui::persistent::last_played_date_format); - - if (!last_played.isValid()) - { - last_played = QDateTime::fromString(last_played_str, gui::persistent::last_played_date_format_old); - } - } - - const u64 game_size = game->info.size_on_disk; - - setItem(row, static_cast(gui::game_list_columns::icon), icon_item); - setItem(row, static_cast(gui::game_list_columns::name), title_item); - setItem(row, static_cast(gui::game_list_columns::serial), serial_item); - setItem(row, static_cast(gui::game_list_columns::firmware), new custom_table_widget_item(game->info.fw)); - setItem(row, static_cast(gui::game_list_columns::version), new custom_table_widget_item(app_version)); - setItem(row, static_cast(gui::game_list_columns::category), new custom_table_widget_item(game->localized_category)); - setItem(row, static_cast(gui::game_list_columns::path), new custom_table_widget_item(game->info.path)); - setItem(row, static_cast(gui::game_list_columns::move), new custom_table_widget_item((supports_move ? tr("Supported") : tr("Not Supported")).toStdString(), Qt::UserRole, !supports_move)); - setItem(row, static_cast(gui::game_list_columns::resolution), new custom_table_widget_item(Localized::GetStringFromU32(game->info.resolution, localized.resolution.mode, true))); - setItem(row, static_cast(gui::game_list_columns::sound), new custom_table_widget_item(Localized::GetStringFromU32(game->info.sound_format, localized.sound.format, true))); - setItem(row, static_cast(gui::game_list_columns::parental), new custom_table_widget_item(Localized::GetStringFromU32(game->info.parental_lvl, localized.parental.level), Qt::UserRole, game->info.parental_lvl)); - setItem(row, static_cast(gui::game_list_columns::last_play), new custom_table_widget_item(locale.toString(last_played, last_played >= QDateTime::currentDateTime().addDays(-7) ? gui::persistent::last_played_date_with_time_of_day_format : gui::persistent::last_played_date_format_new), Qt::UserRole, last_played)); - setItem(row, static_cast(gui::game_list_columns::playtime), new custom_table_widget_item(elapsed_ms == 0 ? tr("Never played") : localized.GetVerboseTimeByMs(elapsed_ms), Qt::UserRole, elapsed_ms)); - setItem(row, static_cast(gui::game_list_columns::compat), compat_item); - setItem(row, static_cast(gui::game_list_columns::dir_size), new custom_table_widget_item(game_size != umax ? gui::utils::format_byte_size(game_size) : tr("Unknown"), Qt::UserRole, QVariant::fromValue(game_size))); - - if (selected_item_id == game->info.path + game->info.icon_path) - { - selected_row = row; - } - - row++; - } - - selectRow(selected_row); -} - -void game_list_table::repaint_icons(std::vector& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio) -{ - game_list_base::repaint_icons(game_data, icon_color, icon_size, device_pixel_ratio); - adjust_icon_column(); -} diff --git a/rpcs3qt-legacy/game_list_table.h b/rpcs3qt-legacy/game_list_table.h deleted file mode 100644 index e3d693034..000000000 --- a/rpcs3qt-legacy/game_list_table.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "game_list.h" - -class persistent_settings; -class game_list_frame; - -class game_list_table : public game_list -{ - Q_OBJECT - -public: - game_list_table(game_list_frame* frame, std::shared_ptr persistent_settings); - - /** Restores the initial layout of the table */ - void restore_layout(const QByteArray& state); - - /** Resizes the columns to their contents and adds a small spacing */ - void resize_columns_to_contents(int spacing = 20); - - void adjust_icon_column(); - - void sort(usz game_count, int sort_column, Qt::SortOrder col_sort_order); - - void set_custom_config_icon(const game_info& game); - - void populate( - const std::vector& game_data, - const std::map& notes_map, - const std::map& title_map, - const std::string& selected_item_id, - bool play_hover_movies) override; - - void repaint_icons(std::vector& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio) override; - -Q_SIGNALS: - void size_on_disk_ready(const game_info& game); - -private: - game_list_frame* m_game_list_frame{}; - std::shared_ptr m_persistent_settings; -}; diff --git a/rpcs3qt-legacy/gl_gs_frame.cpp b/rpcs3qt-legacy/gl_gs_frame.cpp deleted file mode 100644 index ee6d901e4..000000000 --- a/rpcs3qt-legacy/gl_gs_frame.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include "gl_gs_frame.h" - -#include "Emu/System.h" -#include "Emu/system_config.h" - -#include -#include - -gl_gs_frame::gl_gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, std::shared_ptr gui_settings, bool force_fullscreen) - : gs_frame(screen, geometry, appIcon, std::move(gui_settings), force_fullscreen) -{ - setSurfaceType(QSurface::OpenGLSurface); - - m_format.setRenderableType(QSurfaceFormat::OpenGL); - m_format.setMajorVersion(4); - m_format.setMinorVersion(3); - m_format.setProfile(QSurfaceFormat::CoreProfile); - m_format.setAlphaBufferSize(0); - m_format.setDepthBufferSize(0); - m_format.setSwapBehavior(QSurfaceFormat::SwapBehavior::DoubleBuffer); - m_format.setSwapInterval(0); - if (g_cfg.video.debug_output) - { - m_format.setOption(QSurfaceFormat::FormatOption::DebugContext); - } - setFormat(m_format); - create(); - show(); -} - -void gl_gs_frame::reset() -{ - m_primary_context = nullptr; -} - -draw_context_t gl_gs_frame::make_context() -{ - auto context = new GLContext(); - context->handle = new QOpenGLContext(); - - if (m_primary_context) - { - QOffscreenSurface* surface = nullptr; - - // Workaround for the Qt warning: "Attempting to create QWindow-based QOffscreenSurface outside the gui thread. Expect failures." - Emu.BlockingCallFromMainThread([&]() - { - surface = new QOffscreenSurface(); - surface->setFormat(m_format); - surface->create(); - }); - - // Share resources with the first created context - context->handle->setShareContext(m_primary_context->handle); - context->surface = surface; - context->owner = true; - } - else - { - // This is the first created context, all others will share resources with this one - m_primary_context = context; - context->surface = this; - context->owner = false; - } - - context->handle->setFormat(m_format); - - if (!context->handle->create()) - { - fmt::throw_exception("Failed to create OpenGL context"); - } - - return context; -} - -void gl_gs_frame::set_current(draw_context_t ctx) -{ - if (!ctx) - { - fmt::throw_exception("Null context handle passed to set_current"); - } - - const auto context = static_cast(ctx); - - if (!context->handle->makeCurrent(context->surface)) - { - if (!context->owner) - { - create(); - } - else if (!context->handle->isValid()) - { - if (!context->handle->create()) - { - fmt::throw_exception("Failed to create OpenGL context"); - } - } - - if (!context->handle->makeCurrent(context->surface)) - { - fmt::throw_exception("Could not bind OpenGL context"); - } - } -} - -void gl_gs_frame::delete_context(draw_context_t ctx) -{ - const auto gl_ctx = static_cast(ctx); - - gl_ctx->handle->doneCurrent(); - -#ifdef _MSC_VER - // AMD driver crashes when executing wglDeleteContext - // Catch with SEH - __try - { - delete gl_ctx->handle; - } - __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) - { - rsx_log.fatal("Your graphics driver just crashed whilst cleaning up. All consumed VRAM should have been released, but you may want to restart the emulator just in case"); - } -#else - delete gl_ctx->handle; -#endif - - if (gl_ctx->owner) - { - delete gl_ctx->surface; - } - - delete gl_ctx; -} - -void gl_gs_frame::flip(draw_context_t context, bool skip_frame) -{ - gs_frame::flip(context); - - // Do not swap buffers if frame skip is active - if (skip_frame) - return; - - const auto gl_ctx = static_cast(context); - - if (auto window = dynamic_cast(gl_ctx->surface); window && window->isExposed()) - { - gl_ctx->handle->swapBuffers(gl_ctx->surface); - } -} diff --git a/rpcs3qt-legacy/gl_gs_frame.h b/rpcs3qt-legacy/gl_gs_frame.h deleted file mode 100644 index 223ac3cfd..000000000 --- a/rpcs3qt-legacy/gl_gs_frame.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "gs_frame.h" - -#include - -struct GLContext -{ - QSurface* surface = nullptr; - QOpenGLContext* handle = nullptr; - bool owner = false; -}; - -class gl_gs_frame : public gs_frame -{ -private: - QSurfaceFormat m_format; - GLContext* m_primary_context = nullptr; - -public: - explicit gl_gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, std::shared_ptr gui_settings, bool force_fullscreen); - - void reset() override; - draw_context_t make_context() override; - void set_current(draw_context_t ctx) override; - void delete_context(draw_context_t ctx) override; - void flip(draw_context_t context, bool skip_frame = false) override; -}; diff --git a/rpcs3qt-legacy/gs_frame.cpp b/rpcs3qt-legacy/gs_frame.cpp deleted file mode 100644 index 79f958ccf..000000000 --- a/rpcs3qt-legacy/gs_frame.cpp +++ /dev/null @@ -1,1210 +0,0 @@ -#include "gs_frame.h" -#include "gui_settings.h" - -#include "util/Config.h" -#include "util/Timer.h" -#include "util/date_time.h" -#include "util/File.h" -#include "util/video_provider.h" -#include "Emu/System.h" -#include "Emu/system_config.h" -#include "Emu/system_progress.hpp" -#include "Emu/IdManager.h" -#include "Emu/Audio/audio_utils.h" -#include "rpcsx/fw/ps3/cellScreenshot.h" -#include "rpcsx/fw/ps3/cellAudio.h" -#include "Emu/Cell/lv2/sys_rsxaudio.h" -#include "Emu/RSX/rsx_utils.h" -#include "Emu/RSX/Overlays/overlay_message.h" -#include "Emu/Io/interception.h" -#include "Emu/Io/recording_config.h" - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "png.h" - -#ifdef _WIN32 -#include -#elif defined(__APPLE__) -// nothing -#else -#ifdef HAVE_WAYLAND -#include -#include -#endif -#ifdef HAVE_X11 -#include -#endif -#endif - -LOG_CHANNEL(screenshot_log, "SCREENSHOT"); -LOG_CHANNEL(mark_log, "MARK"); -LOG_CHANNEL(gui_log, "GUI"); - -extern atomic_t g_user_asked_for_recording; -extern atomic_t g_user_asked_for_screenshot; -extern atomic_t g_user_asked_for_frame_capture; -extern atomic_t g_disable_frame_limit; -extern atomic_t g_game_window_focused; -extern atomic_t g_recording_mode; - -namespace pad -{ - extern atomic_t g_home_menu_requested; -} - -gs_frame::gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, std::shared_ptr gui_settings, bool force_fullscreen) - : QWindow(), m_initial_geometry(geometry), m_gui_settings(std::move(gui_settings)), m_start_games_fullscreen(force_fullscreen), m_renderer(g_cfg.video.renderer) -{ - m_window_title = Emu.GetFormattedTitle(0); - - if (!g_cfg_recording.load()) - { - gui_log.notice("Could not load recording config. Using defaults."); - } - - g_fxo->need(); - m_video_encoder = std::make_shared(); - - if (!appIcon.isNull()) - { - setIcon(appIcon); - } - -#ifdef __APPLE__ - // Needed for MoltenVK to work properly on MacOS - if (g_cfg.video.renderer == video_renderer::vulkan) - setSurfaceType(QSurface::VulkanSurface); -#endif - - // NOTE: You cannot safely create a wayland window that has hidden initial status and perform any changes on the window while it is still hidden. - // Doing this will create a surface with deferred commands that require a buffer. When binding to your session, this may assert in your compositor due to protocol restrictions. - Visibility startup_visibility = Hidden; -#ifndef _WIN32 - if (const char* session_type = ::getenv("XDG_SESSION_TYPE")) - { - if (!strcasecmp(session_type, "wayland")) - { - // Start windowed. This is a featureless rectangle on-screen with no window decorations. - // It does not even resemble a window until the WM attaches later on. - // Fullscreen could technically work with some fiddling, but easily breaks depending on geometry input. - startup_visibility = Windowed; - } - } -#endif - - setMinimumWidth(160); - setMinimumHeight(90); - setScreen(screen); - setGeometry(geometry); - setTitle(QString::fromStdString(m_window_title)); - - if (g_cfg.video.renderer != video_renderer::opengl) - { - // Do not display the window before OpenGL is configured! - // This works fine in windows and X11 but wayland-egl will crash later. - setVisibility(startup_visibility); - create(); - } - - load_gui_settings(); - - // Change cursor when in fullscreen. - connect(this, &QWindow::visibilityChanged, this, [this](QWindow::Visibility visibility) - { - handle_cursor(visibility, true, false, true); - }); - - // Change cursor when this window gets or loses focus. - connect(this, &QWindow::activeChanged, this, [this]() - { - g_game_window_focused = isActive(); - handle_cursor(visibility(), false, true, true); - }); - - // Configure the mouse hide on idle timer - connect(&m_mousehide_timer, &QTimer::timeout, this, &gs_frame::mouse_hide_timeout); - m_mousehide_timer.setSingleShot(true); - - m_progress_indicator = std::make_unique(0, 100); -} - -gs_frame::~gs_frame() -{ - g_user_asked_for_screenshot = false; - pad::g_home_menu_requested = false; - - // Save active screen to gui settings - const QScreen* current_screen = screen(); - const QList screens = QGuiApplication::screens(); - int screen_index = 0; - - for (int i = 0; i < screens.count(); i++) - { - if (current_screen == ::at32(screens, i)) - { - screen_index = i; - break; - } - } - - m_gui_settings->SetValue(gui::gs_screen, screen_index); -} - -void gs_frame::load_gui_settings() -{ - m_disable_mouse = m_gui_settings->GetValue(gui::gs_disableMouse).toBool(); - m_disable_kb_hotkeys = m_gui_settings->GetValue(gui::gs_disableKbHotkeys).toBool(); - m_show_mouse_in_fullscreen = m_gui_settings->GetValue(gui::gs_showMouseFs).toBool(); - m_lock_mouse_in_fullscreen = m_gui_settings->GetValue(gui::gs_lockMouseFs).toBool(); - m_hide_mouse_after_idletime = m_gui_settings->GetValue(gui::gs_hideMouseIdle).toBool(); - m_hide_mouse_idletime = m_gui_settings->GetValue(gui::gs_hideMouseIdleTime).toUInt(); - - if (m_disable_kb_hotkeys) - { - if (m_shortcut_handler) - { - m_shortcut_handler->deleteLater(); - m_shortcut_handler = nullptr; - } - } - else if (!m_shortcut_handler) - { - m_shortcut_handler = new shortcut_handler(gui::shortcuts::shortcut_handler_id::game_window, this, m_gui_settings); - connect(m_shortcut_handler, &shortcut_handler::shortcut_activated, this, &gs_frame::handle_shortcut); - } -} - -void gs_frame::update_shortcuts() -{ - if (m_shortcut_handler) - { - m_shortcut_handler->update(); - } -} - -void gs_frame::paintEvent(QPaintEvent* event) -{ - Q_UNUSED(event) -} - -void gs_frame::showEvent(QShowEvent* event) -{ - // We have to calculate new window positions, since the frame is only known once the window was created. - // We will try to find the originally requested dimensions if possible by moving the frame. - - // NOTES: The parameter m_initial_geometry is not necessarily equal to our actual geometry() at this point. - // That's why we use m_initial_geometry instead of the frameGeometry() in some places. - // All of these values, including the screen geometry, can also be negative numbers. - - const QRect available_geometry = screen()->availableGeometry(); // The available screen geometry - const QRect inner_geometry = geometry(); // The current inner geometry - const QRect outer_geometry = frameGeometry(); // The current outer geometry - - // Calculate the left and top frame borders (this will include window handles) - const int left_border = inner_geometry.left() - outer_geometry.left(); - const int top_border = inner_geometry.top() - outer_geometry.top(); - - // Calculate the initially expected frame origin - const QPoint expected_pos(m_initial_geometry.left() - left_border, - m_initial_geometry.top() - top_border); - - // Make sure that the expected position is inside the screen (check left and top borders) - QPoint pos(std::max(expected_pos.x(), available_geometry.left()), - std::max(expected_pos.y(), available_geometry.top())); - - // Find the maximum position that still ensures that the frame is completely visible inside the screen (check right and bottom borders) - QPoint max_pos(available_geometry.left() + available_geometry.width() - frameGeometry().width(), - available_geometry.top() + available_geometry.height() - frameGeometry().height()); - - // Make sure that the "maximum" position is inside the screen (check left and top borders) - max_pos.setX(std::max(max_pos.x(), available_geometry.left())); - max_pos.setY(std::max(max_pos.y(), available_geometry.top())); - - // Adjust the expected position accordingly - pos.setX(std::min(pos.x(), max_pos.x())); - pos.setY(std::min(pos.y(), max_pos.y())); - - // Set the new position - setFramePosition(pos); - - QWindow::showEvent(event); -} - -void gs_frame::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKeySequence& key_sequence) -{ - gui_log.notice("Game window registered shortcut: %s (%s)", shortcut_key, key_sequence.toString()); - - if (m_disable_kb_hotkeys) - { - return; - } - - switch (shortcut_key) - { - case gui::shortcuts::shortcut::gw_toggle_fullscreen: - { - toggle_fullscreen(); - break; - } - case gui::shortcuts::shortcut::gw_exit_fullscreen: - { - if (visibility() == FullScreen) - { - toggle_fullscreen(); - } - break; - } - case gui::shortcuts::shortcut::gw_log_mark: - { - static int count = 0; - mark_log.success("Made forced mark %d in log", ++count); - break; - } - case gui::shortcuts::shortcut::gw_mouse_lock: - { - toggle_mouselock(); - break; - } - case gui::shortcuts::shortcut::gw_screenshot: - { - g_user_asked_for_screenshot = true; - break; - } - case gui::shortcuts::shortcut::gw_toggle_recording: - { - toggle_recording(); - break; - } - case gui::shortcuts::shortcut::gw_pause_play: - { - switch (Emu.GetStatus()) - { - case system_state::ready: - { - Emu.Run(true); - return; - } - case system_state::paused: - { - Emu.Resume(); - return; - } - default: - { - Emu.Pause(); - return; - } - } - break; - } - case gui::shortcuts::shortcut::gw_restart: - { - if (Emu.IsStopped()) - { - Emu.Restart(); - return; - } - - extern bool boot_last_savestate(bool testing); - boot_last_savestate(false); - break; - } - case gui::shortcuts::shortcut::gw_savestate: - { - if (!g_cfg.savestate.suspend_emu) - { - Emu.after_kill_callback = []() - { - Emu.Restart(); - }; - - // Make sure we keep the game window opened - Emu.SetContinuousMode(true); - } - - Emu.Kill(false, true); - return; - } - case gui::shortcuts::shortcut::gw_rsx_capture: - { - g_user_asked_for_frame_capture = true; - break; - } - case gui::shortcuts::shortcut::gw_frame_limit: - { - g_disable_frame_limit = !g_disable_frame_limit; - gui_log.warning("%s boost mode", g_disable_frame_limit.load() ? "Enabled" : "Disabled"); - break; - } - case gui::shortcuts::shortcut::gw_toggle_mouse_and_keyboard: - { - input::toggle_mouse_and_keyboard(); - break; - } - case gui::shortcuts::shortcut::gw_home_menu: - { - pad::g_home_menu_requested = true; - break; - } - case gui::shortcuts::shortcut::gw_mute_unmute: - { - audio::toggle_mute(); - break; - } - case gui::shortcuts::shortcut::gw_volume_up: - { - audio::change_volume(5); - break; - } - case gui::shortcuts::shortcut::gw_volume_down: - { - audio::change_volume(-5); - break; - } - default: - { - break; - } - } -} - -void gs_frame::toggle_fullscreen() -{ - Emu.CallFromMainThread([this]() - { - if (visibility() == FullScreen) - { - // Change to the last recorded visibility. Sanitize it just in case. - if (m_last_visibility != Visibility::Maximized && m_last_visibility != Visibility::Windowed) - { - m_last_visibility = Visibility::Windowed; - } - setVisibility(m_last_visibility); - } - else - { - // Backup visibility for exiting fullscreen mode later. Don't do this in the visibilityChanged slot, - // since entering fullscreen from maximized will first change the visibility to windowed. - m_last_visibility = visibility(); - setVisibility(FullScreen); - } - }); -} - -void gs_frame::toggle_recording() -{ - utils::video_provider& video_provider = g_fxo->get(); - - if (g_recording_mode == recording_mode::cell) - { - gui_log.warning("A video recorder is already in use by cell. Regular recording can not proceed."); - m_video_encoder->stop(); - return; - } - - if (g_recording_mode.exchange(recording_mode::stopped) == recording_mode::rpcs3) - { - m_video_encoder->stop(); - - if (!video_provider.set_video_sink(nullptr, recording_mode::rpcs3)) - { - gui_log.warning("The video provider could not release the video sink. A sink with higher priority must have been set."); - } - - // Play a sound - if (const std::string sound_path = fs::get_config_dir() + "sounds/snd_recording.wav"; fs::is_file(sound_path)) - { - Emu.GetCallbacks().play_sound(sound_path); - } - else - { - QApplication::beep(); - } - - ensure(m_video_encoder->path().starts_with(fs::get_config_dir())); - const std::string shortpath = m_video_encoder->path().substr(fs::get_config_dir().size() - 1); // -1 for / - rsx::overlays::queue_message(tr("Recording saved: %0").arg(QString::fromStdString(shortpath)).toStdString()); - } - else - { - m_video_encoder->stop(); - - const std::string& id = Emu.GetTitleID(); - std::string video_path = fs::get_config_dir() + "recordings/"; - if (!id.empty()) - { - video_path += id + "/"; - } - - if (!fs::create_path(video_path) && fs::g_tls_error != fs::error::exist) - { - screenshot_log.error("Failed to create recordings path \"%s\" : %s", video_path, fs::g_tls_error); - return; - } - - if (!id.empty()) - { - video_path += id + "_"; - } - - video_path += "recording_" + date_time::current_time_narrow<'_'>() + ".mp4"; - - utils::video_encoder::frame_format output_format{}; - output_format.av_pixel_format = static_cast(g_cfg_recording.video.pixel_format.get()); - output_format.width = g_cfg_recording.video.width; - output_format.height = g_cfg_recording.video.height; - output_format.pitch = g_cfg_recording.video.width * 4; - - m_video_encoder->use_internal_audio = true; - m_video_encoder->use_internal_video = true; - m_video_encoder->set_path(video_path); - m_video_encoder->set_framerate(g_cfg_recording.video.framerate); - m_video_encoder->set_video_bitrate(g_cfg_recording.video.video_bps); - m_video_encoder->set_video_codec(g_cfg_recording.video.video_codec); - m_video_encoder->set_max_b_frames(g_cfg_recording.video.max_b_frames); - m_video_encoder->set_gop_size(g_cfg_recording.video.gop_size); - m_video_encoder->set_output_format(output_format); - - switch (g_cfg.audio.provider) - { - case audio_provider::none: - { - // Disable audio recording - m_video_encoder->use_internal_audio = false; - break; - } - case audio_provider::cell_audio: - { - const cell_audio_config& cfg = g_fxo->get().cfg; - m_video_encoder->set_sample_rate(cfg.audio_sampling_rate); - m_video_encoder->set_audio_channels(cfg.audio_channels); - break; - } - case audio_provider::rsxaudio: - { - const auto& rsx_audio = g_fxo->get(); - m_video_encoder->set_sample_rate(rsx_audio.get_sample_rate()); - m_video_encoder->set_audio_channels(rsx_audio.get_channel_count()); - break; - } - } - - m_video_encoder->set_audio_bitrate(g_cfg_recording.audio.audio_bps); - m_video_encoder->set_audio_codec(g_cfg_recording.audio.audio_codec); - m_video_encoder->encode(); - - if (m_video_encoder->has_error) - { - rsx::overlays::queue_message(tr("Recording not possible").toStdString()); - m_video_encoder->stop(); - return; - } - - if (!video_provider.set_video_sink(m_video_encoder, recording_mode::rpcs3)) - { - gui_log.warning("The video provider could not set the video sink. A sink with higher priority must have been set."); - rsx::overlays::queue_message(tr("Recording not possible").toStdString()); - m_video_encoder->stop(); - return; - } - - video_provider.set_pause_time_us(0); - - g_recording_mode = recording_mode::rpcs3; - - rsx::overlays::queue_message(tr("Recording started").toStdString()); - } -} - -void gs_frame::toggle_mouselock() -{ - // first we toggle the value - m_mouse_hide_and_lock = !m_mouse_hide_and_lock; - - // and update the cursor - handle_cursor(visibility(), false, false, true); -} - -void gs_frame::update_cursor() -{ - bool show_mouse; - - if (!isActive()) - { - // Show the mouse by default if this is not the active window - show_mouse = true; - } - else if (m_hide_mouse_after_idletime && !m_mousehide_timer.isActive()) - { - // Hide the mouse if the idle timeout was reached (which means that the timer isn't running) - show_mouse = false; - } - else if (visibility() == QWindow::Visibility::FullScreen) - { - // Fullscreen: Show or hide the mouse depending on the settings. - show_mouse = m_show_mouse_in_fullscreen; - } - else - { - // Windowed: Hide the mouse if it was locked by the user - show_mouse = !m_mouse_hide_and_lock; - } - - // Update Cursor if necessary - if (show_mouse != m_show_mouse.exchange(show_mouse)) - { - setCursor(m_show_mouse ? Qt::ArrowCursor : Qt::BlankCursor); - } -} - -bool gs_frame::get_mouse_lock_state() -{ - handle_cursor(visibility(), false, false, true); - - return isActive() && m_mouse_hide_and_lock; -} - -void gs_frame::hide_on_close() -{ - // Make sure not to save the hidden state, which is useless to us. - const Visibility current_visibility = visibility(); - m_gui_settings->SetValue(gui::gs_visibility, current_visibility == Visibility::Hidden ? Visibility::AutomaticVisibility : current_visibility, false); - m_gui_settings->SetValue(gui::gs_geometry, geometry(), true); - - if (!g_progr_text) - { - // Hide the dialog before stopping if no progress bar is being shown. - // Otherwise users might think that the game softlocked if stopping takes too long. - QWindow::hide(); - } -} - -void gs_frame::close() -{ - if (m_is_closing.exchange(true)) - { - gui_log.notice("Closing game window (ignored, already closing)"); - return; - } - - gui_log.notice("Closing game window"); - - if (m_ignore_stop_events) - { - return; - } - - Emu.CallFromMainThread([this]() - { - // Hide window if necessary - hide_on_close(); - - if (!Emu.IsStopped()) - { - // Notify progress dialog cancellation - g_system_progress_canceled = true; - - // Blocking shutdown request. Obsolete, but I'm keeping it here as last resort. - Emu.after_kill_callback = [this]() - { - deleteLater(); - }; - Emu.GracefulShutdown(true); - } - else - { - deleteLater(); - } - }); -} - -void gs_frame::reset() -{ -} - -bool gs_frame::shown() -{ - return QWindow::isVisible(); -} - -void gs_frame::hide() -{ - Emu.CallFromMainThread([this]() - { - QWindow::hide(); - }); -} - -void gs_frame::show() -{ - Emu.CallFromMainThread([this]() - { - QWindow::show(); - - if (g_cfg.misc.start_fullscreen || m_start_games_fullscreen) - { - setVisibility(FullScreen); - } - else if (const QVariant var = m_gui_settings->GetValue(gui::gs_visibility); var.canConvert()) - { - // Restore saved visibility from last time. Make sure not to hide the window, or the user can't access it anymore. - if (const Visibility visibility = var.value(); visibility != Visibility::Hidden) - { - setVisibility(visibility); - } - } - }); -} - -display_handle_t gs_frame::handle() const -{ -#ifdef _WIN32 - return reinterpret_cast(this->winId()); -#elif defined(__APPLE__) - return reinterpret_cast(this->winId()); // NSView -#else -#ifdef HAVE_WAYLAND - QPlatformNativeInterface* native = QGuiApplication::platformNativeInterface(); - struct wl_display* wl_dpy = static_cast( - native->nativeResourceForWindow("display", NULL)); - struct wl_surface* wl_surf = static_cast( - native->nativeResourceForWindow("surface", const_cast(static_cast(this)))); - if (wl_dpy != nullptr && wl_surf != nullptr) - { - return std::make_pair(wl_dpy, wl_surf); - } - else -#endif -#ifdef HAVE_X11 - { - return std::make_pair(XOpenDisplay(0), static_cast(this->winId())); - } -#else - fmt::throw_exception("Vulkan X11 support disabled at compile-time."); -#endif -#endif -} - -draw_context_t gs_frame::make_context() -{ - return nullptr; -} - -void gs_frame::set_current(draw_context_t context) -{ - Q_UNUSED(context) -} - -void gs_frame::delete_context(draw_context_t context) -{ - Q_UNUSED(context) -} - -int gs_frame::client_width() -{ -#ifdef _WIN32 - RECT rect; - if (GetClientRect(reinterpret_cast(winId()), &rect)) - { - return rect.right - rect.left; - } -#endif // _WIN32 - return width() * devicePixelRatio(); -} - -int gs_frame::client_height() -{ -#ifdef _WIN32 - RECT rect; - if (GetClientRect(reinterpret_cast(winId()), &rect)) - { - return rect.bottom - rect.top; - } -#endif // _WIN32 - return height() * devicePixelRatio(); -} - -f64 gs_frame::client_display_rate() -{ - f64 rate = 20.; // Minimum is 20 - - Emu.BlockingCallFromMainThread([this, &rate]() - { - const QList screens = QGuiApplication::screens(); - - for (int i = 0; i < screens.count(); i++) - { - rate = std::fmax(rate, ::at32(screens, i)->refreshRate()); - } - }); - - return rate; -} - -void gs_frame::flip(draw_context_t, bool /*skip_frame*/) -{ - static Timer fps_t; - - if (!m_flip_showed_frame) - { - // Show on first flip - m_flip_showed_frame = true; - show(); - fps_t.Start(); - } - - ++m_frames; - - if (fps_t.GetElapsedTimeInSec() >= 0.5) - { - std::string new_title = Emu.GetFormattedTitle(m_frames / fps_t.GetElapsedTimeInSec()); - - if (new_title != m_window_title) - { - m_window_title = new_title; - - Emu.CallFromMainThread([this, title = std::move(new_title)]() - { - setTitle(QString::fromStdString(title)); - }); - } - - m_frames = 0; - fps_t.Start(); - } - - if (g_user_asked_for_recording.exchange(false)) - { - Emu.CallFromMainThread([this]() - { - toggle_recording(); - }); - } -} - -bool gs_frame::can_consume_frame() const -{ - utils::video_provider& video_provider = g_fxo->get(); - return video_provider.can_consume_frame(); -} - -void gs_frame::present_frame(std::vector& data, u32 pitch, u32 width, u32 height, bool is_bgra) const -{ - utils::video_provider& video_provider = g_fxo->get(); - video_provider.present_frame(data, pitch, width, height, is_bgra); -} - -void gs_frame::take_screenshot(std::vector&& data, u32 sshot_width, u32 sshot_height, bool is_bgra) -{ - std::thread( - [sshot_width, sshot_height, is_bgra](std::vector sshot_data) - { - thread_base::set_name("Screenshot"); - - screenshot_log.notice("Taking screenshot (%dx%d)", sshot_width, sshot_height); - - const std::string& id = Emu.GetTitleID(); - std::string screen_path = fs::get_config_dir() + "screenshots/"; - if (!id.empty()) - { - screen_path += id + "/"; - } - - if (!fs::create_path(screen_path) && fs::g_tls_error != fs::error::exist) - { - screenshot_log.error("Failed to create screenshot path \"%s\" : %s", screen_path, fs::g_tls_error); - return; - } - - std::string filename = screen_path; - if (!id.empty()) - { - filename += id + "_"; - } - - filename += "screenshot_" + date_time::current_time_narrow<'_'>() + ".png"; - - fs::file sshot_file(filename, fs::open_mode::create + fs::open_mode::write + fs::open_mode::excl); - if (!sshot_file) - { - screenshot_log.error("Failed to save screenshot \"%s\" : %s", filename, fs::g_tls_error); - return; - } - - std::vector sshot_data_alpha(sshot_data.size()); - const u32* sshot_ptr = reinterpret_cast(sshot_data.data()); - u32* alpha_ptr = reinterpret_cast(sshot_data_alpha.data()); - - if (is_bgra) [[likely]] - { - for (usz index = 0; index < sshot_data.size() / sizeof(u32); index++) - { - alpha_ptr[index] = ((sshot_ptr[index] & 0xFF) << 16) | (sshot_ptr[index] & 0xFF00) | ((sshot_ptr[index] & 0xFF0000) >> 16) | 0xFF000000; - } - } - else - { - for (usz index = 0; index < sshot_data.size() / sizeof(u32); index++) - { - alpha_ptr[index] = sshot_ptr[index] | 0xFF000000; - } - } - - screenshot_info manager; - { - auto& s = g_fxo->get(); - std::lock_guard lock(s.mutex); - manager = s; - } - - struct scoped_png_ptrs - { - png_structp write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); - png_infop info_ptr = png_create_info_struct(write_ptr); - - ~scoped_png_ptrs() - { - png_free_data(write_ptr, info_ptr, PNG_FREE_ALL, -1); - png_destroy_write_struct(&write_ptr, &info_ptr); - } - }; - - png_text text[6] = {}; - int num_text = 0; - - const QDateTime date_time = QDateTime::currentDateTime(); - const std::string creation_time = date_time.toString("yyyy:MM:dd hh:mm:ss").toStdString(); - const std::string photo_title = manager.get_photo_title(); - const std::string game_title = manager.get_game_title(); - const std::string game_comment = manager.get_game_comment(); - const std::string& title_id = Emu.GetTitleID(); - - // Write tEXt chunk - text[num_text].compression = PNG_TEXT_COMPRESSION_NONE; - text[num_text].key = const_cast("Creation Time"); - text[num_text].text = const_cast(creation_time.c_str()); - ++num_text; - text[num_text].compression = PNG_TEXT_COMPRESSION_NONE; - text[num_text].key = const_cast("Source"); - text[num_text].text = const_cast("RPCS3"); // Originally PlayStation(R)3 - ++num_text; - text[num_text].compression = PNG_TEXT_COMPRESSION_NONE; - text[num_text].key = const_cast("Title ID"); - text[num_text].text = const_cast(title_id.c_str()); - ++num_text; - - // Write tTXt chunk (they probably meant zTXt) - text[num_text].compression = PNG_TEXT_COMPRESSION_zTXt; - text[num_text].key = const_cast("Title"); - text[num_text].text = const_cast(photo_title.c_str()); - ++num_text; - text[num_text].compression = PNG_TEXT_COMPRESSION_zTXt; - text[num_text].key = const_cast("Game Title"); - text[num_text].text = const_cast(game_title.c_str()); - ++num_text; - text[num_text].compression = PNG_TEXT_COMPRESSION_zTXt; - text[num_text].key = const_cast("Comment"); - text[num_text].text = const_cast(game_comment.c_str()); - - // Create image from data - QImage img(sshot_data_alpha.data(), sshot_width, sshot_height, sshot_width * 4, QImage::Format_RGBA8888); - - // Scale image if necessary - const auto& avconf = g_fxo->get(); - auto new_size = avconf.aspect_convert_dimensions(size2u{u32(img.width()), u32(img.height())}); - - if (new_size.width != static_cast(img.width()) || new_size.height != static_cast(img.height())) - { - img = img.scaled(QSize(new_size.width, new_size.height), Qt::AspectRatioMode::IgnoreAspectRatio, Qt::TransformationMode::SmoothTransformation); - img.convertTo(QImage::Format_RGBA8888); // The current Qt version changes the image format during smooth scaling, so we have to change it back. - } - - // Create row pointers for libpng - std::vector rows(img.height()); - for (int y = 0; y < img.height(); y++) - rows[y] = img.scanLine(y); - - std::vector encoded_png; - - const auto write_png = [&]() - { - const scoped_png_ptrs ptrs; - png_set_IHDR(ptrs.write_ptr, ptrs.info_ptr, img.width(), img.height(), 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - png_set_text(ptrs.write_ptr, ptrs.info_ptr, text, 6); - png_set_rows(ptrs.write_ptr, ptrs.info_ptr, &rows[0]); - png_set_write_fn(ptrs.write_ptr, &encoded_png, [](png_structp png_ptr, png_bytep data, png_size_t length) - { - std::vector* p = static_cast*>(png_get_io_ptr(png_ptr)); - p->insert(p->end(), data, data + length); - }, - nullptr); - png_write_png(ptrs.write_ptr, ptrs.info_ptr, PNG_TRANSFORM_IDENTITY, nullptr); - }; - - write_png(); - sshot_file.write(encoded_png.data(), encoded_png.size()); - - screenshot_log.success("Successfully saved screenshot to %s", filename); - - if (manager.is_enabled) - { - const std::string cell_sshot_overlay_path = manager.get_overlay_path(); - if (fs::is_file(cell_sshot_overlay_path)) - { - screenshot_log.notice("Adding overlay to cell screenshot from %s", cell_sshot_overlay_path); - - QImage overlay_img; - - if (!overlay_img.load(QString::fromStdString(cell_sshot_overlay_path))) - { - screenshot_log.error("Failed to read cell screenshot overlay '%s' : %s", cell_sshot_overlay_path, fs::g_tls_error); - return; - } - - // Games choose the overlay file and the offset based on the current video resolution. - // We need to scale the overlay if our resolution scaling causes the image to have a different size. - - // Scale the resolution first (as seen before with the image) - new_size = avconf.aspect_convert_dimensions(size2u{avconf.resolution_x, avconf.resolution_y}); - - if (new_size.width != static_cast(img.width()) || new_size.height != static_cast(img.height())) - { - const int scale = rsx::get_resolution_scale_percent(); - const int x = (scale * manager.overlay_offset_x) / 100; - const int y = (scale * manager.overlay_offset_y) / 100; - const int width = (scale * overlay_img.width()) / 100; - const int height = (scale * overlay_img.height()) / 100; - - screenshot_log.notice("Scaling overlay from %dx%d at offset (%d,%d) to %dx%d at offset (%d,%d)", - overlay_img.width(), overlay_img.height(), manager.overlay_offset_x, manager.overlay_offset_y, width, height, x, y); - - manager.overlay_offset_x = x; - manager.overlay_offset_y = y; - overlay_img = overlay_img.scaled(QSize(width, height), Qt::AspectRatioMode::IgnoreAspectRatio, Qt::TransformationMode::SmoothTransformation); - } - - if (manager.overlay_offset_x < static_cast(img.width()) && - manager.overlay_offset_y < static_cast(img.height()) && - manager.overlay_offset_x + overlay_img.width() > 0 && - manager.overlay_offset_y + overlay_img.height() > 0) - { - QImage screenshot_img(rows[0], img.width(), img.height(), QImage::Format_RGBA8888); - QPainter painter(&screenshot_img); - painter.drawImage(manager.overlay_offset_x, manager.overlay_offset_y, overlay_img); - painter.end(); - - std::memcpy(rows[0], screenshot_img.constBits(), screenshot_img.sizeInBytes()); - - screenshot_log.success("Applied screenshot overlay '%s'", cell_sshot_overlay_path); - } - } - - const std::string cell_sshot_filename = manager.get_screenshot_path(date_time.toString("yyyy/MM/dd").toStdString()); - const std::string cell_sshot_dir = fs::get_parent_dir(cell_sshot_filename); - - screenshot_log.notice("Saving cell screenshot to %s", cell_sshot_filename); - - if (!fs::create_path(cell_sshot_dir) && fs::g_tls_error != fs::error::exist) - { - screenshot_log.error("Failed to create cell screenshot dir \"%s\" : %s", cell_sshot_dir, fs::g_tls_error); - return; - } - - fs::file cell_sshot_file(cell_sshot_filename, fs::open_mode::create + fs::open_mode::write + fs::open_mode::excl); - if (!cell_sshot_file) - { - screenshot_log.error("Failed to save cell screenshot \"%s\" : %s", cell_sshot_filename, fs::g_tls_error); - return; - } - - encoded_png.clear(); - write_png(); - cell_sshot_file.write(encoded_png.data(), encoded_png.size()); - - screenshot_log.success("Successfully saved cell screenshot to %s", cell_sshot_filename); - } - - // Play a sound - Emu.CallFromMainThread([]() - { - if (const std::string sound_path = fs::get_config_dir() + "sounds/snd_screenshot.wav"; fs::is_file(sound_path)) - { - Emu.GetCallbacks().play_sound(sound_path); - } - else - { - QApplication::beep(); - } - }); - - ensure(filename.starts_with(fs::get_config_dir())); - const std::string shortpath = filename.substr(fs::get_config_dir().size() - 1); // -1 for / - rsx::overlays::queue_message(tr("Screenshot saved: %0").arg(QString::fromStdString(shortpath)).toStdString()); - - return; - }, - std::move(data)) - .detach(); -} - -void gs_frame::mouseDoubleClickEvent(QMouseEvent* ev) -{ - if (m_disable_mouse) - { - return; - } - - switch (g_cfg.io.move) - { - case move_handler::mouse: - case move_handler::raw_mouse: -#ifdef HAVE_LIBEVDEV - case move_handler::gun: -#endif - return; - default: - break; - } - - if (ev->button() == Qt::LeftButton) - { - toggle_fullscreen(); - } -} - -void gs_frame::handle_cursor(QWindow::Visibility visibility, bool visibility_changed, bool active_changed, bool start_idle_timer) -{ - // Let's reload the gui settings when the visibility or the active window changes. - if (visibility_changed || active_changed) - { - load_gui_settings(); - - // Update the mouse lock state if the visibility changed. - if (visibility_changed) - { - // In fullscreen we default to hiding and locking. In windowed mode we do not want the lock by default. - m_mouse_hide_and_lock = (visibility == QWindow::Visibility::FullScreen) && m_lock_mouse_in_fullscreen; - } - } - - // Update the mouse hide timer - if (m_hide_mouse_after_idletime && start_idle_timer) - { - m_mousehide_timer.start(m_hide_mouse_idletime); - } - else - { - m_mousehide_timer.stop(); - } - - // Update the cursor visibility - update_cursor(); -} - -void gs_frame::mouse_hide_timeout() -{ - // Our idle timeout occurred, so we update the cursor - if (m_hide_mouse_after_idletime && m_show_mouse) - { - handle_cursor(visibility(), false, false, false); - } -} - -bool gs_frame::event(QEvent* ev) -{ - if (ev->type() == QEvent::Close) - { - if (m_gui_settings->GetValue(gui::ib_confirm_exit).toBool()) - { - if (visibility() == FullScreen) - { - toggle_fullscreen(); - } - - int result = QMessageBox::Yes; - m_gui_settings->ShowConfirmationBox(tr("Exit Game?"), - tr("Do you really want to exit the game?

Any unsaved progress will be lost!
"), - gui::ib_confirm_exit, &result, nullptr); - - if (result != QMessageBox::Yes) - { - return true; - } - } - - gui_log.notice("Game window close event issued"); - - if (m_ignore_stop_events) - { - return QWindow::event(ev); - } - - if (Emu.IsStopped()) - { - // This should be unreachable, but never say never. Properly close the window anyway. - close(); - } - else - { - // Notify progress dialog cancellation - g_system_progress_canceled = true; - - // Issue async shutdown - Emu.GracefulShutdown(true, true); - - // Hide window if necessary - hide_on_close(); - - // Do not propagate the close event. It will be closed by the rsx_thread. - return true; - } - } - else if (ev->type() == QEvent::MouseMove && (!m_show_mouse || m_mousehide_timer.isActive())) - { - // This will make the cursor visible again if it was hidden by the mouse idle timeout - handle_cursor(visibility(), false, false, true); - } - return QWindow::event(ev); -} - -void gs_frame::progress_reset(bool reset_limit) -{ - m_progress_indicator->reset(); - - if (reset_limit) - { - progress_set_limit(100); - } -} - -void gs_frame::progress_set_value(int value) -{ - m_progress_indicator->set_value(value); -} - -void gs_frame::progress_increment(int delta) -{ - if (delta != 0) - { - m_progress_indicator->set_value(m_progress_indicator->value() + delta); - } -} - -void gs_frame::progress_set_limit(int limit) -{ - m_progress_indicator->set_range(0, limit); -} - -bool gs_frame::has_alpha() -{ - return format().hasAlpha(); -} diff --git a/rpcs3qt-legacy/gs_frame.h b/rpcs3qt-legacy/gs_frame.h deleted file mode 100644 index 7ff47877a..000000000 --- a/rpcs3qt-legacy/gs_frame.h +++ /dev/null @@ -1,125 +0,0 @@ -#pragma once - -#include "shortcut_handler.h" -#include "progress_indicator.h" -#include "util/types.hpp" -#include "util/atomic.hpp" -#include "util/media_utils.h" -#include "Emu/RSX/GSFrameBase.h" - -#include -#include -#include - -#include -#include - -class gui_settings; -enum class video_renderer; - -class gs_frame : public QWindow, public GSFrameBase -{ - Q_OBJECT - -private: - // taskbar progress - std::unique_ptr m_progress_indicator; - - shortcut_handler* m_shortcut_handler = nullptr; - - QRect m_initial_geometry; - - std::shared_ptr m_gui_settings; - QTimer m_mousehide_timer; - - u64 m_frames = 0; - std::string m_window_title; - QWindow::Visibility m_last_visibility = Visibility::Windowed; - atomic_t m_is_closing = false; - atomic_t m_show_mouse = true; - bool m_disable_mouse = false; - bool m_disable_kb_hotkeys = false; - bool m_mouse_hide_and_lock = false; - bool m_show_mouse_in_fullscreen = false; - bool m_lock_mouse_in_fullscreen = true; - bool m_hide_mouse_after_idletime = false; - u32 m_hide_mouse_idletime = 2000; // ms - bool m_flip_showed_frame = false; - bool m_start_games_fullscreen = false; - bool m_ignore_stop_events = false; - - std::shared_ptr m_video_encoder{}; - -public: - explicit gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, std::shared_ptr gui_settings, bool force_fullscreen); - ~gs_frame(); - - video_renderer renderer() const - { - return m_renderer; - }; - - void ignore_stop_events() - { - m_ignore_stop_events = true; - } - - draw_context_t make_context() override; - void set_current(draw_context_t context) override; - void delete_context(draw_context_t context) override; - void toggle_fullscreen() override; - - void update_shortcuts(); - - // taskbar progress - void progress_reset(bool reset_limit = false); - void progress_set_value(int value); - void progress_increment(int delta); - void progress_set_limit(int limit); - - /* - Returns true if the mouse is locked inside the game window. - Also conveniently updates the cursor visibility, because using it from a mouse handler indicates mouse emulation. - */ - bool get_mouse_lock_state(); - - bool can_consume_frame() const override; - void present_frame(std::vector& data, u32 pitch, u32 width, u32 height, bool is_bgra) const override; - void take_screenshot(std::vector&& data, u32 sshot_width, u32 sshot_height, bool is_bgra) override; - -protected: - video_renderer m_renderer; - - void paintEvent(QPaintEvent* event) override; - void showEvent(QShowEvent* event) override; - - void close() override; - void reset() override; - - bool shown() override; - void hide() override; - void show() override; - void mouseDoubleClickEvent(QMouseEvent* ev) override; - - display_handle_t handle() const override; - - void flip(draw_context_t context, bool skip_frame = false) override; - int client_width() override; - int client_height() override; - f64 client_display_rate() override; - bool has_alpha() override; - - bool event(QEvent* ev) override; - -private: - void load_gui_settings(); - void hide_on_close(); - void toggle_recording(); - void toggle_mouselock(); - void update_cursor(); - void handle_cursor(QWindow::Visibility visibility, bool visibility_changed, bool active_changed, bool start_idle_timer); - -private Q_SLOTS: - void mouse_hide_timeout(); - void handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKeySequence& key_sequence); -}; diff --git a/rpcs3qt-legacy/gui_application.cpp b/rpcs3qt-legacy/gui_application.cpp deleted file mode 100644 index 43362f861..000000000 --- a/rpcs3qt-legacy/gui_application.cpp +++ /dev/null @@ -1,1477 +0,0 @@ -#include "stdafx.h" -#include "gui_application.h" - -#include "qt_utils.h" -#include "permissions.h" -#include "welcome_dialog.h" -#include "main_window.h" -#include "emu_settings.h" -#include "gui_settings.h" -#include "persistent_settings.h" -#include "gs_frame.h" -#include "gl_gs_frame.h" -#include "localized_emu.h" -#include "qt_camera_handler.h" -#include "qt_music_handler.h" -#include "rpcs3_version.h" -#include "display_sleep_control.h" - -#ifdef WITH_DISCORD_RPC -#include "_discord_utils.h" -#endif - -#include "Emu/Audio/audio_utils.h" -#include "rpcsx/fw/ps3/cellSysutil.h" -#include "Emu/Io/Null/null_camera_handler.h" -#include "Emu/Io/Null/null_music_handler.h" -#include "Emu/vfs_config.h" -#include "util/init_mutex.hpp" -#include "util/console.h" - -#include "Input/keyboard_pad_handler.h" -#include "Emu/Io/Null/NullPadHandler.h" -#include "Input/ds3_pad_handler.h" -#include "Input/ds4_pad_handler.h" -#include "Input/dualsense_pad_handler.h" -#include "Input/skateboard_pad_handler.h" -#include "Input/ps_move_handler.h" -#include "Input/virtual_pad_handler.h" -#ifdef _WIN32 -#include "Input/xinput_pad_handler.h" -#include "Input/mm_joystick_handler.h" -#endif - -#ifdef HAVE_SDL3 -#include "Input/sdl_pad_handler.h" -#endif - -#ifdef HAVE_LIBEVDEV -#include "Input/evdev_joystick_handler.h" -#endif - -#include "qt_video_source.h" -#include "trophy_notification_helper.h" -#include "save_data_dialog.h" -#include "msg_dialog_frame.h" -#include "osk_dialog_frame.h" -#include "recvmessage_dialog_frame.h" -#include "sendmessage_dialog_frame.h" -#include "stylesheets.h" -#include "progress_dialog.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "Emu/RSX/Null/NullGSRender.h" -#include "Emu/RSX/GL/GLGSRender.h" - -#if defined(HAVE_VULKAN) -#include "Emu/RSX/VK/VKGSRender.h" -#endif - -#ifdef _WIN32 -#include -#include - -#include "Emu/Cell/lv2/sys_usbd.h" -#endif - -LOG_CHANNEL(gui_log, "GUI"); - -std::unique_ptr g_raw_mouse_handler; - -s32 gui_application::m_language_id = static_cast(CELL_SYSUTIL_LANG_ENGLISH_US); - -[[noreturn]] void report_fatal_error(std::string_view text, bool is_html = false, bool include_help_text = true); - -gui_application::gui_application(int& argc, char** argv) : QApplication(argc, argv) -{ - std::setlocale(LC_NUMERIC, "C"); // On linux Qt changes to system locale while initializing QCoreApplication -} - -gui_application::~gui_application() -{ -#ifdef WITH_DISCORD_RPC - discord::shutdown(); -#endif -#ifdef _WIN32 - unregister_device_notification(); -#endif -} - -bool gui_application::Init() -{ -#ifndef __APPLE__ - setWindowIcon(QIcon(":/rpcs3.ico")); -#endif - - if (!rpcs3::is_release_build() && !rpcs3::is_local_build()) - { - const std::string_view branch_name = rpcs3::get_full_branch(); - gui_log.warning("Experimental Build Warning! Build origin: %s", branch_name); - - QMessageBox msg; - msg.setWindowModality(Qt::WindowModal); - msg.setWindowTitle(tr("Experimental Build Warning")); - msg.setIcon(QMessageBox::Critical); - msg.setTextFormat(Qt::RichText); - msg.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msg.setDefaultButton(QMessageBox::No); - msg.setText(gui::utils::make_paragraph(tr( - "Please understand that this build is not an official RPCS3 release.\n" - "This build contains changes that may break games, or even damage your data.\n" - "We recommend to download and use the official build from the %0.\n" - "\n" - "Build origin: %1\n" - "Do you wish to use this build anyway?") - .arg(gui::utils::make_link(tr("RPCS3 website"), "https://rpcs3.net/download")) - .arg(Qt::convertFromPlainText(branch_name.data())))); - msg.layout()->setSizeConstraint(QLayout::SetFixedSize); - - if (msg.exec() == QMessageBox::No) - { - return false; - } - } - - m_emu_settings = std::make_shared(); - m_gui_settings = std::make_shared(); - m_persistent_settings = std::make_shared(); - - if (!m_emu_settings->Init()) - { - return false; - } - - if (m_gui_settings->GetValue(gui::m_attachCommandLine).toBool()) - { - utils::attach_console(utils::console_stream::std_err, true); - } - else - { - m_gui_settings->SetValue(gui::m_attachCommandLine, false); - } - - // The user might be set by cli arg. If not, set another user. - if (m_active_user.empty()) - { - // Get active user with standard user as fallback - m_active_user = m_persistent_settings->GetCurrentUser("00000001").toStdString(); - } - - // Force init the emulator - InitializeEmulator(m_active_user, m_show_gui); - - // Create the main window - if (m_show_gui) - { - m_main_window = new main_window(m_gui_settings, m_emu_settings, m_persistent_settings, nullptr); - - const auto codes = GetAvailableLanguageCodes(); - const auto language = m_gui_settings->GetValue(gui::loc_language).toString(); - const auto index = codes.indexOf(language); - - LoadLanguage(index < 0 ? QLocale(QLocale::English).bcp47Name() : ::at32(codes, index)); - } - - // Create callbacks from the emulator, which reference the handlers. - InitializeCallbacks(); - - // Create connects to propagate events throughout Gui. - InitializeConnects(); - - if (m_gui_settings->GetValue(gui::ib_show_welcome).toBool()) - { - welcome_dialog* welcome = new welcome_dialog(m_gui_settings, false); - - if (welcome->exec() == QDialog::Rejected) - { - // If the agreement on RPCS3's usage conditions was not accepted by the user, ask the main window to gracefully terminate - return false; - } - } - - // Check maxfiles - if (utils::get_maxfiles() < 4096) - { - QMessageBox::warning(nullptr, - tr("Warning"), - tr("The current limit of maximum file descriptors is too low.\n" - "Some games will crash.\n" - "\n" - "Please increase the limit before running RPCS3.")); - } - - if (m_main_window && !m_main_window->Init(m_with_cli_boot)) - { - return false; - } - -#ifdef WITH_DISCORD_RPC - // Discord Rich Presence Integration - if (m_gui_settings->GetValue(gui::m_richPresence).toBool()) - { - discord::initialize(); - } -#endif - - // Install native event filter -#ifdef _WIN32 // Currently only needed for raw mouse input on windows - installNativeEventFilter(&m_native_event_filter); - - if (m_main_window) - { - register_device_notification(m_main_window->winId()); - } -#endif - - return true; -} - -void gui_application::SwitchTranslator(QTranslator& translator, const QString& filename, const QString& language_code) -{ - // remove the old translator - removeTranslator(&translator); - - const QString lang_path = QLibraryInfo::path(QLibraryInfo::TranslationsPath) + QStringLiteral("/"); - const QString file_path = lang_path + filename; - - if (QFileInfo(file_path).isFile()) - { - // load the new translator - if (translator.load(file_path)) - { - installTranslator(&translator); - } - } - else if (QString default_code = QLocale(QLocale::English).bcp47Name(); language_code != default_code) - { - // show error, but ignore default case "en", since it is handled in source code - gui_log.error("No translation file found in: %s", file_path); - - // reset current language to default "en" - set_language_code(std::move(default_code)); - } -} - -void gui_application::LoadLanguage(const QString& language_code) -{ - if (m_language_code == language_code) - { - return; - } - - set_language_code(language_code); - - const QLocale locale = QLocale(language_code); - const QString locale_name = QLocale::languageToString(locale.language()); - - QLocale::setDefault(locale); - - // Idk if this is overruled by the QLocale default, so I'll change it here just to be sure. - // As per QT recommendations to avoid conflicts for POSIX functions - std::setlocale(LC_NUMERIC, "C"); - - SwitchTranslator(m_translator, QStringLiteral("rpcs3_%1.qm").arg(language_code), language_code); - - if (m_main_window) - { - const QString default_code = QLocale(QLocale::English).bcp47Name(); - QStringList language_codes = GetAvailableLanguageCodes(); - - if (!language_codes.contains(default_code)) - { - language_codes.prepend(default_code); - } - - m_main_window->RetranslateUI(language_codes, m_language_code); - } - - m_gui_settings->SetValue(gui::loc_language, m_language_code); - - gui_log.notice("Current language changed to %s (%s)", locale_name, language_code); -} - -QStringList gui_application::GetAvailableLanguageCodes() -{ - QStringList language_codes; - - const QString language_path = QLibraryInfo::path(QLibraryInfo::TranslationsPath); - - if (QFileInfo(language_path).isDir()) - { - const QDir dir(language_path); - const QStringList filenames = dir.entryList(QStringList("rpcs3_*.qm")); - - for (const QString& filename : filenames) - { - QString language_code = filename; // "rpcs3_en.qm" - language_code.truncate(language_code.lastIndexOf('.')); // "rpcs3_en" - language_code.remove(0, language_code.indexOf('_') + 1); // "en" - - if (language_codes.contains(language_code)) - { - gui_log.error("Found duplicate language '%s' (%s)", language_code, filename); - } - else - { - language_codes << language_code; - } - } - } - - return language_codes; -} - -void gui_application::set_language_code(QString language_code) -{ - m_language_code = language_code; - - // Transform language code to lowercase and use '-' - language_code = language_code.toLower().replace("_", "-"); - - // Try to find the CELL language ID for this language code - static const std::map language_ids = { - {"ja", CELL_SYSUTIL_LANG_JAPANESE}, - {"en", CELL_SYSUTIL_LANG_ENGLISH_US}, - {"en-us", CELL_SYSUTIL_LANG_ENGLISH_US}, - {"en-gb", CELL_SYSUTIL_LANG_ENGLISH_GB}, - {"fr", CELL_SYSUTIL_LANG_FRENCH}, - {"es", CELL_SYSUTIL_LANG_SPANISH}, - {"de", CELL_SYSUTIL_LANG_GERMAN}, - {"it", CELL_SYSUTIL_LANG_ITALIAN}, - {"nl", CELL_SYSUTIL_LANG_DUTCH}, - {"pt", CELL_SYSUTIL_LANG_PORTUGUESE_PT}, - {"pt-pt", CELL_SYSUTIL_LANG_PORTUGUESE_PT}, - {"pt-br", CELL_SYSUTIL_LANG_PORTUGUESE_BR}, - {"ru", CELL_SYSUTIL_LANG_RUSSIAN}, - {"ko", CELL_SYSUTIL_LANG_KOREAN}, - {"zh", CELL_SYSUTIL_LANG_CHINESE_T}, - {"zh-hant", CELL_SYSUTIL_LANG_CHINESE_T}, - {"zh-hans", CELL_SYSUTIL_LANG_CHINESE_S}, - {"fi", CELL_SYSUTIL_LANG_FINNISH}, - {"sv", CELL_SYSUTIL_LANG_SWEDISH}, - {"da", CELL_SYSUTIL_LANG_DANISH}, - {"no", CELL_SYSUTIL_LANG_NORWEGIAN}, - {"nn", CELL_SYSUTIL_LANG_NORWEGIAN}, - {"nb", CELL_SYSUTIL_LANG_NORWEGIAN}, - {"pl", CELL_SYSUTIL_LANG_POLISH}, - {"tr", CELL_SYSUTIL_LANG_TURKISH}, - }; - - // Check direct match first - const auto it = language_ids.find(language_code); - if (it != language_ids.cend()) - { - m_language_id = static_cast(it->second); - return; - } - - // Try to find closest match - for (const auto& [code, id] : language_ids) - { - if (language_code.startsWith(code)) - { - m_language_id = static_cast(id); - return; - } - } - - // Fallback to English (US) - m_language_id = static_cast(CELL_SYSUTIL_LANG_ENGLISH_US); -} - -s32 gui_application::get_language_id() -{ - return m_language_id; -} - -void gui_application::InitializeConnects() -{ - connect(&m_timer, &QTimer::timeout, this, &gui_application::UpdatePlaytime); - connect(this, &gui_application::OnEmulatorRun, this, &gui_application::StartPlaytime); - connect(this, &gui_application::OnEmulatorStop, this, &gui_application::StopPlaytime); - connect(this, &gui_application::OnEmulatorPause, this, &gui_application::StopPlaytime); - connect(this, &gui_application::OnEmulatorResume, this, &gui_application::StartPlaytime); - connect(this, &QGuiApplication::applicationStateChanged, this, &gui_application::OnAppStateChanged); - - if (m_main_window) - { - connect(m_main_window, &main_window::RequestLanguageChange, this, &gui_application::LoadLanguage); - connect(m_main_window, &main_window::RequestGlobalStylesheetChange, this, &gui_application::OnChangeStyleSheetRequest); - connect(m_main_window, &main_window::NotifyEmuSettingsChange, this, [this]() - { - OnEmuSettingsChange(); - }); - connect(m_main_window, &main_window::NotifyShortcutHandlers, this, &gui_application::OnShortcutChange); - - connect(this, &gui_application::OnEmulatorRun, m_main_window, &main_window::OnEmuRun); - connect(this, &gui_application::OnEmulatorStop, m_main_window, &main_window::OnEmuStop); - connect(this, &gui_application::OnEmulatorPause, m_main_window, &main_window::OnEmuPause); - connect(this, &gui_application::OnEmulatorResume, m_main_window, &main_window::OnEmuResume); - connect(this, &gui_application::OnEmulatorReady, m_main_window, &main_window::OnEmuReady); - connect(this, &gui_application::OnEnableDiscEject, m_main_window, &main_window::OnEnableDiscEject); - connect(this, &gui_application::OnEnableDiscInsert, m_main_window, &main_window::OnEnableDiscInsert); - - connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged, this, [this]() - { - OnChangeStyleSheetRequest(); - }); - } - -#ifdef WITH_DISCORD_RPC - connect(this, &gui_application::OnEmulatorRun, [this](bool /*start_playtime*/) - { - // Discord Rich Presence Integration - if (m_gui_settings->GetValue(gui::m_richPresence).toBool()) - { - discord::update_presence(Emu.GetTitleID(), Emu.GetTitle()); - } - }); - connect(this, &gui_application::OnEmulatorStop, [this]() - { - // Discord Rich Presence Integration - if (m_gui_settings->GetValue(gui::m_richPresence).toBool()) - { - discord::update_presence(m_gui_settings->GetValue(gui::m_discordState).toString().toStdString()); - } - }); -#endif - - qRegisterMetaType>("std::function"); - connect(this, &gui_application::RequestCallFromMainThread, this, &gui_application::CallFromMainThread); -} - -std::unique_ptr gui_application::get_gs_frame() -{ - // Load AppIcon - const QIcon app_icon = m_main_window ? m_main_window->GetAppIcon() : gui::utils::get_app_icon_from_path(Emu.GetBoot(), Emu.GetTitleID()); - - if (m_game_window) - { - // Check if the continuous mode is enabled. We reset the mode after each use in order to ensure that it is only used when explicitly needed. - const bool continuous_mode_enabled = Emu.ContinuousModeEnabled(true); - - // Make sure we run the same config - const bool is_same_renderer = m_game_window->renderer() == g_cfg.video.renderer; - - if (is_same_renderer && (Emu.IsChildProcess() || continuous_mode_enabled)) - { - gui_log.notice("gui_application: Re-using old game window (IsChildProcess=%d, ContinuousModeEnabled=%d)", Emu.IsChildProcess(), continuous_mode_enabled); - - if (!app_icon.isNull()) - { - m_game_window->setIcon(app_icon); - } - return std::unique_ptr(m_game_window); - } - - // Clean-up old game window. This should only happen if the renderer changed or there was an unexpected error during boot. - Emu.GetCallbacks().close_gs_frame(); - } - - gui_log.notice("gui_application: Creating new game window"); - - extern const std::unordered_map, value_hash> g_video_out_resolution_map; - - auto [w, h] = ::at32(g_video_out_resolution_map, g_cfg.video.resolution); - - const bool resize_game_window = m_gui_settings->GetValue(gui::gs_resize).toBool(); - - if (resize_game_window) - { - if (m_gui_settings->GetValue(gui::gs_resize_manual).toBool()) - { - w = m_gui_settings->GetValue(gui::gs_width).toInt(); - h = m_gui_settings->GetValue(gui::gs_height).toInt(); - } - else - { - const qreal device_pixel_ratio = devicePixelRatio(); - w /= device_pixel_ratio; - h /= device_pixel_ratio; - } - } - - QScreen* screen = nullptr; - QRect base_geometry{}; - - // Use screen index set by CLI argument - int screen_index = m_game_screen_index; - const int last_screen_index = m_gui_settings->GetValue(gui::gs_screen).toInt(); - - // Use last used screen if no CLI index was set - if (screen_index < 0) - { - screen_index = last_screen_index; - } - - // Try to find the specified screen - if (screen_index >= 0) - { - const QList available_screens = screens(); - - if (screen_index < available_screens.count()) - { - screen = ::at32(available_screens, screen_index); - - if (screen) - { - base_geometry = screen->geometry(); - } - } - - if (!screen) - { - gui_log.error("The selected game screen with index %d is not available (available screens: %d)", screen_index, available_screens.count()); - } - } - - // Fallback to the screen of the main window. Use the primary screen as last resort. - if (!screen) - { - screen = m_main_window ? m_main_window->screen() : primaryScreen(); - base_geometry = m_main_window ? m_main_window->frameGeometry() : primaryScreen()->geometry(); - } - - // Use saved geometry if possible. Ignore this if the last used screen is different than the requested screen. - QRect frame_geometry = screen_index != last_screen_index ? QRect{} : m_gui_settings->GetValue(gui::gs_geometry).value(); - - if (frame_geometry.isNull() || frame_geometry.isEmpty()) - { - // Center above main window or inside screen if the saved geometry is invalid - frame_geometry = gui::utils::create_centered_window_geometry(screen, base_geometry, w, h); - } - else if (resize_game_window) - { - // Apply size override to our saved geometry if needed - frame_geometry.setSize(QSize(w, h)); - } - - gs_frame* frame = nullptr; - - switch (g_cfg.video.renderer.get()) - { - case video_renderer::opengl: - { - frame = new gl_gs_frame(screen, frame_geometry, app_icon, m_gui_settings, m_start_games_fullscreen); - break; - } - case video_renderer::null: - case video_renderer::vulkan: - { - frame = new gs_frame(screen, frame_geometry, app_icon, m_gui_settings, m_start_games_fullscreen); - break; - } - } - - m_game_window = frame; - ensure(m_game_window); - -#ifdef _WIN32 - if (!m_show_gui) - { - register_device_notification(m_game_window->winId()); - } -#endif - - connect(m_game_window, &gs_frame::destroyed, this, [this]() - { - gui_log.notice("gui_application: Deleting old game window"); - m_game_window = nullptr; - -#ifdef _WIN32 - if (!m_show_gui) - { - unregister_device_notification(); - } -#endif - }); - - return std::unique_ptr(frame); -} - -/** RPCS3 emulator has functions it desires to call from the GUI at times. Initialize them in here. */ -void gui_application::InitializeCallbacks() -{ - EmuCallbacks callbacks = CreateCallbacks(); - - callbacks.try_to_quit = [this](bool force_quit, std::function on_exit) -> bool - { - // Close rpcs3 if closed in no-gui mode - if (force_quit || !m_main_window) - { - if (on_exit) - { - on_exit(); - } - - if (m_main_window) - { - // Close main window in order to save its window state - m_main_window->close(); - } - quit(); - return true; - } - - return false; - }; - callbacks.call_from_main_thread = [this](std::function func, atomic_t* wake_up) - { - RequestCallFromMainThread(std::move(func), wake_up); - }; - - callbacks.init_gs_render = [](utils::serial* ar) - { - switch (g_cfg.video.renderer.get()) - { - case video_renderer::null: - { - g_fxo->init>(ar); - break; - } - case video_renderer::opengl: - { -#if not defined(__APPLE__) - g_fxo->init>(ar); -#endif - break; - } - case video_renderer::vulkan: - { -#if defined(HAVE_VULKAN) - g_fxo->init>(ar); -#endif - break; - } - } - }; - - callbacks.get_camera_handler = []() -> std::shared_ptr - { - switch (g_cfg.io.camera.get()) - { - case camera_handler::null: - case camera_handler::fake: - { - return std::make_shared(); - } - case camera_handler::qt: - { - return std::make_shared(); - } - } - return nullptr; - }; - - callbacks.get_music_handler = - []() -> std::shared_ptr - { - switch (g_cfg.audio.music.get()) - { - case music_handler::null: - { - return std::make_shared(); - } - case music_handler::qt: - { - return std::make_shared(); - } - } - return nullptr; - }; - - callbacks.create_pad_handler = [](pad_handler type, void* thread, void* window) -> std::shared_ptr - { - switch (type) - { - case pad_handler::null: - break; - case pad_handler::keyboard: - { - auto result = std::make_shared(); - result->moveToThread(static_cast(thread)); - result->SetTargetWindow(static_cast(window)); - return result; - } - case pad_handler::ds3: - return std::make_shared(); - case pad_handler::ds4: - return std::make_shared(); - case pad_handler::dualsense: - return std::make_shared(); - case pad_handler::skateboard: - return std::make_shared(); - case pad_handler::move: - return std::make_shared(); -#ifdef _WIN32 - case pad_handler::xinput: - return std::make_shared(); - case pad_handler::mm: - return std::make_shared(); -#endif -#ifdef HAVE_SDL3 - case pad_handler::sdl: - return std::make_shared(); -#endif -#ifdef HAVE_LIBEVDEV - case pad_handler::evdev: - return std::make_shared(); -#endif - case pad_handler::virtual_pad: - return std::make_shared(); - } - - return std::make_shared(); - }; - - callbacks.close_gs_frame = [this]() - { - if (m_game_window) - { - gui_log.warning("gui_application: Closing old game window"); - m_game_window->ignore_stop_events(); - delete m_game_window; - m_game_window = nullptr; - } - }; - callbacks.get_gs_frame = [this]() -> std::unique_ptr - { - return get_gs_frame(); - }; - callbacks.get_msg_dialog = [this]() -> std::shared_ptr - { - return m_show_gui ? std::make_shared() : nullptr; - }; - callbacks.get_osk_dialog = [this]() -> std::shared_ptr - { - return m_show_gui ? std::make_shared() : nullptr; - }; - callbacks.get_save_dialog = []() -> std::unique_ptr - { - return std::make_unique(); - }; - callbacks.get_sendmessage_dialog = [this]() -> std::shared_ptr - { - return std::make_shared(); - }; - callbacks.get_recvmessage_dialog = [this]() -> std::shared_ptr - { - return std::make_shared(); - }; - callbacks.get_trophy_notification_dialog = [this]() -> std::unique_ptr - { - return std::make_unique(m_game_window); - }; - - callbacks.on_run = [this](bool start_playtime) - { - OnEmulatorRun(start_playtime); - }; - callbacks.on_pause = [this]() - { - OnEmulatorPause(); - }; - callbacks.on_resume = [this]() - { - OnEmulatorResume(true); - }; - callbacks.on_stop = [this]() - { - OnEmulatorStop(); - }; - callbacks.on_ready = [this]() - { - OnEmulatorReady(); - }; - - callbacks.enable_disc_eject = [this](bool enabled) - { - Emu.CallFromMainThread([this, enabled]() - { - OnEnableDiscEject(enabled); - }); - }; - callbacks.enable_disc_insert = [this](bool enabled) - { - Emu.CallFromMainThread([this, enabled]() - { - OnEnableDiscInsert(enabled); - }); - }; - - callbacks.on_missing_fw = [this]() - { - if (m_main_window) - { - m_main_window->OnMissingFw(); - } - }; - - callbacks.handle_taskbar_progress = [this](s32 type, s32 value) - { - if (m_game_window) - { - switch (type) - { - case 0: m_game_window->progress_reset(value); break; - case 1: m_game_window->progress_increment(value); break; - case 2: m_game_window->progress_set_limit(value); break; - case 3: m_game_window->progress_set_value(value); break; - default: gui_log.fatal("Unknown type in handle_taskbar_progress(type=%d, value=%d)", type, value); break; - } - } - }; - - callbacks.get_localized_string = [](localized_string_id id, const char* args) -> std::string - { - return localized_emu::get_string(id, args); - }; - - callbacks.get_localized_u32string = [](localized_string_id id, const char* args) -> std::u32string - { - return localized_emu::get_u32string(id, args); - }; - - callbacks.get_localized_setting = [this](const cfg::_base* node, u32 enum_index) -> std::string - { - ensure(!!m_emu_settings); - return m_emu_settings->GetLocalizedSetting(node, enum_index); - }; - - callbacks.play_sound = [this](const std::string& path) - { - Emu.CallFromMainThread([this, path]() - { - if (fs::is_file(path)) - { - // Allow to play 3 sound effects at the same time - while (m_sound_effects.size() >= 3) - { - m_sound_effects.pop_front(); - } - - // Create a new sound effect. Re-using the same object seems to be broken for some users starting with Qt 6.6.3. - std::unique_ptr sound_effect = std::make_unique(); - sound_effect->setSource(QUrl::fromLocalFile(QString::fromStdString(path))); - sound_effect->setVolume(audio::get_volume()); - sound_effect->play(); - - m_sound_effects.push_back(std::move(sound_effect)); - } - }); - }; - - if (m_show_gui) // If this is false, we already have a fallback in the main_application. - { - callbacks.on_install_pkgs = [this](const std::vector& pkgs) - { - ensure(m_main_window); - ensure(!pkgs.empty()); - QStringList pkg_list; - for (const std::string& pkg : pkgs) - { - pkg_list << QString::fromStdString(pkg); - } - return m_main_window->InstallPackages(pkg_list, true); - }; - } - - callbacks.on_emulation_stop_no_response = [this](std::shared_ptr> closed_successfully, int seconds_waiting_already) - { - const std::string terminate_message = tr("Stopping emulator took too long." - "\nSome thread has probably deadlocked. Aborting.") - .toStdString(); - - if (!closed_successfully) - { - report_fatal_error(terminate_message); - } - - Emu.CallFromMainThread([this, closed_successfully, seconds_waiting_already, terminate_message] - { - const auto seconds = std::make_shared(seconds_waiting_already); - - QMessageBox* mb = new QMessageBox(); - mb->setWindowTitle(tr("PS3 Game/Application Is Unresponsive")); - mb->setIcon(QMessageBox::Critical); - mb->setStandardButtons(QMessageBox::Yes | QMessageBox::No); - mb->setDefaultButton(QMessageBox::No); - mb->button(QMessageBox::Yes)->setText(tr("Terminate RPCS3")); - mb->button(QMessageBox::No)->setText(tr("Keep Waiting")); - - QString text_base = tr("Waiting for %0 second(s) already to stop emulation without success." - "\nKeep waiting or terminate RPCS3 unsafely at your own risk?"); - - mb->setText(text_base.arg(10)); - mb->layout()->setSizeConstraint(QLayout::SetFixedSize); - mb->setAttribute(Qt::WA_DeleteOnClose); - - QTimer* update_timer = new QTimer(mb); - - connect(update_timer, &QTimer::timeout, [mb, seconds, text_base, closed_successfully]() - { - *seconds += 1; - mb->setText(text_base.arg(*seconds)); - - if (*closed_successfully) - { - mb->reject(); - } - }); - - connect(mb, &QDialog::accepted, mb, [closed_successfully, terminate_message] - { - if (!*closed_successfully) - { - report_fatal_error(terminate_message); - } - }); - - mb->open(); - update_timer->start(1000); - }); - }; - - callbacks.on_save_state_progress = [this](std::shared_ptr> closed_successfully, stx::shared_ptr ar_ptr, stx::atomic_ptr* code_location, std::shared_ptr init_mtx) - { - Emu.CallFromMainThread([this, closed_successfully, ar_ptr, code_location, init_mtx] - { - const auto half_seconds = std::make_shared(1); - - progress_dialog* pdlg = new progress_dialog(tr("Creating Save-State / Do Not Close RPCS3"), tr("Please wait..."), tr("Hide Progress"), 0, 100, true, m_main_window); - pdlg->setAutoReset(false); - pdlg->setAutoClose(true); - pdlg->show(); - - QString text_base = tr("%0 written, %1 second(s) passed%2"); - - pdlg->setLabelText(text_base.arg("0B").arg(1).arg("")); - pdlg->setAttribute(Qt::WA_DeleteOnClose); - - QTimer* update_timer = new QTimer(pdlg); - - connect(update_timer, &QTimer::timeout, [pdlg, ar_ptr, half_seconds, text_base, closed_successfully, code_location, init_mtx, old_written = usz{0}, repeat_count = u32{0}]() mutable - { - std::string verbose_message; - usz bytes_written = 0; - - while (true) - { - auto mtx = static_cast(init_mtx.get()); - auto init = mtx->access(); - - if (!init) - { - // Try to wait for the abort process to complete - auto fake_reset = mtx->reset(); - if (!fake_reset) - { - // End of emulation termination - pdlg->reject(); - return; - } - - fake_reset.set_init(); - - // Now ar_ptr contains a null file descriptor - continue; - } - - if (auto str_ptr = code_location->load()) - { - verbose_message = "\n" + *str_ptr; - } - - bytes_written = ar_ptr->is_writing() ? std::max(ar_ptr->get_size(), old_written) : old_written; - break; - } - - *half_seconds += 1; - - if (old_written == bytes_written) - { - if (repeat_count == 60) - { - if (verbose_message.empty()) - { - verbose_message += "\n"; - } - else - { - verbose_message += ". "; - } - - verbose_message += tr("If Stuck, Report To Developers").toStdString(); - } - else - { - repeat_count++; - } - } - else - { - repeat_count = 0; - } - - old_written = bytes_written; - - pdlg->setLabelText(text_base.arg(gui::utils::format_byte_size(bytes_written)).arg(*half_seconds / 2).arg(QString::fromStdString(verbose_message))); - - // 300MB -> 50%, 600MB -> 75%, 1200MB -> 87.5% etc - const int percent = std::clamp(static_cast(100. - 100. / std::pow(2., std::fmax(0.01, bytes_written * 1. / (300 * 1024 * 1024)))), 2, 100); - - // Add a third of the remaining progress when the keyword is found - pdlg->setValue(verbose_message.find("Finalizing") != umax ? 100 - ((100 - percent) * 2 / 3) : percent); - - if (*closed_successfully) - { - pdlg->reject(); - } - }); - - pdlg->open(); - update_timer->start(500); - }); - }; - - callbacks.add_breakpoint = [this](u32 addr) - { - Emu.BlockingCallFromMainThread([this, addr]() - { - m_main_window->OnAddBreakpoint(addr); - }); - }; - - callbacks.display_sleep_control_supported = []() - { - return display_sleep_control_supported(); - }; - callbacks.enable_display_sleep = [](bool enabled) - { - enable_display_sleep(enabled); - }; - - callbacks.check_microphone_permissions = []() - { - Emu.BlockingCallFromMainThread([]() - { - gui::utils::check_microphone_permission(); - }); - }; - - callbacks.make_video_source = []() - { - return std::make_unique(); - }; - - Emu.SetCallbacks(std::move(callbacks)); -} - -void gui_application::StartPlaytime(bool start_playtime = true) -{ - if (!start_playtime) - { - return; - } - - const QString serial = QString::fromStdString(Emu.GetTitleID()); - if (serial.isEmpty()) - { - return; - } - - m_persistent_settings->SetLastPlayed(serial, QDateTime::currentDateTime().toString(gui::persistent::last_played_date_format), true); - m_timer_playtime.start(); - m_timer.start(10000); // Update every 10 seconds in case the emulation crashes -} - -void gui_application::UpdatePlaytime() -{ - if (!m_timer_playtime.isValid()) - { - m_timer.stop(); - return; - } - - const QString serial = QString::fromStdString(Emu.GetTitleID()); - if (serial.isEmpty()) - { - m_timer_playtime.invalidate(); - m_timer.stop(); - return; - } - - m_persistent_settings->AddPlaytime(serial, m_timer_playtime.restart(), false); - m_persistent_settings->SetLastPlayed(serial, QDateTime::currentDateTime().toString(gui::persistent::last_played_date_format), true); -} - -void gui_application::StopPlaytime() -{ - m_timer.stop(); - - if (!m_timer_playtime.isValid()) - return; - - const QString serial = QString::fromStdString(Emu.GetTitleID()); - if (serial.isEmpty()) - { - m_timer_playtime.invalidate(); - return; - } - - m_persistent_settings->AddPlaytime(serial, m_timer_playtime.restart(), false); - m_persistent_settings->SetLastPlayed(serial, QDateTime::currentDateTime().toString(gui::persistent::last_played_date_format), true); - m_timer_playtime.invalidate(); -} - -/* - * Handle a request to change the stylesheet based on the current entry in the settings. - */ -void gui_application::OnChangeStyleSheetRequest() -{ - // skip stylesheets on first repaint if a style was set from command line - if (m_use_cli_style && gui::stylesheet.isEmpty()) - { - gui::stylesheet = styleSheet().isEmpty() ? "/* style set by command line arg */" : styleSheet(); - - if (m_main_window) - { - m_main_window->RepaintGui(); - } - - return; - } - - // Remove old fonts - QFontDatabase::removeAllApplicationFonts(); - - const QString stylesheet_name = m_gui_settings->GetValue(gui::m_currentStylesheet).toString(); - - // Determine default style - if (m_default_style.isEmpty()) - { -#ifdef _WIN32 - // On windows, the custom stylesheets don't seem to work properly unless we use the windowsvista style as default - if (QStyleFactory::keys().contains("windowsvista")) - { - m_default_style = "windowsvista"; - gui_log.notice("Using '%s' as default style", m_default_style); - } -#endif - - // Use the initial style as default style - if (const QStyle* style = m_default_style.isEmpty() ? QApplication::style() : nullptr) - { - m_default_style = style->name(); - gui_log.notice("Determined '%s' as default style", m_default_style); - } - - // Fallback to the first style, which is supposed to be the default style according to the Qt docs. - if (m_default_style.isEmpty()) - { - if (const QStringList styles = QStyleFactory::keys(); !styles.empty()) - { - m_default_style = styles.front(); - gui_log.notice("Determined '%s' as default style (first style available)", m_default_style); - } - } - } - - // Reset style to default before doing anything else, or we will get unexpected effects in custom stylesheets. - if (QStyle* style = QStyleFactory::create(m_default_style)) - { - setStyle(style); - } - - const auto match_native_style = [&stylesheet_name]() -> QString - { - // Search for "native (