Compare commits

...

58 Commits

Author SHA1 Message Date
Rosalie Wanders 01ebb8ec8d Package: v0.8.8 2025-11-16 21:43:23 +01:00
Rosalie Wanders 3a2c14a7e7 RMG: improve refresh button state in NetplaySessionBrowserDialog 2025-11-16 17:42:02 +01:00
Rosalie Wanders 0ecf762d30 3rdParty: apply zero init variable patch to mupen64plus-video-GLideN64 2025-11-16 17:03:46 +01:00
Rosalie Wanders 13ab10c262 3rdParty: don't enable asserts when using RelWithDebInfo 2025-11-11 11:55:11 +01:00
Rosalie Wanders 87babd2c41 RMG-Input: allow middle widget in AlignCenterLayout to have 40% of width 2025-11-07 15:44:43 +01:00
Rosalie Wanders ce19f1cc2c RMG-Input: autoConfigButton -> autoConfigureButton 2025-11-07 15:41:01 +01:00
Rosalie Wanders 7985875d7c RMG-Input: track state of children in {disable,enable}AllChildren() 2025-11-07 15:33:57 +01:00
Rosalie Wanders a66fe3bdce RMG: add confirmation checkbox to drag & drop confirmation dialog 2025-11-07 15:21:53 +01:00
Rosalie Wanders 768c195f12 RMG: move 'Ask for confirmation on exit' setting to the emulation tab 2025-11-07 15:21:24 +01:00
Rosalie Wanders c45873464c RMG: improve emulation shutdown handling in MainWindow::closeEvent() 2025-11-07 14:55:53 +01:00
Rosalie Wanders 8b869c63c6 RMG: don't show exit confirmation dialog when launched with --nogui 2025-11-07 14:55:27 +01:00
Rosalie Wanders 1b2215cc02 Package: v0.8.7 2025-11-06 21:15:45 +01:00
Rosalie Wanders 3290e83966 RMG: add 'Game Type' to the RomInfoDialog
This also removes the Unknown fallback for 64DD disks,
there was never a PAL 64DD so the NTSC value was correct which I forgot
2025-11-06 21:09:10 +01:00
Rosalie Wanders da0a44d26a RMG: set region of 64DD disks to Unknown in RomInfoDialog 2025-11-06 21:00:11 +01:00
Rosalie Wanders b2716715ec RMG-Input: add custom layout for top row of UI elements and add tooltips 2025-11-06 20:32:33 +01:00
Rosalie Wanders 1547fb6617 RMG-Input: save and restore state of 'Auto-Configure' button 2025-11-06 15:53:59 +01:00
Rosalie Wanders b19771525e RMG: fix CheatsCommon::SetCheatOption() using CoreGetCheatOption() 2025-11-06 15:22:18 +01:00
Rosalie Wanders 697241b9b7 RMG-Core: reset m64p_CheatCodes in CoreApply{,Netplay}Cheats() 2025-11-06 15:22:18 +01:00
Rosalie Wanders 6f6eb62256 RMG-Core: use english for win32 error messages in Library.cpp 2025-10-26 23:46:00 +01:00
Rosalie Wanders 5d57d16e2e RMG-Input: show native a/b/x/y button names in HotKeysDialog too 2025-10-24 23:01:01 +02:00
Rosalie Wanders dd10314a13 ci: use windows-2025 2025-10-20 21:03:13 +02:00
Rosalie Wanders 05c49ca00e RMG: fix drag & drop being broken in the grid view after last commit 2025-10-20 21:02:54 +02:00
Rosalie Wanders 2aa4828f6f RMG: add support for drag & drop with KCoreAddons
This allows the flatpak to support drag & drop, this is due to Qt6
still not supporting the file portal during drag & drop.

https://bugreports.qt.io/browse/QTBUG-91357
2025-10-20 12:42:25 +02:00
Rosalie Wanders e5b8f30ef4 Package: add libatomic to build dependencies in RMG.spec 2025-10-19 17:41:01 +02:00
Rosalie Wanders e2407e5886 Package: add libatomic to dependencies in RMG.spec 2025-10-19 17:32:12 +02:00
Rosalie Wanders 7b4b7a80ae Package: v0.8.6 2025-10-19 17:14:09 +02:00
Rosalie Wanders 5ccf15b852 RMG-Input: fix handling InputProfileDB with automatic device 2025-10-19 16:51:21 +02:00
Rosalie Wanders c991721faf RMG-Input: rename SetIsCurrentJoystickGameController() 2025-10-19 16:10:32 +02:00
Rosalie Wanders 2679fd41d5 RMG-Input: add 'Auto-Configure' feature 2025-10-19 15:56:15 +02:00
Rosalie Wanders dc10430315 RMG: add magic-line icon 2025-10-19 15:50:50 +02:00
Rosalie Wanders cbe455c640 Data: add InputProfileDB.json 2025-10-19 15:50:50 +02:00
Rosalie Wanders 9662f7095a Script: add GenerateInputProfileDbEntry.sh 2025-10-19 12:21:01 +02:00
Rosalie Wanders de77034fa4 3rdParty: update mupen64plus-core 2025-10-19 12:20:49 +02:00
Rosalie Wanders a955e7b272 RMG-Input: controller -> gamepad 2025-10-19 12:16:49 +02:00
Rosalie Wanders 6ade361df3 3rdParty: add bounds checking to cart_rom.c in mupen64plus-core 2025-10-19 12:04:59 +02:00
Rosalie Wanders aba3676b1e RMG-Input: use native gamepad button names for a/b/x/y 2025-10-18 01:23:50 +02:00
Rosalie Wanders 2927d86d49 RMG-Input: use c++ style casts in VRU.cpp 2025-10-17 21:38:59 +02:00
Rosalie Wanders 2af46a43a6 RMG-Input: only capture required variables in signal lambdas 2025-10-17 21:34:32 +02:00
Rosalie Wanders 30121f2db7 RMG-Input-GCA: implement 'Swap Z and L' setting 2025-10-17 19:36:34 +02:00
Rosalie Wanders 72d13568fb RMG-Core: add GCAInput_SwapZL setting 2025-10-17 19:36:34 +02:00
Rosalie Wanders a6d9187e56 RMG-Input-GCA: poll input on different thread 2025-10-17 19:36:34 +02:00
Rosalie Wanders 939db73b0b RMG: capture value in signal lambdas instead of extracting from text 2025-10-16 21:30:27 +02:00
Rosalie Wanders 7154725520 RMG: add and use NetplayCommon::GetNetworkRequest() 2025-10-16 21:29:28 +02:00
Rosalie Wanders b6ab15de37 RMG: use c++ style casts 2025-10-16 21:28:46 +02:00
Rosalie Wanders a38866563b RMG-Input: use c++ style casts 2025-10-16 21:27:43 +02:00
Rosalie Wanders 66f5962494 RMG: add support for both PIF file variants that mupen64plus-core supports 2025-10-16 00:16:20 +02:00
Rosalie Wanders 471cce3fdb RMG: use currently set path as starting path for directory dialogs 2025-10-15 13:38:17 +02:00
Rosalie Wanders 4276491a37 3rdParty: add SiDmaDuration to Pokemon Stadium in mupen64plus-core 2025-10-14 23:30:49 +02:00
Rosalie Wanders 928a9427a2 RMG-Input: use SDL_GetGamepadJoystick() for gamepads 2025-10-11 23:41:23 +02:00
Rosalie Wanders 903d44f73c RMG: update AboutDialog.ui 2025-10-11 22:13:50 +02:00
Rosalie Wanders d577dd4194 RMG-Core: remove user directories override settings 2025-10-11 21:43:14 +02:00
Rosalie Wanders 5732775246 RMG: remove option to override user directories in the settings dialog 2025-10-11 21:39:42 +02:00
Rosalie Wanders 0b93d54643 RMG-Input: add icons to buttons in options dialog 2025-10-11 21:26:05 +02:00
Rosalie Wanders 604b50eb7d RMG: add icons to buttons in settings dialog 2025-10-11 21:25:41 +02:00
Rosalie Wanders 5e6ac33048 RMG: add icons to context menu of the ROM browser 2025-10-11 21:05:15 +02:00
Rosalie Wanders 470c94df9f RMG-Input: use QString::left() instead of QString::slice()
I didn't realize QString::slice() was Qt 6.8+
2025-10-11 20:20:50 +02:00
Rosalie Wanders 5caf80a43d RMG-Input: fallback to partial MD5 hash when there's no internal name 2025-10-11 19:47:39 +02:00
Rosalie Wanders 1f82d47c09 RMG: add icons to buttons in CheatsDialog 2025-10-11 19:32:00 +02:00
82 changed files with 1642 additions and 629 deletions
+1 -1
View File
@@ -97,7 +97,7 @@ jobs:
path: Bin/*.AppImage
build-windows:
runs-on: windows-2022
runs-on: windows-2025
strategy:
matrix:
features: [ ON, OFF ]
+2 -2
View File
@@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.15)
option(PORTABLE_INSTALL "Portable Installation" ON)
option(UPDATER "Enables updater" ${WIN32})
option(APPIMAGE_UPDATER "Enables AppImage updater" OFF)
option(DRAG_DROP "Enables drag and drop" ON)
option(KCA_DRAG_DROP "Enables drag and drop using KCoreAddons" OFF) # https://bugreports.qt.io/browse/QTBUG-91357
option(NETPLAY "Enables netplay" ON)
option(VRU "Enables VRU support in RMG-Input" ON)
option(USE_CCACHE "Enables usage of ccache when ccache has been found" ON)
@@ -156,7 +156,7 @@ if (VRU)
DESTINATION ${DATA_INSTALL_PATH}
)
endif(VRU)
install(FILES ${SDL_GAMECONTROLLERDB}
install(FILES ${SDL_GAMECONTROLLERDB} ${CMAKE_SOURCE_DIR}/Data/InputProfileDB.json
DESTINATION ${DATA_INSTALL_PATH}
)
file(GLOB GENERATED_CHEAT_FILES ${CMAKE_SOURCE_DIR}/Data/Cheats/Generated/*.cht)
+230
View File
@@ -0,0 +1,230 @@
[
{
"DeviceName": [ "fallback_profile" ],
"DeviceType": 4,
"A_InputType": "0",
"A_Name": "a",
"A_Data": "0",
"A_ExtraData": "0",
"B_InputType": "0",
"B_Name": "x",
"B_Data": "2",
"B_ExtraData": "0",
"Start_InputType": "0",
"Start_Name": "start",
"Start_Data": "6",
"Start_ExtraData": "0",
"DpadUp_InputType": "0",
"DpadUp_Name": "dpup",
"DpadUp_Data": "11",
"DpadUp_ExtraData": "0",
"DpadDown_InputType": "0",
"DpadDown_Name": "dpdown",
"DpadDown_Data": "12",
"DpadDown_ExtraData": "0",
"DpadLeft_InputType": "0",
"DpadLeft_Name": "dpleft",
"DpadLeft_Data": "13",
"DpadLeft_ExtraData": "0",
"DpadRight_InputType": "0",
"DpadRight_Name": "dpright",
"DpadRight_Data": "14",
"DpadRight_ExtraData": "0",
"CButtonUp_InputType": "1",
"CButtonUp_Name": "righty-",
"CButtonUp_Data": "3",
"CButtonUp_ExtraData": "0",
"CButtonDown_InputType": "1",
"CButtonDown_Name": "righty+",
"CButtonDown_Data": "3",
"CButtonDown_ExtraData": "1",
"CButtonLeft_InputType": "1",
"CButtonLeft_Name": "rightx-",
"CButtonLeft_Data": "2",
"CButtonLeft_ExtraData": "0",
"CButtonRight_InputType": "1",
"CButtonRight_Name": "rightx+",
"CButtonRight_Data": "2",
"CButtonRight_ExtraData": "1",
"LeftTrigger_InputType": "0",
"LeftTrigger_Name": "leftshoulder",
"LeftTrigger_Data": "9",
"LeftTrigger_ExtraData": "0",
"RightTrigger_InputType": "0",
"RightTrigger_Name": "rightshoulder",
"RightTrigger_Data": "10",
"RightTrigger_ExtraData": "0",
"ZTrigger_InputType": "1",
"ZTrigger_Name": "lefttrigger+",
"ZTrigger_Data": "4",
"ZTrigger_ExtraData": "1",
"AnalogStickUp_InputType": "1",
"AnalogStickUp_Name": "lefty-",
"AnalogStickUp_Data": "1",
"AnalogStickUp_ExtraData": "0",
"AnalogStickDown_InputType": "1",
"AnalogStickDown_Name": "lefty+",
"AnalogStickDown_Data": "1",
"AnalogStickDown_ExtraData": "1",
"AnalogStickLeft_InputType": "1",
"AnalogStickLeft_Name": "leftx-",
"AnalogStickLeft_Data": "0",
"AnalogStickLeft_ExtraData": "0",
"AnalogStickRight_InputType": "1",
"AnalogStickRight_Name": "leftx+",
"AnalogStickRight_Data": "0",
"AnalogStickRight_ExtraData": "1"
},
{
"DeviceName": [ "Nintendo N64 Controller" ],
"DeviceType": 4,
"A_InputType": "0",
"A_Name": "a",
"A_Data": "0",
"A_ExtraData": "0",
"B_InputType": "0",
"B_Name": "b",
"B_Data": "1",
"B_ExtraData": "0",
"Start_InputType": "0",
"Start_Name": "start",
"Start_Data": "6",
"Start_ExtraData": "0",
"DpadUp_InputType": "0",
"DpadUp_Name": "dpup",
"DpadUp_Data": "11",
"DpadUp_ExtraData": "0",
"DpadDown_InputType": "0",
"DpadDown_Name": "dpdown",
"DpadDown_Data": "12",
"DpadDown_ExtraData": "0",
"DpadLeft_InputType": "0",
"DpadLeft_Name": "dpleft",
"DpadLeft_Data": "13",
"DpadLeft_ExtraData": "0",
"DpadRight_InputType": "0",
"DpadRight_Name": "dpright",
"DpadRight_Data": "14",
"DpadRight_ExtraData": "0",
"CButtonUp_InputType": "0",
"CButtonUp_Name": "y",
"CButtonUp_Data": "3",
"CButtonUp_ExtraData": "0",
"CButtonDown_InputType": "1",
"CButtonDown_Name": "righttrigger+",
"CButtonDown_Data": "5",
"CButtonDown_ExtraData": "1",
"CButtonLeft_InputType": "0",
"CButtonLeft_Name": "x",
"CButtonLeft_Data": "2",
"CButtonLeft_ExtraData": "0",
"CButtonRight_InputType": "0",
"CButtonRight_Name": "back",
"CButtonRight_Data": "4",
"CButtonRight_ExtraData": "0",
"LeftTrigger_InputType": "0",
"LeftTrigger_Name": "leftshoulder",
"LeftTrigger_Data": "9",
"LeftTrigger_ExtraData": "0",
"RightTrigger_InputType": "0",
"RightTrigger_Name": "rightshoulder",
"RightTrigger_Data": "10",
"RightTrigger_ExtraData": "0",
"ZTrigger_InputType": "1",
"ZTrigger_Name": "lefttrigger+",
"ZTrigger_Data": "4",
"ZTrigger_ExtraData": "1",
"AnalogStickUp_InputType": "1",
"AnalogStickUp_Name": "lefty-",
"AnalogStickUp_Data": "1",
"AnalogStickUp_ExtraData": "0",
"AnalogStickDown_InputType": "1",
"AnalogStickDown_Name": "lefty+",
"AnalogStickDown_Data": "1",
"AnalogStickDown_ExtraData": "1",
"AnalogStickLeft_InputType": "1",
"AnalogStickLeft_Name": "leftx-",
"AnalogStickLeft_Data": "0",
"AnalogStickLeft_ExtraData": "0",
"AnalogStickRight_InputType": "1",
"AnalogStickRight_Name": "leftx+",
"AnalogStickRight_Data": "0",
"AnalogStickRight_ExtraData": "1"
},
{
"DeviceName": [ "Nintendo GameCube Controller" ],
"DeviceType": 4,
"A_InputType": "0",
"A_Name": "a",
"A_Data": "0",
"A_ExtraData": "0",
"B_InputType": "0",
"B_Name": "b",
"B_Data": "1",
"B_ExtraData": "0",
"Start_InputType": "0",
"Start_Name": "start",
"Start_Data": "6",
"Start_ExtraData": "0",
"DpadUp_InputType": "0",
"DpadUp_Name": "dpup",
"DpadUp_Data": "11",
"DpadUp_ExtraData": "0",
"DpadDown_InputType": "0",
"DpadDown_Name": "dpdown",
"DpadDown_Data": "12",
"DpadDown_ExtraData": "0",
"DpadLeft_InputType": "0",
"DpadLeft_Name": "dpleft",
"DpadLeft_Data": "13",
"DpadLeft_ExtraData": "0",
"DpadRight_InputType": "0",
"DpadRight_Name": "dpright",
"DpadRight_Data": "14",
"DpadRight_ExtraData": "0",
"CButtonUp_InputType": "1;0",
"CButtonUp_Name": "righty-;y",
"CButtonUp_Data": "3;3",
"CButtonUp_ExtraData": "0;0",
"CButtonDown_InputType": "1",
"CButtonDown_Name": "righty+",
"CButtonDown_Data": "3",
"CButtonDown_ExtraData": "1",
"CButtonLeft_InputType": "1",
"CButtonLeft_Name": "rightx-",
"CButtonLeft_Data": "2",
"CButtonLeft_ExtraData": "0",
"CButtonRight_InputType": "1;0",
"CButtonRight_Name": "rightx+;x",
"CButtonRight_Data": "2;2",
"CButtonRight_ExtraData": "1;0",
"LeftTrigger_InputType": "0",
"LeftTrigger_Name": "rightshoulder",
"LeftTrigger_Data": "10",
"LeftTrigger_ExtraData": "0",
"RightTrigger_InputType": "1",
"RightTrigger_Name": "righttrigger+",
"RightTrigger_Data": "5",
"RightTrigger_ExtraData": "1",
"ZTrigger_InputType": "1",
"ZTrigger_Name": "lefttrigger+",
"ZTrigger_Data": "4",
"ZTrigger_ExtraData": "1",
"AnalogStickUp_InputType": "1",
"AnalogStickUp_Name": "lefty-",
"AnalogStickUp_Data": "1",
"AnalogStickUp_ExtraData": "0",
"AnalogStickDown_InputType": "1",
"AnalogStickDown_Name": "lefty+",
"AnalogStickDown_Data": "1",
"AnalogStickDown_ExtraData": "1",
"AnalogStickLeft_InputType": "1",
"AnalogStickLeft_Name": "leftx-",
"AnalogStickLeft_Data": "0",
"AnalogStickLeft_ExtraData": "0",
"AnalogStickRight_InputType": "1",
"AnalogStickRight_Name": "leftx+",
"AnalogStickRight_Data": "0",
"AnalogStickRight_ExtraData": "1"
}
]
+1 -1
View File
@@ -1,6 +1,6 @@
# Maintainer: Rosalie Wanders <rosalie@mailbox.org>
pkgname=rmg
pkgver=0.8.5
pkgver=0.8.8
pkgrel=1
pkgdesc="Rosalie's Mupen GUI"
arch=('x86_64' 'aarch64')
+3 -1
View File
@@ -1,5 +1,5 @@
Name: RMG
Version: 0.8.5
Version: 0.8.8
Release: %autorelease
Summary: Rosalie's Mupen GUI
@@ -26,6 +26,7 @@ BuildRequires: qt6-qtbase-devel
BuildRequires: qt6-qtsvg-devel
BuildRequires: qt6-qtwebsockets-devel
BuildRequires: libxkbcommon-devel
BuildRequires: libatomic
Requires: libusb1
Requires: hidapi
@@ -36,6 +37,7 @@ Requires: speexdsp
Requires: qt6-qtbase
Requires: qt6-qtsvg
Requires: qt6-qtwebsockets
Requires: libatomic
%description
Rosalie's Mupen GUI is a free and open-source mupen64plus front-end written in C++
@@ -29,6 +29,60 @@
</screenshot>
</screenshots>
<releases>
<release version="v0.8.8" date="2025-11-16" type="stable">
<description>
<p>Changes:</p>
<ul>
<li>Fix exit confirmation dialog showing when launching RMG with '--nogui'</li>
<li>Fix emulation not shutting down properly on exit in some cases</li>
<li>Fix remaining issues with the 'Auto-Configure' button state in the configuration dialog of RMG-Input</li>
<li>Fix asserts being enabled when building the RelWithDebInfo configuration</li>
<li>Fix uninitialized variable being read in the configuration dialog of GLideN64</li>
<li>Add checkbox to drag and drop confirmation dialog to prevent future confirmations</li>
<li>Move 'Ask for confirmation on exit' setting to the emulation tab in the settings dialog</li>
<li>Improve 'Input Device' UI element in the configuration GUI of RMG-Input by allowing it to take more width</li>
<li>Improve refresh and join button state in the Netplay Session Browser dialog</li>
</ul>
</description>
</release>
<release version="v0.8.7" date="2025-11-06" type="stable">
<description>
<p>Changes:</p>
<ul>
<li>Fix drag and drop not working in the flatpak</li>
<li>Fix hotkeys dialog in RMG-Input not showing native face button names</li>
<li>Fix cheat dialog in RMG not allowing users to set cheat option since v0.7.2</li>
<li>Fix cheat codes not being cleared before trying to load another cheat in RMG-Core</li>
<li>Fix 'Auto-Configure' button being enabled when it should be disabled after configuring a button</li>
<li>Fix top row of UI elements in the configuration GUI of RMG-Input not being aligned properly</li>
<li>Add tooltips to the top row of UI elements in the configuration GUI of RMG-Input</li>
<li>Add 'Game Type' to the 'ROM Information' dialog</li>
</ul>
</description>
</release>
<release version="v0.8.6" date="2025-10-19" type="stable">
<description>
<p>Changes:</p>
<ul>
<li>Fix game specific profile name being empty for ROMs with no internal ROM name in RMG-Input</li>
<li>Fix GameBoy tower functionality not working in Pokemon Stadium when using a GameBoy save</li>
<li>Fix currently set directory not being the starting directory in directory dialogs</li>
<li>Fix ROM browser refreshing after 'Edit ROM Directory...' when the directory is the same</li>
<li>Fix settings dialog not allowing you to select all supported PIF file variants</li>
<li>Fix slowdown when using RMG-Input-GCA by polling input on a different thread</li>
<li>Fix RMG-Input not using native controller button names for the face buttons in the configuration GUI</li>
<li>Add icons to the cheats dialog</li>
<li>Add icons to the context menu of the ROM browser</li>
<li>Add icons to the buttons of the settings dialog</li>
<li>Add icons to the buttons of the options dialog in RMG-Input</li>
<li>Add 'Swap Z and L' setting to RMG-Input-GCA</li>
<li>Add Auto-Configure feature to RMG-Input</li>
<li>Add more bounds checking to mupen64plus-core</li>
<li>Remove user directories override settings</li>
<li>Update mupen64plus-core</li>
</ul>
</description>
</release>
<release version="v0.8.5" date="2025-10-09" type="stable">
<description>
<p>Changes:</p>
+4 -2
View File
@@ -47,8 +47,10 @@ else()
set(MAKE_OPTFLAGS "${CMAKE_C_FLAGS}")
endif()
if ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo" OR
"${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
if ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")
set(MAKE_DEBUG "0")
set(MAKE_OPTFLAGS "${MAKE_OPTFLAGS} -g")
elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
set(MAKE_DEBUG "1")
else()
set(MAKE_DEBUG "0")
+25
View File
@@ -7618,6 +7618,11 @@ Players=4
SaveType=Eeprom 16KB
Rumble=Yes
[49060B34AD97BF8EB741E650130FFAF8]
GoodName=Kirby 64 - The Crystal Shards (widescreen hack)
CRC=EAF8B4FB F12FEA81
RefMD5=D33E4254336383A17FF4728360562ADA
[329C0A564D3B4477E2887097389AB7D6]
GoodName=Kirby 64 - The Crystal Shards (U) [b1]
CRC=80A2BBB5 828DFD3E
@@ -12440,6 +12445,7 @@ CRC=84077275 57315B9C
Players=4
SaveType=Flash RAM
Transferpak=Yes
SiDmaDuration=8192
[9CC940F5F463416F0B2C15F07A088F33]
GoodName=Pokemon Stadium (E) (V1.0) [f1]
@@ -12452,6 +12458,7 @@ CRC=91C9E05D AD3AAFB9
Players=4
SaveType=Flash RAM
Transferpak=Yes
SiDmaDuration=8192
[0E85A098D0F0E8A7EB572A69612A6873]
GoodName=Pokemon Stadium (F) [!]
@@ -12459,6 +12466,7 @@ CRC=A23553A3 42BF2D39
Players=4
SaveType=Flash RAM
Transferpak=Yes
SiDmaDuration=8192
[4688C66F8C13EDA13DD5678176A7FD8A]
GoodName=Pokemon Stadium (F) [f1]
@@ -12471,6 +12479,7 @@ CRC=42011E1B E3552DB5
Players=4
SaveType=Flash RAM
Transferpak=Yes
SiDmaDuration=8192
[8FAA825507E6CD3F388CC33343FAE547]
GoodName=Pokemon Stadium (G) [f1]
@@ -12483,6 +12492,7 @@ CRC=A53FA82D DAE2C15D
Players=4
SaveType=Flash RAM
Transferpak=Yes
SiDmaDuration=8192
[D14A499BC4E324974EAE3E42DEC58625]
GoodName=Pokemon Stadium (S) [!]
@@ -12490,6 +12500,7 @@ CRC=B6E549CE DC8134C0
Players=4
SaveType=Flash RAM
Transferpak=Yes
SiDmaDuration=8192
[8D296A614A47AC99B80D1EDE669E62B3]
GoodName=Pokemon Stadium (S) [f1]
@@ -12502,6 +12513,7 @@ CRC=90F5D9B3 9D0EDCF0
Players=4
SaveType=Flash RAM
Transferpak=Yes
SiDmaDuration=8192
[C9214988B08511D2F44FAC2FAD2EE67A]
GoodName=Pokemon Stadium (U) (V1.0) [f1]
@@ -12514,6 +12526,7 @@ CRC=1A122D43 C17DAF0F
Players=4
SaveType=Flash RAM
Transferpak=Yes
SiDmaDuration=8192
[A7087A96A709E4C662A459B4B4159094]
GoodName=Pokemon Stadium (U) (V1.1) [b1]
@@ -12531,6 +12544,7 @@ CRC=9C8FB2FA 9B84A09B
Players=4
SaveType=Flash RAM
Transferpak=Yes
SiDmaDuration=8192
[B1271DB50D6EF8F6B53CC640C3743E4F]
GoodName=Pokemon Stadium 2 (E) [!]
@@ -12538,6 +12552,7 @@ CRC=2952369C B6E4C3A8
Players=4
SaveType=Flash RAM
Transferpak=Yes
SiDmaDuration=8192
[BB1623BD4751240138DE55E18D3ACA7E]
GoodName=Pokemon Stadium 2 (E) [T+Ita0.05]
@@ -12550,6 +12565,7 @@ CRC=AC5AA5C7 A9B0CDC3
Players=4
SaveType=Flash RAM
Transferpak=Yes
SiDmaDuration=8192
[484F3E696C94980ACF3D7068F9ACC98F]
GoodName=Pokemon Stadium 2 (G) [!]
@@ -12557,6 +12573,7 @@ CRC=439B7E7E C1A1495D
Players=4
SaveType=Flash RAM
Transferpak=Yes
SiDmaDuration=8192
[5A04D7F1AB9C6B080176567AA7168D3A]
GoodName=Pokemon Stadium 2 (I) [!]
@@ -12564,6 +12581,7 @@ CRC=EFCEAF00 22094848
Players=4
SaveType=Flash RAM
Transferpak=Yes
SiDmaDuration=8192
[B26AAFD452C9816E1B7AA0954E75825F]
GoodName=Pokemon Stadium 2 (S) [!]
@@ -12571,6 +12589,7 @@ CRC=D0A1FC5B 2FB8074B
Players=4
SaveType=Flash RAM
Transferpak=Yes
SiDmaDuration=8192
[1561C75D11CEDF356A8DDB1A4A5F9D5D]
GoodName=Pokemon Stadium 2 (U) [!]
@@ -12578,6 +12597,7 @@ CRC=03571182 892FD06D
Players=4
SaveType=Flash RAM
Transferpak=Yes
SiDmaDuration=8192
[BBDC4C4F1C474298189312008A1768C4]
GoodName=Polaris SnoCross (U) [!]
@@ -12949,6 +12969,11 @@ SaveType=Eeprom 16KB
Players=4
Rumble=Yes
[27F1C0E40E2802CE57E5BAAFA958098C]
GoodName=RR64 - Ridge Racer 64 (widescreen hack)
CRC=22CEC23C 76A9234F
RefMD5=990F97D56456FC23E52BD263E709E21E
[8CF63267BA38AB73763354C79C4ADB74]
GoodName=RR64 - Ridge Racer 64 (U) [f1] (PAL)
CRC=4E17004A 259E946C
@@ -63,9 +63,13 @@ void read_cart_rom(void* opaque, uint32_t address, uint32_t* value)
{
*value = cart_rom->last_write;
}
else if (addr < cart_rom->rom_size)
{
*value = *(uint32_t*)(cart_rom->rom + addr);
}
else
{
*value = cart_rom->rom_size == 0 ? 0 : *(uint32_t*)(cart_rom->rom + addr);
*value = 0;
}
}
@@ -197,7 +197,7 @@ void ConfigDialog::_init(bool reInit, bool blockCustomSettings)
ui->overscanPalBottomSpinBox->setValue(config.frameBufferEmulation.overscanPAL.bottom);
QStringList fullscreenModesList, fullscreenRatesList;
int fullscreenMode, fullscreenRate;
int fullscreenMode = 0, fullscreenRate = 0;
fillFullscreenResolutionsList(fullscreenModesList, fullscreenMode, fullscreenRatesList, fullscreenRate);
#ifdef M64P_GLIDENUI
if (fullscreenModesList.isEmpty() && fullscreenRatesList.isEmpty()) {
+2
View File
@@ -955,6 +955,7 @@ CORE_EXPORT bool CoreApplyCheats(void)
for (const CoreCheat& cheat : cheats)
{
m64p_cheatCodes.clear();
skipCheat = false;
if (!CoreIsCheatEnabled("", cheat))
@@ -1072,6 +1073,7 @@ CORE_EXPORT bool CoreApplyNetplayCheats(void)
for (const CoreCheat& cheat : l_NetplayCheats)
{
m64p_cheatCodes.clear();
skipCheat = false;
for (const CoreCheatCode& code : cheat.CheatCodes)
+2 -12
View File
@@ -55,18 +55,8 @@ static bool config_override_user_dirs(void)
{
std::string error;
m64p_error ret;
std::string dataDir;
std::string cacheDir;
bool overrideUserDirs;
dataDir = CoreSettingsGetStringValue(SettingsID::Core_UserDataDirOverride);
cacheDir = CoreSettingsGetStringValue(SettingsID::Core_UserCacheDirOverride);
overrideUserDirs = CoreSettingsGetBoolValue(SettingsID::Core_OverrideUserDirs);
if (!overrideUserDirs)
{
return true;
}
const std::string dataDir = CoreGetUserDataDirectory().string();
const std::string cacheDir = CoreGetUserCacheDirectory().string();
ret = m64p::Config.OverrideUserPaths(dataDir.c_str(), cacheDir.c_str());
if (ret != M64ERR_SUCCESS)
+31 -51
View File
@@ -296,47 +296,6 @@ CORE_EXPORT std::filesystem::path CoreGetUserConfigDirectory(void)
return directory.make_preferred();
}
CORE_EXPORT std::filesystem::path CoreGetDefaultUserDataDirectory(void)
{
std::filesystem::path directory;
#ifdef PORTABLE_INSTALL
if (CoreGetPortableDirectoryMode())
{
directory = "Data";
}
else
#endif // PORTABLE_INSTALL
{
#ifdef _WIN32
directory = get_appdata_directory("Data");
#else
directory = get_var_directory("XDG_DATA_HOME", "/RMG", "HOME", "/.local/share/RMG");
#endif // _WIN32
}
return directory.make_preferred();
}
CORE_EXPORT std::filesystem::path CoreGetDefaultUserCacheDirectory(void)
{
std::filesystem::path directory;
#ifdef PORTABLE_INSTALL
if (CoreGetPortableDirectoryMode())
{
directory = "Cache";
}
else
#endif // PORTABLE_INSTALL
{
#ifdef _WIN32
directory = get_appdata_directory("Cache");
#else
directory = get_var_directory("XDG_CACHE_HOME", "/RMG", "HOME", "/.cache/RMG");
#endif // _WIN32
}
return directory.make_preferred();
}
CORE_EXPORT std::filesystem::path CoreGetDefaultSaveDirectory(void)
{
std::filesystem::path directory;
@@ -352,7 +311,7 @@ CORE_EXPORT std::filesystem::path CoreGetDefaultSaveDirectory(void)
directory = get_appdata_directory("Save/Game");
#else
directory = CoreGetDefaultUserDataDirectory();
directory = CoreGetUserDataDirectory();
directory += "/Save/Game";
#endif // _WIN32
}
@@ -374,7 +333,7 @@ CORE_EXPORT std::filesystem::path CoreGetDefaultSaveStateDirectory(void)
directory = get_appdata_directory("Save/State");
#else
directory = CoreGetDefaultUserDataDirectory();
directory = CoreGetUserDataDirectory();
directory += "/Save/State";
#endif // _WIN32
}
@@ -396,7 +355,7 @@ CORE_EXPORT std::filesystem::path CoreGetDefaultScreenshotDirectory(void)
directory = get_appdata_directory("Screenshots");
#else
directory = CoreGetDefaultUserDataDirectory();
directory = CoreGetUserDataDirectory();
directory += "/Screenshots";
#endif // _WIN32
}
@@ -405,22 +364,43 @@ CORE_EXPORT std::filesystem::path CoreGetDefaultScreenshotDirectory(void)
CORE_EXPORT std::filesystem::path CoreGetUserDataDirectory(void)
{
if (!m64p::Config.IsHooked())
std::filesystem::path directory;
#ifdef PORTABLE_INSTALL
if (CoreGetPortableDirectoryMode())
{
return std::filesystem::path();
directory = "Data";
}
return std::filesystem::path(m64p::Config.GetUserDataPath());
else
#endif // PORTABLE_INSTALL
{
#ifdef _WIN32
directory = get_appdata_directory("Data");
#else
directory = get_var_directory("XDG_DATA_HOME", "/RMG", "HOME", "/.local/share/RMG");
#endif // _WIN32
}
return directory.make_preferred();
}
CORE_EXPORT std::filesystem::path CoreGetUserCacheDirectory(void)
{
if (!m64p::Config.IsHooked())
std::filesystem::path directory;
#ifdef PORTABLE_INSTALL
if (CoreGetPortableDirectoryMode())
{
return std::filesystem::path();
directory = "Cache";
}
else
#endif // PORTABLE_INSTALL
{
#ifdef _WIN32
directory = get_appdata_directory("Cache");
return std::filesystem::path(m64p::Config.GetUserCachePath());
#else
directory = get_var_directory("XDG_CACHE_HOME", "/RMG", "HOME", "/.cache/RMG");
#endif // _WIN32
}
return directory.make_preferred();
}
CORE_EXPORT std::filesystem::path CoreGetSharedDataDirectory(void)
-8
View File
@@ -40,14 +40,6 @@ std::filesystem::path CoreGetLibraryDirectory(void);
// plugins should be located
std::filesystem::path CoreGetPluginDirectory(void);
// returns the default user data directory where
// data files should be located
std::filesystem::path CoreGetDefaultUserDataDirectory(void);
// returns the default user cache directory where
// cache files should be located
std::filesystem::path CoreGetDefaultUserCacheDirectory(void);
// returns the default save directory where
// game saves should be located
std::filesystem::path CoreGetDefaultSaveDirectory(void);
+1 -1
View File
@@ -52,7 +52,7 @@ CORE_EXPORT std::string CoreGetLibraryError(void)
size_t size =
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buf, 0, nullptr);
nullptr, err, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (LPSTR)&buf, 0, nullptr);
std::string msg(buf, size);
+3 -10
View File
@@ -214,16 +214,6 @@ static l_Setting get_setting(SettingsID settingId)
};
break;
case SettingsID::Core_OverrideUserDirs:
setting = {SETTING_SECTION_CORE, "OverrideUserDirectories", true};
break;
case SettingsID::Core_UserDataDirOverride:
setting = {SETTING_SECTION_CORE, "UserDataDirectory", CoreGetDefaultUserDataDirectory().string()};
break;
case SettingsID::Core_UserCacheDirOverride:
setting = {SETTING_SECTION_CORE, "UserCacheDirectory", CoreGetDefaultUserCacheDirectory().string()};
break;
case SettingsID::Core_OverrideGameSpecificSettings:
setting = {SETTING_SECTION_CORE, "OverrideGameSpecificSettings", false};
break;
@@ -1376,6 +1366,9 @@ static l_Setting get_setting(SettingsID settingId)
case SettingsID::GCAInput_TriggerTreshold:
setting = {SETTING_SECTION_GCA, "TriggerTreshold", 50};
break;
case SettingsID::GCAInput_SwapZL:
setting = {SETTING_SECTION_GCA, "GCAInput_SwapZL", true};
break;
}
return setting;
+1 -5
View File
@@ -54,11 +54,6 @@ enum class SettingsID
Core_INPUT_Plugin,
Core_RSP_Plugin,
// Core User Directory Settings
Core_OverrideUserDirs,
Core_UserDataDirOverride,
Core_UserCacheDirOverride,
// Core 64DD ROM Settings
Core_64DD_JapaneseIPL,
Core_64DD_AmericanIPL,
@@ -466,6 +461,7 @@ enum class SettingsID
GCAInput_Sensitivity,
GCAInput_CButtonTreshold,
GCAInput_TriggerTreshold,
GCAInput_SwapZL,
Invalid
};
+6 -1
View File
@@ -30,4 +30,9 @@ target_include_directories(RMG-Input-GCA PRIVATE
${LIBUSB1_INCLUDE_DIRS}
)
target_link_libraries(RMG-Input-GCA RMG-Core ${LIBUSB1_LIBRARIES} Qt6::Gui Qt6::Widgets Qt6::Svg)
target_link_libraries(RMG-Input-GCA
RMG-Core
${LIBUSB1_LIBRARIES}
Qt6::Gui Qt6::Widgets Qt6::Svg
atomic # needed by GCC due to std::atomic usage
)
@@ -21,6 +21,7 @@ MainDialog::MainDialog(QWidget* parent) : QDialog(parent)
this->sensitivitySlider->setValue(CoreSettingsGetIntValue(SettingsID::GCAInput_Sensitivity));
this->triggerTresholdSlider->setValue(CoreSettingsGetIntValue(SettingsID::GCAInput_TriggerTreshold));
this->cButtonTresholdSlider->setValue(CoreSettingsGetIntValue(SettingsID::GCAInput_CButtonTreshold));
this->swapZLCheckBox->setChecked(CoreSettingsGetBoolValue(SettingsID::GCAInput_SwapZL));
}
MainDialog::~MainDialog()
@@ -39,6 +40,7 @@ void MainDialog::on_buttonBox_clicked(QAbstractButton* button)
CoreSettingsSetValue(SettingsID::GCAInput_Sensitivity, this->sensitivitySlider->value());
CoreSettingsSetValue(SettingsID::GCAInput_TriggerTreshold, this->triggerTresholdSlider->value());
CoreSettingsSetValue(SettingsID::GCAInput_CButtonTreshold, this->cButtonTresholdSlider->value());
CoreSettingsSetValue(SettingsID::GCAInput_SwapZL, this->swapZLCheckBox->isChecked());
CoreSettingsSave();
}
else if (pushButton == defaultButton)
@@ -47,6 +49,7 @@ void MainDialog::on_buttonBox_clicked(QAbstractButton* button)
this->sensitivitySlider->setValue(CoreSettingsGetDefaultIntValue(SettingsID::GCAInput_Sensitivity));
this->triggerTresholdSlider->setValue(CoreSettingsGetDefaultIntValue(SettingsID::GCAInput_TriggerTreshold));
this->cButtonTresholdSlider->setValue(CoreSettingsGetDefaultIntValue(SettingsID::GCAInput_CButtonTreshold));
this->swapZLCheckBox->setChecked(CoreSettingsGetDefaultBoolValue(SettingsID::GCAInput_SwapZL));
}
}
@@ -90,6 +90,22 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Buttons</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QCheckBox" name="swapZLCheckBox">
<property name="text">
<string>Swap Z and L</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
+95 -37
View File
@@ -20,6 +20,12 @@
#include "UserInterface/MainDialog.hpp"
#include <atomic>
#include <chrono>
#include <thread>
#include <array>
#include <mutex>
//
// Local Defines
//
@@ -46,6 +52,8 @@ struct SettingsProfile
double TriggerTreshold = 0.5;
double CButtonTreshold = 0.4;
bool SwapZL = false;
};
struct GameCubeAdapterControllerState
@@ -100,7 +108,11 @@ static bool l_UsbInitialized = false;
// GCA variables
static libusb_device_handle* l_DeviceHandle = nullptr;
static GameCubeAdapterControllerState l_ControllerState[4] = {0};
static std::atomic<bool> l_PollThreadRunning;
static std::atomic<bool> l_PolledState;
static std::mutex l_ControllerStateMutex;
static std::array<GameCubeAdapterControllerState, 4> l_ControllerState;
static std::thread l_PollThread;
static SettingsProfile l_Settings = {0};
// mupen64plus debug callback
@@ -151,16 +163,22 @@ static void usb_quit(void)
}
}
static void gca_reset_state(void)
{
l_ControllerStateMutex.lock();
l_ControllerState = {0};
l_ControllerStateMutex.unlock();
}
static bool gca_init(void)
{
std::string debugMessage;
int ret;
// reset state
for (int i = 0; i < NUM_CONTROLLERS; i++)
{
l_ControllerState[i] = {0};
}
gca_reset_state();
l_PolledState.store(false);
l_PollThreadRunning.store(true);
// attempt open device
l_DeviceHandle = libusb_open_device_with_vid_pid(nullptr, GCA_VENDOR_ID, GCA_PRODUCT_ID);
@@ -227,39 +245,62 @@ static void gca_quit(void)
}
}
static bool gca_poll_input(int index, GameCubeAdapterControllerState& state)
static void gca_poll_thread(void)
{
if (index == 0 && l_DeviceHandle != nullptr)
uint8_t readBuf[37] = {0};
int transferred = 0;
int ret;
int offset;
std::array<GameCubeAdapterControllerState, 4> state;
std::string debugMessage;
while (l_PollThreadRunning.load(std::memory_order_relaxed))
{
uint8_t readBuf[37] = {0};
int transferred = 0;
int ret = libusb_interrupt_transfer(l_DeviceHandle, GCA_ENDPOINT_IN, readBuf, sizeof(readBuf), &transferred, 16);
if (ret != LIBUSB_SUCCESS || transferred != sizeof(readBuf))
ret = libusb_interrupt_transfer(l_DeviceHandle, GCA_ENDPOINT_IN, readBuf, sizeof(readBuf), &transferred, 16);
if (ret == LIBUSB_ERROR_NO_DEVICE)
{
std::string debugMessage;
debugMessage = "gca_poll_input(): failed to retrieve input buffer: ";
debugMessage = "gca_poll_thread(): adapter disconnected, stopping polling thread";
PluginDebugMessage(M64MSG_WARNING, debugMessage);
// reset state
gca_reset_state();
// ensure that we don't get stuck in InitiateControllers(),
// because that might be waiting on l_PolledState to be set
l_PolledState.store(true);
return;
}
else if (ret != LIBUSB_SUCCESS || transferred != sizeof(readBuf))
{
debugMessage = "gca_poll_thread(): failed to retrieve input buffer: ";
debugMessage += libusb_error_name(ret);
PluginDebugMessage(M64MSG_WARNING, debugMessage);
return false;
continue;
}
for (int i = 0; i < NUM_CONTROLLERS; i++)
{
int offset = (i * 9);
l_ControllerState[i].Status = readBuf[offset + 1];
l_ControllerState[i].Buttons1 = readBuf[offset + 2];
l_ControllerState[i].Buttons2 = readBuf[offset + 3];
l_ControllerState[i].LeftStickX = readBuf[offset + 4];
l_ControllerState[i].LeftStickY = readBuf[offset + 5];
l_ControllerState[i].RightStickX = readBuf[offset + 6];
l_ControllerState[i].RightStickY = readBuf[offset + 7];
l_ControllerState[i].LeftTrigger = readBuf[offset + 8];
l_ControllerState[i].RightTrigger = readBuf[offset + 9];
offset = (i * 9);
state[i].Status = readBuf[offset + 1];
state[i].Buttons1 = readBuf[offset + 2];
state[i].Buttons2 = readBuf[offset + 3];
state[i].LeftStickX = readBuf[offset + 4];
state[i].LeftStickY = readBuf[offset + 5];
state[i].RightStickX = readBuf[offset + 6];
state[i].RightStickY = readBuf[offset + 7];
state[i].LeftTrigger = readBuf[offset + 8];
state[i].RightTrigger = readBuf[offset + 9];
}
}
state = l_ControllerState[index];
return true;
l_ControllerStateMutex.lock();
l_ControllerState = state;
l_ControllerStateMutex.unlock();
l_PolledState.store(true, std::memory_order_relaxed);
// poll every 1ms
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
static void load_settings(void)
@@ -268,6 +309,7 @@ static void load_settings(void)
l_Settings.SensitivityValue = static_cast<double>(CoreSettingsGetIntValue(SettingsID::GCAInput_Sensitivity)) / 100.0;
l_Settings.CButtonTreshold = static_cast<double>(CoreSettingsGetIntValue(SettingsID::GCAInput_CButtonTreshold)) / 100.0;
l_Settings.TriggerTreshold = static_cast<double>(CoreSettingsGetIntValue(SettingsID::GCAInput_TriggerTreshold)) / 100.0;
l_Settings.SwapZL = CoreSettingsGetBoolValue(SettingsID::GCAInput_SwapZL);
}
static double apply_deadzone(const double input, const double deadzone)
@@ -368,8 +410,11 @@ EXPORT void CALL ControllerCommand(int Control, unsigned char* Command)
EXPORT void CALL GetKeys(int Control, BUTTONS* Keys)
{
GameCubeAdapterControllerState state;
if (!gca_poll_input(Control, state) || !state.Status)
l_ControllerStateMutex.lock();
GameCubeAdapterControllerState state = l_ControllerState[Control];
l_ControllerStateMutex.unlock();
if (!state.Status)
{
Keys->Value = 0;
return;
@@ -382,15 +427,15 @@ EXPORT void CALL GetKeys(int Control, BUTTONS* Keys)
Keys->R_DPAD = state.DpadRight;
Keys->D_DPAD = state.DpadDown;
Keys->U_DPAD = state.DpadUp;
Keys->Z_TRIG = state.Z;
const int triggerTreshold = INT8_MAX * l_Settings.TriggerTreshold;
const int cStickTreshold = INT8_MAX * l_Settings.CButtonTreshold;
const int8_t cX = static_cast<int8_t>(state.RightStickX + 128);
const int8_t cY = static_cast<int8_t>(state.RightStickY + 128);
Keys->L_TRIG = state.LeftTrigger > triggerTreshold;
Keys->R_TRIG = state.RightTrigger > triggerTreshold;
Keys->L_TRIG = l_Settings.SwapZL ? state.Z : (state.LeftTrigger > triggerTreshold);
Keys->Z_TRIG = l_Settings.SwapZL ? (state.LeftTrigger > triggerTreshold) : state.Z;
Keys->L_CBUTTON = cX < -cStickTreshold;
Keys->R_CBUTTON = cX > cStickTreshold;
@@ -417,16 +462,22 @@ EXPORT void CALL InitiateControllers(CONTROL_INFO ControlInfo)
return;
}
// start polling thread
l_PollThread = std::thread(gca_poll_thread);
// wait for initial state to be polled
while (!l_PolledState.load())
{
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
l_ControllerStateMutex.lock();
for (int i = 0; i < NUM_CONTROLLERS; i++)
{
GameCubeAdapterControllerState state;
if (!gca_poll_input(i, state))
{
return;
}
GameCubeAdapterControllerState state = l_ControllerState[i];
ControlInfo.Controls[i].Present = (state.Status > 0) ? 1 : 0;
}
l_ControllerStateMutex.unlock();
// load settings
load_settings();
@@ -443,6 +494,13 @@ EXPORT int CALL RomOpen(void)
EXPORT void CALL RomClosed(void)
{
// wait for polling thread to stop
if (l_PollThread.joinable())
{
l_PollThreadRunning.store(false);
l_PollThread.join();
}
gca_quit();
}
+4
View File
@@ -13,6 +13,7 @@ find_package(Qt6 COMPONENTS Gui Widgets Core Svg REQUIRED)
find_package(SDL3 REQUIRED)
set(RMG_INPUT_SOURCES
UserInterface/Layout/AlignCenterLayout.cpp
UserInterface/Widget/ControllerWidget.ui
UserInterface/Widget/ControllerWidget.cpp
UserInterface/Widget/ControllerImageWidget.cpp
@@ -28,6 +29,9 @@ set(RMG_INPUT_SOURCES
UserInterface/MainDialog.ui
UserInterface/MainDialog.cpp
UserInterface/UIResources.qrc
UserInterface/UICommon.cpp
Utilities/Sdl3ButtonLabelToString.cpp
Utilities/InputProfileDB.cpp
Utilities/QtKeyToSdl3Key.cpp
Thread/SDLThread.cpp
Thread/HotkeysThread.cpp
@@ -25,17 +25,19 @@ bool EventFilter::eventFilter(QObject *object, QEvent *event)
{
case QEvent::Type::KeyPress:
{
if (((QKeyEvent *)event)->key() != Qt::Key_Escape)
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() != Qt::Key_Escape)
{
emit this->on_EventFilter_KeyPressed((QKeyEvent *)event);
emit this->on_EventFilter_KeyPressed(keyEvent);
return true;
}
} break;
case QEvent::Type::KeyRelease:
{
if (((QKeyEvent *)event)->key() != Qt::Key_Escape)
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() != Qt::Key_Escape)
{
emit this->on_EventFilter_KeyReleased((QKeyEvent *)event);
emit this->on_EventFilter_KeyReleased(keyEvent);
return true;
}
} break;
@@ -45,3 +47,4 @@ bool EventFilter::eventFilter(QObject *object, QEvent *event)
return QObject::eventFilter(object, event);
}
;
@@ -8,20 +8,23 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "HotkeysDialog.hpp"
#include "UserInterface/UICommon.hpp"
#include "common.hpp"
using namespace UserInterface;
HotkeysDialog::HotkeysDialog(QWidget* parent, QList<HotkeySettingMapping> hotkeySettingMappings,
bool isGameController, SDL_JoystickID joystickId,
SDL_JoystickID joystickId, SDL_Gamepad* gamepad,
bool filterEvents, bool removeDuplicates) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint)
{
this->setupUi(this);
this->currentJoystickId = joystickId;
this->isCurrentJoystickGameController = isGameController;
this->filterEventsForButtons = filterEvents;
this->removeDuplicates = removeDuplicates;
this->currentJoystickId = joystickId;
this->currentGamepad = gamepad;
this->isCurrentJoystickGamepad = (gamepad != nullptr);
this->filterEventsForButtons = filterEvents;
this->removeDuplicates = removeDuplicates;
this->hotkeySettingMappings.append(
{
@@ -87,7 +90,7 @@ HotkeysDialog::HotkeysDialog(QWidget* parent, QList<HotkeySettingMapping> hotkey
for (size_t y = 0; y < givenMapping.inputTypes.size(); y++)
{
buttonMapping.button->AddInputData(
(InputType)givenMapping.inputTypes.at(y),
static_cast<InputType>(givenMapping.inputTypes.at(y)),
givenMapping.inputData.at(y),
givenMapping.extraInputData.at(y),
QString::fromStdString(givenMapping.inputText.at(y))
@@ -115,7 +118,7 @@ void HotkeysDialog::on_MainDialog_SdlEvent(SDL_Event* event)
if (event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN)
{ // gamepad button
if (!this->isCurrentJoystickGameController &&
if (!this->isCurrentJoystickGamepad &&
this->filterEventsForButtons)
{
return;
@@ -124,11 +127,11 @@ void HotkeysDialog::on_MainDialog_SdlEvent(SDL_Event* event)
joystickId = event->gbutton.which;
inputType = InputType::GamepadButton;
sdlButton = event->gbutton.button;
sdlButtonName = SDL_GetGamepadStringForButton((SDL_GamepadButton)sdlButton);
sdlButtonName = UICommon::GetGamepadButtonText(this->currentGamepad, static_cast<SDL_GamepadButton>(sdlButton));
}
else
{ // joystick button
if (this->isCurrentJoystickGameController &&
if (this->isCurrentJoystickGamepad &&
this->filterEventsForButtons)
{
return;
@@ -164,7 +167,7 @@ void HotkeysDialog::on_MainDialog_SdlEvent(SDL_Event* event)
if (event->type == SDL_EVENT_GAMEPAD_BUTTON_UP)
{ // gamepad button
if (!this->isCurrentJoystickGameController &&
if (!this->isCurrentJoystickGamepad &&
this->filterEventsForButtons)
{
return;
@@ -174,7 +177,7 @@ void HotkeysDialog::on_MainDialog_SdlEvent(SDL_Event* event)
}
else
{ // joystick button
if (this->isCurrentJoystickGameController &&
if (this->isCurrentJoystickGamepad &&
this->filterEventsForButtons)
{
return;
@@ -209,7 +212,7 @@ void HotkeysDialog::on_MainDialog_SdlEvent(SDL_Event* event)
if (event->type == SDL_EVENT_GAMEPAD_AXIS_MOTION)
{ // gamepad axis
if (!this->isCurrentJoystickGameController &&
if (!this->isCurrentJoystickGamepad &&
this->filterEventsForButtons)
{
return;
@@ -219,12 +222,12 @@ void HotkeysDialog::on_MainDialog_SdlEvent(SDL_Event* event)
inputType = InputType::GamepadAxis;
sdlAxis = event->gaxis.axis;
sdlAxisValue = event->gaxis.value;
sdlAxisName = SDL_GetGamepadStringForAxis((SDL_GamepadAxis)sdlAxis);
sdlAxisName = SDL_GetGamepadStringForAxis(static_cast<SDL_GamepadAxis>(sdlAxis));
sdlAxisName += sdlAxisValue > 0 ? "+" : "-";
}
else
{ // joystick axis
if (this->isCurrentJoystickGameController &&
if (this->isCurrentJoystickGamepad &&
this->filterEventsForButtons)
{
return;
@@ -40,10 +40,11 @@ Q_OBJECT
private:
SDL_JoystickID currentJoystickId = -1;
bool isCurrentJoystickGameController = false;
bool filterEventsForButtons = false;
bool removeDuplicates = false;
SDL_JoystickID currentJoystickId = -1;
SDL_Gamepad* currentGamepad = nullptr;
bool isCurrentJoystickGamepad = false;
bool filterEventsForButtons = false;
bool removeDuplicates = false;
struct hotkeySettingMapping
{
@@ -60,7 +61,7 @@ private:
public:
HotkeysDialog(QWidget *parent, QList<HotkeySettingMapping> hotkeySettingMappings,
bool isGameController, SDL_JoystickID joystickId,
SDL_JoystickID joystickId, SDL_Gamepad* gamepad,
bool filterEvents, bool removeDuplicates);
QList<HotkeySettingMapping> GetSettingMappings();
@@ -0,0 +1,100 @@
/*
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
* Copyright (C) 2020-2025 Rosalie Wanders <rosalie@mailbox.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "AlignCenterLayout.hpp"
using namespace UserInterface::Layout;
AlignCenterLayout::AlignCenterLayout(int spacing) : QLayout()
{
this->setSpacing(spacing);
}
AlignCenterLayout::~AlignCenterLayout(void)
{
for (int i = 0; i < this->items.count(); i++)
{
delete this->items.at(i);
}
this->items.clear();
}
void AlignCenterLayout::addItem(QLayoutItem *item)
{
this->items.append(item);
}
QSize AlignCenterLayout::sizeHint() const
{
return this->minimumSize();
}
QSize AlignCenterLayout::minimumSize() const
{
QSize size(0, 0);
int count = this->items.count();
for (int i = 0; i < count; i++)
{
QLayoutItem* item = this->items.at(i);
size = size.expandedTo(item->minimumSize());
}
// take spacing for top & bottom into account
size.setHeight(size.height() + (this->spacing() * 2));
return size;
}
QSize AlignCenterLayout::maximumSize() const
{
return this->minimumSize();
}
int AlignCenterLayout::count() const
{
return this->items.count();
}
QLayoutItem* AlignCenterLayout::itemAt(int index) const
{
return index >= 0 && index < this->items.size() ? this->items.value(index) : nullptr;
}
QLayoutItem* AlignCenterLayout::takeAt(int index)
{
return index >= 0 && index < this->items.size() ? this->items.takeAt(index) : nullptr;
}
void AlignCenterLayout::setGeometry(const QRect& rect)
{
QLayout::setGeometry(rect);
QLayoutItem* leftItem = this->items.at(0);
QLayoutItem* centerItem = this->items.at(1);
QLayoutItem* rightItem = this->items.at(2);
// the side widgets are limited to 25% of available width
// the middle widget is limited to 40% of the available width
//
// this also takes into account the situation where there's an overlap
// between widgets, which is something this avoids.
const int spacing = this->spacing();
const int itemHeight = centerItem->minimumSize().height() + spacing;
const int rectWidth = rect.width();
const int topLocation = rect.y() + spacing;
const int centerItemWidth = std::min(centerItem->sizeHint().width(), static_cast<int>(std::ceil(rectWidth / 2.5)));
const int centerItemLocation = ((rectWidth / 2)) - (centerItemWidth / 2);
const int maxSideItemWidth = std::min(centerItemLocation, rectWidth / 4) - spacing;
const int sideItemWidth = std::min(leftItem->minimumSize().width(), maxSideItemWidth);
leftItem->setGeometry(QRect(rect.x(), topLocation, sideItemWidth, itemHeight));
centerItem->setGeometry(QRect(rect.x() + centerItemLocation, topLocation, centerItemWidth, itemHeight));
rightItem->setGeometry(QRect(rect.x() + rectWidth - sideItemWidth, topLocation, sideItemWidth, itemHeight));
}
@@ -0,0 +1,42 @@
/*
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
* Copyright (C) 2020-2025 Rosalie Wanders <rosalie@mailbox.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef ALIGNCENTERLAYOUT_HPP
#define ALIGNCENTERLAYOUT_HPP
#include <QLayoutItem>
#include <QLayout>
#include <QList>
namespace UserInterface
{
namespace Layout
{
class AlignCenterLayout : public QLayout
{
private:
QList<QLayoutItem *> items;
public:
AlignCenterLayout(int spacing);
~AlignCenterLayout(void);
void addItem(QLayoutItem* item) override;
QSize sizeHint() const override;
QSize minimumSize() const override;
QSize maximumSize() const override;
int count() const override;
QLayoutItem *itemAt(int) const override;
QLayoutItem *takeAt(int) override;
void setGeometry(const QRect& rect) override;
};
}
}
#endif // ALIGNCENTERLAYOUT_HPP
+35 -30
View File
@@ -59,6 +59,9 @@ MainDialog::MainDialog(QWidget* parent, Thread::SDLThread* sdlThread, bool romCo
&MainDialog::on_ControllerWidget_UserProfileRemoved);
}
// initialize input profile DB
this->inputProfileDB.Load();
// always add keyboard device
for (auto& controllerWidget : this->controllerWidgets)
{
@@ -68,14 +71,14 @@ MainDialog::MainDialog(QWidget* parent, Thread::SDLThread* sdlThread, bool romCo
// so we only have to expose it there
if (controllerWidget == this->controllerWidgets.last())
{
controllerWidget->AddInputDevice({ InputDeviceType::EmulateVRU, "Voice Recognition Unit" });
controllerWidget->AddInputDevice({ InputDeviceType::EmulateVRU, "Voice Recognition Unit" }, {});
}
#endif // VRU
controllerWidget->SetAllowKeyboardForAutomatic(controllerWidget == this->controllerWidgets.first());
controllerWidget->AddInputDevice({ InputDeviceType::None, "None" });
controllerWidget->AddInputDevice({ InputDeviceType::Automatic, "Automatic" });
controllerWidget->AddInputDevice({ InputDeviceType::Keyboard, "Keyboard" });
this->addInputDevice(controllerWidget, { InputDeviceType::None, "None" });
this->addInputDevice(controllerWidget, { InputDeviceType::Automatic, "Automatic" });
this->addInputDevice(controllerWidget, { InputDeviceType::Keyboard, "Keyboard" });
controllerWidget->SetInitialized(true);
}
@@ -92,11 +95,24 @@ MainDialog::~MainDialog()
this->closeInputDevice();
}
void MainDialog::addInputDevice(Widget::ControllerWidget* controllerWidget, const InputDevice& device)
{
// only fallback to fallback profile when
// we have a controller device
InputProfileDBEntry inputProfile = this->inputProfileDB.FindEntry(device,
device.type == InputDeviceType::Automatic ||
device.type == InputDeviceType::Joystick);
controllerWidget->AddInputDevice(device, inputProfile);
}
void MainDialog::addInputDevice(const InputDevice& device)
{
InputProfileDBEntry inputProfile = this->inputProfileDB.FindEntry(device);
for (auto& controllerWidget : this->controllerWidgets)
{
controllerWidget->AddInputDevice(device);
controllerWidget->AddInputDevice(device, inputProfile);
}
}
@@ -112,14 +128,14 @@ void MainDialog::openInputDevice(InputDevice device)
{
Widget::ControllerWidget* controllerWidget;
controllerWidget = this->controllerWidgets.at(this->tabWidget->currentIndex());
InputProfileDBEntry inputProfile = {};
// we don't need to open a non-joystick device
if (device.type != InputDeviceType::Automatic &&
device.type != InputDeviceType::Joystick)
{
this->currentDevice = { };
controllerWidget->SetCurrentJoystickID(this->currentDevice.id);
controllerWidget->SetCurrentJoystick(nullptr, nullptr);
controllerWidget->SetCurrentInputDevice(this->currentDevice.id, nullptr, nullptr, inputProfile);
return;
}
@@ -130,12 +146,12 @@ void MainDialog::openInputDevice(InputDevice device)
if (currentIndex < this->inputDeviceList.size())
{ // use device when there's one
device.id = this->inputDeviceList.at(currentIndex).id;
inputProfile = this->inputProfileDB.FindEntry(this->inputDeviceList.at(currentIndex));
}
else
{ // no device found, fallback to keyboard
this->currentDevice = { InputDeviceType::Keyboard, "Keyboard" };
controllerWidget->SetCurrentJoystickID(this->currentDevice.id);
controllerWidget->SetCurrentJoystick(nullptr, nullptr);
controllerWidget->SetCurrentInputDevice(this->currentDevice.id, nullptr, nullptr, inputProfile);
return;
}
}
@@ -145,18 +161,16 @@ void MainDialog::openInputDevice(InputDevice device)
(controllerMode == 2))
{
this->currentJoystick = nullptr;
this->currentController = SDL_OpenGamepad(device.id);
this->currentGamepad = SDL_OpenGamepad(device.id);
}
else if (controllerMode == 0 || controllerMode == 1)
{
this->currentJoystick = SDL_OpenJoystick(device.id);
this->currentController = nullptr;
this->currentGamepad = nullptr;
}
this->currentDevice = device;
controllerWidget->SetCurrentJoystickID(device.id);
controllerWidget->SetIsCurrentJoystickGameController(currentController != nullptr);
controllerWidget->SetCurrentJoystick(this->currentJoystick, this->currentController);
controllerWidget->SetCurrentInputDevice(device.id, this->currentJoystick, this->currentGamepad, inputProfile);
}
void MainDialog::closeInputDevice()
@@ -167,10 +181,10 @@ void MainDialog::closeInputDevice()
this->currentJoystick = nullptr;
}
if (this->currentController != nullptr)
if (this->currentGamepad != nullptr)
{
SDL_CloseGamepad(this->currentController);
this->currentController = nullptr;
SDL_CloseGamepad(this->currentGamepad);
this->currentGamepad = nullptr;
}
}
@@ -202,15 +216,6 @@ void MainDialog::on_InputPollTimer_triggered()
break;
}
// check if controller has been disconnected,
// if so, keep trying to re-open it
if ((this->currentJoystick != nullptr && !SDL_JoystickConnected(this->currentJoystick)) ||
(this->currentController != nullptr && !SDL_GamepadConnected(this->currentController)))
{
this->closeInputDevice();
this->openInputDevice(this->currentDevice);
}
// process SDL events
SDL_Event event;
while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_EVENT_FIRST, SDL_EVENT_LAST) == 1)
@@ -355,8 +360,8 @@ void MainDialog::on_EventFilter_KeyPressed(QKeyEvent *event)
SDL_KeyboardEvent keyboardEvent;
keyboardEvent.down = true;
keyboardEvent.type = SDL_EVENT_KEY_DOWN;
keyboardEvent.scancode = (SDL_Scancode)key;
keyboardEvent.key = (SDL_Keycode)key;
keyboardEvent.scancode = static_cast<SDL_Scancode>(key);
keyboardEvent.key = static_cast<SDL_Keycode>(key);
keyboardEvent.mod = mod;
SDL_Event sdlEvent;
@@ -374,8 +379,8 @@ void MainDialog::on_EventFilter_KeyReleased(QKeyEvent *event)
SDL_KeyboardEvent keyboardEvent;
keyboardEvent.down = false;
keyboardEvent.type = SDL_EVENT_KEY_UP;
keyboardEvent.scancode = (SDL_Scancode)key;
keyboardEvent.key = (SDL_Keycode)key;
keyboardEvent.scancode = static_cast<SDL_Scancode>(key);
keyboardEvent.key = static_cast<SDL_Keycode>(key);
keyboardEvent.mod = mod;
SDL_Event sdlEvent;
@@ -13,6 +13,7 @@
#include "common.hpp"
#include <QDialog>
#include "Utilities/InputProfileDB.hpp"
#include "Widget/ControllerWidget.hpp"
#include "Thread/SDLThread.hpp"
#include "EventFilter.hpp"
@@ -31,19 +32,22 @@ private:
QTimer* inputPollTimer;
Thread::SDLThread* sdlThread;
Utilities::InputProfileDB inputProfileDB;
QList<InputDevice> oldInputDeviceList;
QList<InputDevice> inputDeviceList;
bool updatingDeviceList = false;
QList<Widget::ControllerWidget*> controllerWidgets;
SDL_Joystick* currentJoystick = nullptr;
SDL_Gamepad* currentController = nullptr;
SDL_Joystick* currentJoystick = nullptr;
SDL_Gamepad* currentGamepad = nullptr;
InputDevice currentDevice;
int previousTabWidgetIndex = 0;
EventFilter* eventFilter;
void addInputDevice(Widget::ControllerWidget* controllerWidget, const InputDevice& device);
void addInputDevice(const InputDevice& device);
void removeInputDevice(const InputDevice& device);
@@ -22,7 +22,7 @@
using namespace UserInterface;
OptionsDialog::OptionsDialog(QWidget* parent, OptionsDialogSettings settings,
SDL_Joystick* joystick, SDL_Gamepad* controller) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint)
SDL_Joystick* joystick, SDL_Gamepad* gamepad) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint)
{
this->setupUi(this);
@@ -43,16 +43,11 @@ OptionsDialog::OptionsDialog(QWidget* parent, OptionsDialogSettings settings,
this->hideEmulationInfoText();
}
this->clearGameboyRomButton->setText("");
this->clearGameboyRomButton->setIcon(QIcon::fromTheme("delete-bin-line"));
this->clearGameboySaveButton->setText("");
this->clearGameboySaveButton->setIcon(QIcon::fromTheme("delete-bin-line"));
this->currentJoystick = joystick;
this->currentController = controller;
this->currentJoystick = joystick;
this->currentGamepad = gamepad;
this->testRumbleButton->setVisible(settings.ControllerPak == 1);
this->testRumbleButton->setEnabled(this->currentJoystick != nullptr || this->currentController != nullptr);
this->testRumbleButton->setEnabled(this->currentJoystick != nullptr || this->currentGamepad != nullptr);
}
OptionsDialogSettings OptionsDialog::GetSettings()
@@ -143,7 +138,7 @@ void OptionsDialog::on_testRumbleButton_clicked()
SDL_PropertiesID properties;
if ((this->currentJoystick != nullptr && (properties = SDL_GetJoystickProperties(this->currentJoystick)) != 0) ||
(this->currentController != nullptr && (properties = SDL_GetGamepadProperties(this->currentController)) != 0))
(this->currentGamepad != nullptr && (properties = SDL_GetGamepadProperties(this->currentGamepad)) != 0))
{
const char* propertyName = this->currentJoystick != nullptr ? SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN
: SDL_PROP_GAMEPAD_CAP_RUMBLE_BOOLEAN;
@@ -168,6 +163,6 @@ void OptionsDialog::on_testRumbleButton_clicked()
}
else
{
SDL_RumbleGamepad(this->currentController, 0xFFFF, 0xFFFF, 1500);
SDL_RumbleGamepad(this->currentGamepad, 0xFFFF, 0xFFFF, 1500);
}
}
@@ -38,7 +38,7 @@ Q_OBJECT
public:
OptionsDialog(QWidget *parent, OptionsDialogSettings settings,
SDL_Joystick* joystick, SDL_Gamepad* controller);
SDL_Joystick* joystick, SDL_Gamepad* gamepad);
OptionsDialogSettings GetSettings();
@@ -48,8 +48,8 @@ protected:
private:
OptionsDialogSettings settings;
SDL_Joystick* currentJoystick = nullptr;
SDL_Gamepad* currentController = nullptr;
SDL_Joystick* currentJoystick = nullptr;
SDL_Gamepad* currentGamepad = nullptr;
void setIconsForEmulationInfoText(void);
void hideEmulationInfoText(void);
@@ -114,12 +114,18 @@
<property name="text">
<string>Change</string>
</property>
<property name="icon">
<iconset theme="folder-open-line"/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="clearGameboyRomButton">
<property name="text">
<string>Clear</string>
<string/>
</property>
<property name="icon">
<iconset theme="delete-bin-line"/>
</property>
</widget>
</item>
@@ -144,12 +150,18 @@
<property name="text">
<string>Change</string>
</property>
<property name="icon">
<iconset theme="folder-open-line"/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="clearGameboySaveButton">
<property name="text">
<string>Clear</string>
<string/>
</property>
<property name="icon">
<iconset theme="delete-bin-line"/>
</property>
</widget>
</item>
@@ -280,7 +292,7 @@
</item>
<item>
<property name="text">
<string>Controller</string>
<string>Gamepad</string>
</property>
</item>
</widget>
@@ -0,0 +1,37 @@
/*
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
* Copyright (C) 2020-2025 Rosalie Wanders <rosalie@mailbox.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "Utilities/Sdl3ButtonLabelToString.hpp"
#include "UICommon.hpp"
QString UICommon::GetGamepadButtonText(SDL_Gamepad* gamepad, SDL_GamepadButton button)
{
QString text = SDL_GetGamepadStringForButton(button);
if (gamepad == nullptr)
{
return text;
}
// convert A/B/X/Y buttons to their native counterpart,
// this makes the button text look better in the UI
if (button == SDL_GAMEPAD_BUTTON_SOUTH ||
button == SDL_GAMEPAD_BUTTON_EAST ||
button == SDL_GAMEPAD_BUTTON_WEST ||
button == SDL_GAMEPAD_BUTTON_NORTH)
{
SDL_GamepadButtonLabel buttonLabel = SDL_GetGamepadButtonLabel(gamepad, button);
QString labelString = Utilities::Sdl3ButtonLabelToString(buttonLabel);
if (!labelString.isEmpty())
{ // replace only when we have a name
text = labelString;
}
}
return text;
}
@@ -0,0 +1,24 @@
/*
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
* Copyright (C) 2020-2025 Rosalie Wanders <rosalie@mailbox.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef UICOMMON_HPP
#define UICOMMON_HPP
#include <SDL3/SDL.h>
#include <QString>
namespace UICommon
{
// attempts to retrieve native button text for button from gamepad
QString GetGamepadButtonText(SDL_Gamepad* gamepad, SDL_GamepadButton button);
};
#endif // UICOMMON_HPP
@@ -26,7 +26,7 @@ void AddMappingButton::Initialize(ControllerWidget* widget, MappingButton* butto
this->controllerWidget = widget;
this->mappingButton = button;
connect(this, &QPushButton::released, [=, this]
connect(this, &QPushButton::released, [this]
{
emit this->controllerWidget->on_AddMappingButton_Released(this->mappingButton);
});
@@ -10,6 +10,9 @@
#include "ControllerWidget.hpp"
#include "UserInterface/OptionsDialog.hpp"
#include "UserInterface/Layout/AlignCenterLayout.hpp"
#include "UserInterface/UICommon.hpp"
#include "common.hpp"
#include <RMG-Core/RomSettings.hpp>
@@ -25,12 +28,24 @@
#include <SDL3/SDL.h>
using namespace UserInterface::Layout;
using namespace UserInterface::Widget;
ControllerWidget::ControllerWidget(QWidget* parent, EventFilter* eventFilter) : QWidget(parent)
{
this->setupUi(this);
// sadly Qt Designer doesn't support inserting
// custom layouts, so just replace an existing
// one with our custom one
this->mainLayout->removeItem(this->horizontalLayout_4);
delete this->horizontalLayout_4;
AlignCenterLayout* layout = new AlignCenterLayout(this->mainLayout->spacing());
layout->addWidget(profileGroupBox);
layout->addWidget(inputDeviceGroupBox);
layout->addWidget(deadZoneGroupBox);
this->mainLayout->insertLayout(0, layout);
// install eventFilter to all children and parents
this->installEventFilter(eventFilter);
for (auto& widget : this->children())
@@ -175,7 +190,6 @@ ControllerWidget::ControllerWidget(QWidget* parent, EventFilter* eventFilter) :
ControllerWidget::~ControllerWidget()
{
}
void ControllerWidget::initializeMappingButtons()
@@ -238,6 +252,7 @@ void ControllerWidget::initializeProfileButtons()
void ControllerWidget::initializeMiscButtons()
{
this->inputDeviceRefreshButton->setIcon(QIcon::fromTheme("refresh-line"));
this->autoConfigureButton->setIcon(QIcon::fromTheme("magic-line"));
this->resetButton->setIcon(QIcon::fromTheme("restart-line"));
this->optionsButton->setIcon(QIcon::fromTheme("settings-3-line"));
this->hotkeysButton->setIcon(QIcon::fromTheme("gamepad-line"));
@@ -245,10 +260,10 @@ void ControllerWidget::initializeMiscButtons()
bool ControllerWidget::isCurrentDeviceKeyboard()
{
const InputDevice device = this->inputDeviceComboBox->currentData().value<InputDevice>();
const inputDeviceData deviceData = this->inputDeviceComboBox->currentData().value<inputDeviceData>();
return device.type == InputDeviceType::Keyboard ||
(this->allowKeyboardForAutomatic && device.type == InputDeviceType::Automatic);
return deviceData.device.type == InputDeviceType::Keyboard ||
(this->allowKeyboardForAutomatic && deviceData.device.type == InputDeviceType::Automatic);
}
bool ControllerWidget::isCurrentDeviceNotFound()
@@ -259,23 +274,11 @@ bool ControllerWidget::isCurrentDeviceNotFound()
void ControllerWidget::disableAllChildren()
{
for (auto& object : this->children())
if (this->disabledAllChildren)
{
if (!object->inherits("QWidget"))
{
continue;
}
QWidget* widget = (QWidget*)object;
if (widget->isEnabled())
{
widget->setEnabled(false);
}
return;
}
}
void ControllerWidget::enableAllChildren()
{
for (auto& object : this->children())
{
if (!object->inherits("QWidget"))
@@ -284,11 +287,46 @@ void ControllerWidget::enableAllChildren()
}
QWidget* widget = static_cast<QWidget*>(object);
if (!widget->isEnabled())
if (widget == this->autoConfigureButton)
{ // save the state because whether it's enabled
// is set elsewhere
this->autoConfigureButtonEnabled = widget->isEnabled();
}
if (widget->isEnabled())
{
widget->setEnabled(false);
}
}
this->disabledAllChildren = true;
}
void ControllerWidget::enableAllChildren()
{
if (!this->disabledAllChildren)
{
return;
}
for (auto& object : this->children())
{
if (!object->inherits("QWidget"))
{
continue;
}
QWidget* widget = static_cast<QWidget*>(object);
if (widget == this->autoConfigureButton)
{ // we have to restore the state
widget->setEnabled(this->autoConfigureButtonEnabled);
}
else if (!widget->isEnabled())
{
widget->setEnabled(true);
}
}
this->disabledAllChildren = false;
}
void ControllerWidget::removeDuplicates(MappingButton* button)
@@ -488,6 +526,22 @@ bool ControllerWidget::hasAnySettingChanged(QString sectionQString)
return false;
}
void ControllerWidget::addProfile(const QString& profile, const QString& section)
{
int index = this->profileComboBox->findData(section);
if (index == -1)
{
this->profileComboBox->addItem(profile, section);
// add tooltip to UI
index = this->profileComboBox->findData(section);
if (index != -1)
{
this->profileComboBox->setItemData(index, profile, Qt::ToolTipRole);
}
}
}
void ControllerWidget::showErrorMessage(QString text, QString details)
{
QMessageBox msgBox(this);
@@ -499,7 +553,7 @@ void ControllerWidget::showErrorMessage(QString text, QString details)
msgBox.exec();
}
void ControllerWidget::AddInputDevice(const InputDevice& device)
void ControllerWidget::AddInputDevice(const InputDevice& device, const InputProfileDBEntry& inputProfile)
{
QString deviceName = QString::fromStdString(device.name);
QString name = deviceName;
@@ -511,18 +565,26 @@ void ControllerWidget::AddInputDevice(const InputDevice& device)
name += ")";
}
this->inputDeviceNameList.append(deviceName);
this->inputDeviceComboBox->addItem(name, QVariant::fromValue<InputDevice>(device));
this->inputDeviceComboBox->addItem(name, QVariant::fromValue<inputDeviceData>({ device, inputProfile }));
// add tooltip to UI
int index = this->inputDeviceComboBox->findText(name);
if (index != -1)
{
this->inputDeviceComboBox->setItemData(index, name, Qt::ToolTipRole);
}
}
void ControllerWidget::RemoveInputDevice(const InputDevice& device)
{
inputDeviceNameList.removeOne(QString::fromStdString(device.name));
int index = this->inputDeviceComboBox->findData(QVariant::fromValue<InputDevice>(device));
if (index >= 0)
inputDeviceData deviceData;
for (int i = 0; i < this->inputDeviceComboBox->count(); i++)
{
this->inputDeviceComboBox->removeItem(index);
deviceData = this->inputDeviceComboBox->itemData(i).value<inputDeviceData>();
if (device == deviceData.device)
{
this->inputDeviceComboBox->removeItem(i);
}
}
}
@@ -599,7 +661,6 @@ void ControllerWidget::CheckInputDeviceSettings(QString sectionQString)
int notFoundIndex = this->inputDeviceComboBox->findText("(not found)", Qt::MatchFlag::MatchEndsWith);
if (notFoundIndex != -1)
{
this->inputDeviceNameList.removeAt(notFoundIndex);
this->inputDeviceComboBox->removeItem(notFoundIndex);
}
@@ -608,7 +669,8 @@ void ControllerWidget::CheckInputDeviceSettings(QString sectionQString)
for (int i = 0; i < this->inputDeviceComboBox->count(); i++)
{
InputDevice otherDevice = this->inputDeviceComboBox->itemData(i).value<InputDevice>();
inputDeviceData otherDeviceData = this->inputDeviceComboBox->itemData(i).value<inputDeviceData>();
InputDevice otherDevice = otherDeviceData.device;
if (device.name == otherDevice.name &&
device.serial == otherDevice.serial)
{
@@ -643,9 +705,8 @@ void ControllerWidget::CheckInputDeviceSettings(QString sectionQString)
{ // no match
QString title = QString::fromStdString(deviceName);
title += " (not found)";
this->inputDeviceNameList.append(QString::fromStdString(deviceName));
this->inputDeviceComboBox->addItem(title, QVariant::fromValue<InputDevice>(device));
this->inputDeviceComboBox->setCurrentIndex(this->inputDeviceNameList.count() - 1);
this->inputDeviceComboBox->addItem(title, QVariant::fromValue<inputDeviceData>({ device, {} }));
this->inputDeviceComboBox->setCurrentIndex(this->inputDeviceComboBox->count() - 1);
}
}
@@ -669,7 +730,7 @@ void ControllerWidget::GetCurrentInputDevice(InputDevice& device, bool ignoreDev
}
else
{
device = this->inputDeviceComboBox->itemData(currentIndex).value<InputDevice>();
device = this->inputDeviceComboBox->itemData(currentIndex).value<inputDeviceData>().device;
}
}
@@ -710,6 +771,9 @@ void ControllerWidget::on_profileComboBox_currentIndexChanged(int value)
// update previous index
this->previousProfileComboBoxIndex = value;
// update tooltip
this->profileComboBox->setToolTip(this->profileComboBox->itemText(value));
// reload settings from section
this->LoadSettings(this->getCurrentSettingsSection());
// reload input device settings
@@ -724,20 +788,26 @@ void ControllerWidget::on_inputDeviceComboBox_currentIndexChanged(int value)
return;
}
InputDevice device = this->inputDeviceComboBox->itemData(value).value<InputDevice>();
inputDeviceData deviceData = this->inputDeviceComboBox->itemData(value).value<inputDeviceData>();
this->ClearControllerImage();
if (this->isCurrentDeviceNotFound())
{
device = { };
deviceData.device = { };
}
// set plugged in state
this->setPluggedIn(device.type != InputDeviceType::None &&
device.type != InputDeviceType::EmulateVRU);
// set whether auto configure button is enabled
this->autoConfigureButton->setEnabled(deviceData.inputProfile.valid);
emit this->CurrentInputDeviceChanged(this, device);
// set plugged in state
this->setPluggedIn(deviceData.device.type != InputDeviceType::None &&
deviceData.device.type != InputDeviceType::EmulateVRU);
// update tooltip
this->inputDeviceComboBox->setToolTip(this->inputDeviceComboBox->itemText(value));
emit this->CurrentInputDeviceChanged(this, deviceData.device);
}
void ControllerWidget::on_inputDeviceRefreshButton_clicked()
@@ -785,7 +855,7 @@ void ControllerWidget::on_addProfileButton_clicked()
section = this->getUserProfileSectionName(newProfile);
// add profile to UI
this->profileComboBox->addItem(newProfile, section);
this->addProfile(newProfile, section);
this->profileComboBox->setCurrentText(newProfile);
// add profile to settings
@@ -869,6 +939,57 @@ void ControllerWidget::on_removeProfileButton_clicked()
}
}
void ControllerWidget::on_autoConfigureButton_clicked()
{
inputDeviceData deviceData = this->inputDeviceComboBox->currentData().value<inputDeviceData>();
QString name;
for (const auto& inputEntry : deviceData.inputProfile.inputEntries)
{
if (inputEntry.button != N64ControllerButton::Invalid)
{
for (const auto& buttonMapping : this->buttonWidgetMappings)
{
if (buttonMapping.button == inputEntry.button)
{
buttonMapping.buttonWidget->Clear();
for (int i = 0; i < inputEntry.data.size(); i++)
{
if (inputEntry.inputTypes[i] == InputType::GamepadButton)
{
name = UICommon::GetGamepadButtonText(this->currentGamepad, static_cast<SDL_GamepadButton>(inputEntry.data[i]));
}
else
{
name = inputEntry.names[i];
}
buttonMapping.buttonWidget->AddInputData(inputEntry.inputTypes[i], inputEntry.data[i],
inputEntry.extraData[i], name);
}
break;
}
}
}
else if (inputEntry.axis != InputAxisDirection::Invalid)
{
for (const auto& axisMapping : this->joystickWidgetMappings)
{
if (axisMapping.direction == inputEntry.axis)
{
axisMapping.buttonWidget->Clear();
for (int i = 0; i < inputEntry.data.size(); i++)
{
axisMapping.buttonWidget->AddInputData(inputEntry.inputTypes[i], inputEntry.data[i],
inputEntry.extraData[i], inputEntry.names[i]);
}
break;
}
}
}
}
}
void ControllerWidget::on_resetButton_clicked()
{
const QString section = this->getCurrentSettingsSection();
@@ -885,12 +1006,12 @@ void ControllerWidget::on_resetButton_clicked()
void ControllerWidget::on_optionsButton_clicked()
{
InputDevice device = this->inputDeviceComboBox->currentData().value<InputDevice>();
const bool isKeyboard = device.type == InputDeviceType::Keyboard;
inputDeviceData deviceData = this->inputDeviceComboBox->currentData().value<inputDeviceData>();
const bool isKeyboard = deviceData.device.type == InputDeviceType::Keyboard;
OptionsDialog dialog(this, this->optionsDialogSettings,
isKeyboard ? nullptr : this->currentJoystick,
isKeyboard ? nullptr : this->currentController);
isKeyboard ? nullptr : this->currentGamepad);
int ret = dialog.exec();
// when saved, store settings for later
@@ -903,8 +1024,7 @@ void ControllerWidget::on_optionsButton_clicked()
void ControllerWidget::on_hotkeysButton_clicked()
{
HotkeysDialog dialog(this, this->hotkeySettingMappings,
this->isCurrentJoystickGameController,
this->currentJoystickId,
this->currentJoystickId, this->currentGamepad,
this->optionsDialogSettings.FilterEventsForButtons,
this->optionsDialogSettings.RemoveDuplicateMappings);
@@ -1039,7 +1159,7 @@ void ControllerWidget::on_MainDialog_SdlEvent(SDL_Event* event)
if ((event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) ||
(event->type == SDL_EVENT_GAMEPAD_BUTTON_UP))
{ // gamepad button
if (!this->isCurrentJoystickGameController &&
if (!this->isCurrentJoystickGamepad &&
this->optionsDialogSettings.FilterEventsForButtons)
{
return;
@@ -1049,12 +1169,12 @@ void ControllerWidget::on_MainDialog_SdlEvent(SDL_Event* event)
inputType = InputType::GamepadButton;
sdlButton = event->gbutton.button;
sdlButtonPressed = (event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN);
sdlButtonName = SDL_GetGamepadStringForButton((SDL_GamepadButton)sdlButton);
sdlButtonName = UICommon::GetGamepadButtonText(this->currentGamepad, static_cast<SDL_GamepadButton>(sdlButton));
}
else if ((event->type == SDL_EVENT_JOYSTICK_BUTTON_DOWN) ||
(event->type == SDL_EVENT_JOYSTICK_BUTTON_UP))
{ // joystick button
if (this->isCurrentJoystickGameController &&
if (this->isCurrentJoystickGamepad &&
this->optionsDialogSettings.FilterEventsForButtons)
{
return;
@@ -1154,7 +1274,7 @@ void ControllerWidget::on_MainDialog_SdlEvent(SDL_Event* event)
bool sdlHatCentered = (sdlHatDirection == SDL_HAT_CENTERED);
QString sdlHatName = "hat " + QString::number(sdlHat) + ":" + QString::number(sdlHatDirection);
if (this->isCurrentJoystickGameController &&
if (this->isCurrentJoystickGamepad &&
this->optionsDialogSettings.FilterEventsForButtons)
{
return;
@@ -1293,7 +1413,7 @@ void ControllerWidget::on_MainDialog_SdlEvent(SDL_Event* event)
if (event->type == SDL_EVENT_GAMEPAD_AXIS_MOTION)
{ // gamepad axis
if (!this->isCurrentJoystickGameController &&
if (!this->isCurrentJoystickGamepad &&
this->optionsDialogSettings.FilterEventsForAxis)
{
return;
@@ -1303,12 +1423,12 @@ void ControllerWidget::on_MainDialog_SdlEvent(SDL_Event* event)
inputType = InputType::GamepadAxis;
sdlAxis = event->gaxis.axis;
sdlAxisValue = event->gaxis.value;
sdlAxisName = SDL_GetGamepadStringForAxis((SDL_GamepadAxis)sdlAxis);
sdlAxisName = SDL_GetGamepadStringForAxis(static_cast<SDL_GamepadAxis>(sdlAxis));
sdlAxisName += sdlAxisValue > 0 ? "+" : "-";
}
else
{ // joystick axis
if (this->isCurrentJoystickGameController &&
if (this->isCurrentJoystickGamepad &&
this->optionsDialogSettings.FilterEventsForAxis)
{
return;
@@ -1419,7 +1539,7 @@ void ControllerWidget::on_MainDialog_SdlEvent(SDL_Event* event)
break;
}
const SDL_Scancode sdlButton = (SDL_Scancode)event->key.scancode;
const SDL_Scancode sdlButton = static_cast<SDL_Scancode>(event->key.scancode);
const bool sdlButtonPressed = (event->type == SDL_EVENT_KEY_DOWN);
// handle button widget
@@ -1508,8 +1628,8 @@ void ControllerWidget::on_MainDialog_SdlEventPollFinished()
bool ControllerWidget::IsPluggedIn()
{
const InputDevice device = this->inputDeviceComboBox->currentData().value<InputDevice>();
return device.type != InputDeviceType::None;
const inputDeviceData deviceData = this->inputDeviceComboBox->currentData().value<inputDeviceData>();
return deviceData.device.type != InputDeviceType::None;
}
void ControllerWidget::SetOnlyLoadGameProfile(bool value, CoreRomHeader romHeader, CoreRomSettings romSettings)
@@ -1532,7 +1652,7 @@ void ControllerWidget::SetSettingsSection(QString profile, QString section)
this->settingsSection = section;
if (!this->onlyLoadGameProfile)
{
this->profileComboBox->addItem(profile, section);
this->addProfile(profile, section);
}
}
@@ -1570,14 +1690,18 @@ void ControllerWidget::LoadSettings()
this->gameSection = section + " Game " + QString::fromStdString(romSettings.MD5);
// use internal rom name
QString internalName = QString::fromStdString(romHeader.Name);
QString name = QString::fromStdString(romHeader.Name);
if (name.isEmpty())
{ // fallback to partial MD5 hash name
name = QString::fromStdString(romSettings.MD5).left(10);
}
// add game specific profile when
// it doesn't exist yet
int index = this->profileComboBox->findData(this->gameSection);
if (index == -1)
{
this->profileComboBox->addItem(internalName, this->gameSection);
this->addProfile(name, this->gameSection);
}
// if a game specific section exists,
@@ -1594,7 +1718,7 @@ void ControllerWidget::LoadSettings()
!CoreSettingsKeyExists(this->gameSection.toStdString(), "UseGameProfile") ||
CoreSettingsGetBoolValue(SettingsID::Input_UseGameProfile, this->gameSection.toStdString()))
{
this->profileComboBox->setCurrentText(internalName);
this->profileComboBox->setCurrentText(name);
// only use game section if it exists
if (CoreSettingsSectionExists(this->gameSection.toStdString()))
@@ -1628,7 +1752,7 @@ void ControllerWidget::LoadSettings()
int index = this->profileComboBox->findData(profileSection);
if (index == -1)
{
this->profileComboBox->addItem(QString::fromStdString(profile), profileSection);
this->addProfile(QString::fromStdString(profile), profileSection);
}
}
}
@@ -1714,7 +1838,7 @@ void ControllerWidget::LoadSettings(QString sectionQString, bool loadUserProfile
// add all input data
for (int i = 0; i < count; i++)
{
buttonSetting.button->AddInputData((InputType)types[i], data[i], extraData[i], QString::fromStdString(names.at(i)));
buttonSetting.button->AddInputData(static_cast<InputType>(types[i]), data[i], extraData[i], QString::fromStdString(names.at(i)));
}
}
@@ -1939,20 +2063,25 @@ void ControllerWidget::RevertSettings()
}
}
void ControllerWidget::SetCurrentJoystickID(SDL_JoystickID joystickId)
void ControllerWidget::SetCurrentInputDevice(SDL_JoystickID joystickId, SDL_Joystick* joystick,
SDL_Gamepad* gamepad, const InputProfileDBEntry& inputProfile)
{
this->currentJoystickId = joystickId;
}
this->currentJoystick = joystick;
this->currentGamepad = gamepad;
this->isCurrentJoystickGamepad = (gamepad != nullptr);
void ControllerWidget::SetIsCurrentJoystickGameController(bool isGameController)
{
this->isCurrentJoystickGameController = isGameController;
}
// we can enable the auto-configure button when we have a gamepad
this->autoConfigureButton->setEnabled(gamepad != nullptr);
void ControllerWidget::SetCurrentJoystick(SDL_Joystick* joystick, SDL_Gamepad* controller)
{
this->currentJoystick = joystick;
this->currentController = controller;
// update input profile when needed
if (inputProfile.valid)
{
int currentIndex = this->inputDeviceComboBox->currentIndex();
inputDeviceData deviceData = this->inputDeviceComboBox->itemData(currentIndex).value<inputDeviceData>();
deviceData.inputProfile = inputProfile;
this->inputDeviceComboBox->setItemData(currentIndex, QVariant::fromValue<inputDeviceData>(deviceData));
}
}
void ControllerWidget::AddUserProfile(QString name, QString section)
@@ -1960,7 +2089,7 @@ void ControllerWidget::AddUserProfile(QString name, QString section)
int index = this->profileComboBox->findData(section);
if (index == -1)
{
this->profileComboBox->addItem(name, section);
this->addProfile(name, section);
}
}
@@ -16,6 +16,7 @@
#include "UserInterface/OptionsDialog.hpp"
#include "UserInterface/HotkeysDialog.hpp"
#include "UserInterface/EventFilter.hpp"
#include "Utilities/InputProfileDB.hpp"
using namespace UserInterface::Widget;
@@ -43,7 +44,6 @@ private:
OptionsDialogSettings optionsDialogSettings;
QList<QString> inputDeviceNameList;
MappingButton* currentButton = nullptr;
bool addMappingToButton = false;
@@ -51,6 +51,8 @@ private:
QList<QString> removedProfiles;
QList<QString> addedProfiles;
bool autoConfigureButtonEnabled = false;
struct buttonWidgetMapping
{
enum N64ControllerButton button;
@@ -72,6 +74,12 @@ private:
SettingsID extraDataSettingsId;
};
struct inputDeviceData
{
InputDevice device;
InputProfileDBEntry inputProfile;
};
QList<buttonWidgetMapping> buttonWidgetMappings;
QList<axisWidgetMapping> joystickWidgetMappings;
QList<buttonSettingMapping> buttonSettingMappings;
@@ -85,6 +93,8 @@ private:
bool isCurrentDeviceKeyboard();
bool isCurrentDeviceNotFound();
bool disabledAllChildren = false;
void disableAllChildren();
void enableAllChildren();
@@ -101,13 +111,15 @@ private:
bool hasAnySettingChanged(QString section);
void addProfile(const QString& profile, const QString& section);
void showErrorMessage(QString text, QString details = "");
SDL_JoystickID currentJoystickId = -1;
bool isCurrentJoystickGameController = false;
SDL_JoystickID currentJoystickId = -1;
bool isCurrentJoystickGamepad = false;
SDL_Joystick* currentJoystick = nullptr;
SDL_Gamepad* currentController = nullptr;
SDL_Joystick* currentJoystick = nullptr;
SDL_Gamepad* currentGamepad = nullptr;
int previousProfileComboBoxIndex = -1;
@@ -123,7 +135,7 @@ public:
ControllerWidget(QWidget* parent, EventFilter* eventFilter);
~ControllerWidget();
void AddInputDevice(const InputDevice& device);
void AddInputDevice(const InputDevice& device, const InputProfileDBEntry& inputProfile);
void RemoveInputDevice(const InputDevice& device);
void CheckInputDeviceSettings();
void CheckInputDeviceSettings(QString section);
@@ -151,9 +163,8 @@ public:
void RevertSettings();
void SetCurrentJoystickID(SDL_JoystickID joystickId);
void SetIsCurrentJoystickGameController(bool isGameController);
void SetCurrentJoystick(SDL_Joystick* joystick, SDL_Gamepad* controller);
void SetCurrentInputDevice(SDL_JoystickID joystickId, SDL_Joystick* joystick,
SDL_Gamepad* gamepad, const InputProfileDBEntry& inputProfile);
void AddUserProfile(QString name, QString section);
void RemoveUserProfile(QString name, QString section);
@@ -170,6 +181,7 @@ private slots:
void on_addProfileButton_clicked();
void on_removeProfileButton_clicked();
void on_autoConfigureButton_clicked();
void on_resetButton_clicked();
void on_optionsButton_clicked();
void on_hotkeysButton_clicked();
@@ -19,7 +19,7 @@
<property name="windowTitle">
<string>ControllerWidget</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
<layout class="QVBoxLayout" name="mainLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
@@ -70,19 +70,6 @@
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_8">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="inputDeviceGroupBox">
<property name="sizePolicy">
@@ -124,19 +111,6 @@
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="deadZoneGroupBox">
<property name="title">
@@ -175,19 +149,6 @@
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
@@ -1253,6 +1214,13 @@
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="autoConfigureButton">
<property name="text">
<string>Auto-Configure</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="resetButton">
<property name="text">
@@ -1276,19 +1244,6 @@
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
@@ -31,7 +31,7 @@ void HotkeyButton::Initialize(HotkeysDialog* dialog)
{
this->hotkeysDialog = dialog;
connect(this, &QPushButton::released, [=, this]
connect(this, &QPushButton::released, [this]
{
emit this->hotkeysDialog->on_HotkeyButton_Released(this);
});
@@ -173,6 +173,7 @@ void HotkeyButton::on_countDownTimer_triggered()
this->currentCountDownNum = 5;
this->countDownTimer->stop();
emit this->hotkeysDialog->on_HotkeyButton_TimerFinished(this);
return;
}
this->currentCountDownNum--;
@@ -31,7 +31,7 @@ void MappingButton::Initialize(ControllerWidget* widget)
{
this->controllerWidget = widget;
connect(this, &QPushButton::released, [=, this]
connect(this, &QPushButton::released, [this]
{
emit this->controllerWidget->on_MappingButton_Released(this);
});
@@ -26,7 +26,7 @@ void RemoveMappingButton::Initialize(ControllerWidget* widget, MappingButton* bu
this->controllerWidget = widget;
this->mappingButton = button;
connect(this, &QPushButton::released, [=, this]
connect(this, &QPushButton::released, [this]
{
emit this->controllerWidget->on_RemoveMappingButton_Released(this->mappingButton);
});
@@ -0,0 +1,158 @@
/*
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
* Copyright (C) 2020-2025 Rosalie Wanders <rosalie@mailbox.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "InputProfileDB.hpp"
#include "main.hpp"
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QFile>
#include <RMG-Core/m64p/api/m64p_types.h>
#include <RMG-Core/Directories.hpp>
//
// Public Functions
//
bool Utilities::InputProfileDB::Load(void)
{
std::filesystem::path profileDatabaseFilePath;
profileDatabaseFilePath = CoreGetSharedDataDirectory();
profileDatabaseFilePath += "/InputProfileDB.json";
QFile jsonFile(profileDatabaseFilePath);
if (!jsonFile.open(QFile::ReadOnly))
{
std::string message = "InputProfileDB::Load(): Failed to open DB";
PluginDebugMessage(M64MSG_WARNING, message);
return false;
}
QJsonParseError error;
QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonFile.readAll(), &error);
if (error.error != QJsonParseError::NoError)
{
std::string message = "InputProfileDB::Load(): Failed to load json: ";
message += error.errorString().toStdString();
PluginDebugMessage(M64MSG_WARNING, message);
return false;
}
const struct
{
N64ControllerButton button;
InputAxisDirection axis;
QString baseKey;
} jsonButtonMappings[] =
{
{ N64ControllerButton::A, InputAxisDirection::Invalid, "A_" },
{ N64ControllerButton::B, InputAxisDirection::Invalid, "B_" },
{ N64ControllerButton::Start, InputAxisDirection::Invalid, "Start_" },
{ N64ControllerButton::DpadUp, InputAxisDirection::Invalid, "DpadUp_" },
{ N64ControllerButton::DpadDown, InputAxisDirection::Invalid, "DpadDown_" },
{ N64ControllerButton::DpadLeft, InputAxisDirection::Invalid, "DpadLeft_" },
{ N64ControllerButton::DpadRight, InputAxisDirection::Invalid, "DpadRight_" },
{ N64ControllerButton::CButtonUp, InputAxisDirection::Invalid, "CButtonUp_" },
{ N64ControllerButton::CButtonDown, InputAxisDirection::Invalid, "CButtonDown_" },
{ N64ControllerButton::CButtonLeft, InputAxisDirection::Invalid, "CButtonLeft_" },
{ N64ControllerButton::CButtonRight, InputAxisDirection::Invalid, "CButtonRight_" },
{ N64ControllerButton::LeftShoulder, InputAxisDirection::Invalid, "LeftTrigger_", },
{ N64ControllerButton::RightShoulder, InputAxisDirection::Invalid, "RightTrigger_" },
{ N64ControllerButton::ZTrigger, InputAxisDirection::Invalid, "ZTrigger_" },
{ N64ControllerButton::Invalid, InputAxisDirection::Up, "AnalogStickUp_" },
{ N64ControllerButton::Invalid, InputAxisDirection::Down, "AnalogStickDown_" },
{ N64ControllerButton::Invalid, InputAxisDirection::Left, "AnalogStickLeft_" },
{ N64ControllerButton::Invalid, InputAxisDirection::Right, "AnalogStickRight_" },
};
InputProfileDBEntry entry;
InputProfileDBInputEntry inputEntry;
for (const auto& arrayValue : jsonDocument.array())
{
// reset entry state
entry = {};
entry.valid = true;
QJsonObject jsonObject = arrayValue.toObject();
QJsonArray deviceNameArray = jsonObject["DeviceName"].toArray();
for (const auto& deviceName : deviceNameArray)
{
entry.deviceNames.push_back(deviceName.toString());
}
entry.deviceType = static_cast<InputDeviceType>(jsonObject["DeviceType"].toInt());
// add all button mappings
for (const auto& buttonMapping : jsonButtonMappings)
{
// reset input entry state
inputEntry = {};
QStringList inputTypes = jsonObject[buttonMapping.baseKey + "InputType"].toString().split(';');
QStringList names = jsonObject[buttonMapping.baseKey + "Name"].toString().split(';');
QStringList data = jsonObject[buttonMapping.baseKey + "Data"].toString().split(';');
QStringList extraData = jsonObject[buttonMapping.baseKey + "ExtraData"].toString().split(';');
inputEntry.button = buttonMapping.button;
inputEntry.axis = buttonMapping.axis;
inputEntry.names = names;
size_t size = std::min(inputTypes.size(), std::min(names.size(), std::min(data.size(), extraData.size())));
for (int i = 0; i < size; i++)
{
inputEntry.inputTypes.push_back(static_cast<InputType>(inputTypes[i].toInt()));
inputEntry.data.push_back(data[i].toInt());
inputEntry.extraData.push_back(extraData[i].toInt());
}
entry.inputEntries.push_back(inputEntry);
}
// add to list
this->entries.push_back(entry);
}
return true;
}
InputProfileDBEntry Utilities::InputProfileDB::FindEntry(const InputDevice& device, bool fallback)
{
QString deviceName = QString::fromStdString(device.name);
for (const auto& dbEntry : this->entries)
{
if (dbEntry.deviceType == device.type)
{
for (const auto& name : dbEntry.deviceNames)
{
if (name == deviceName)
{
return dbEntry;
}
}
}
}
if (fallback)
{
return this->FindEntry({ InputDeviceType::Joystick, "fallback_profile" }, false);
}
// nothing found, return invalid entry
return { false };
}
@@ -0,0 +1,49 @@
#ifndef INPUTPROFILEDB_HPP
#define INPUTPROFILEDB_HPP
#include <QStringList>
#include <QString>
#include <QList>
#include "common.hpp"
struct InputProfileDBInputEntry
{
N64ControllerButton button;
InputAxisDirection axis;
QList<InputType> inputTypes;
QStringList names;
QList<int> data;
QList<int> extraData;
};
struct InputProfileDBEntry
{
bool valid = false;
QStringList deviceNames;
InputDeviceType deviceType;
QList<InputProfileDBInputEntry> inputEntries;
};
namespace Utilities
{
class InputProfileDB
{
private:
QList<InputProfileDBEntry> entries;
public:
// attempts to load DB in memory
bool Load(void);
// attempts to find entry using device name and type
InputProfileDBEntry FindEntry(const InputDevice& device, bool fallback = true);
};
} // namespace Utilities
#endif // INPUTPROFILEDB_HPP
@@ -0,0 +1,53 @@
/*
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
* Copyright (C) 2020-2025 Rosalie Wanders <rosalie@mailbox.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "Sdl3ButtonLabelToString.hpp"
#include <SDL3/SDL.h>
using namespace Utilities;
QString Utilities::Sdl3ButtonLabelToString(SDL_GamepadButtonLabel label)
{
QString buttonName;
switch (label)
{
case SDL_GAMEPAD_BUTTON_LABEL_A:
buttonName = "a";
break;
case SDL_GAMEPAD_BUTTON_LABEL_B:
buttonName = "b";
break;
case SDL_GAMEPAD_BUTTON_LABEL_X:
buttonName = "x";
break;
case SDL_GAMEPAD_BUTTON_LABEL_Y:
buttonName = "y";
break;
case SDL_GAMEPAD_BUTTON_LABEL_CROSS:
buttonName = "cross";
break;
case SDL_GAMEPAD_BUTTON_LABEL_CIRCLE:
buttonName = "circle";
break;
case SDL_GAMEPAD_BUTTON_LABEL_SQUARE:
buttonName = "square";
break;
case SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE:
buttonName = "triangle";
break;
default:
break;
}
return buttonName;
}
@@ -0,0 +1,12 @@
#ifndef SDL3BUTTONLABELTOSTRING_HPP
#define SDL3BUTTONLABELTOSTRING_HPP
#include <SDL3/SDL.h>
#include <QString>
namespace Utilities
{
QString Sdl3ButtonLabelToString(SDL_GamepadButtonLabel label);
} // namespace Utilities
#endif // SDL3BUTTONLABELTOSTRING_HPP
+9 -9
View File
@@ -111,14 +111,14 @@ static bool hook_vosk(void)
return false;
}
l_vosk_model_new = (ptr_vosk_model_new) CoreGetLibrarySymbol(l_VoskLibHandle, "vosk_model_new");
l_vosk_model_free = (ptr_vosk_model_free) CoreGetLibrarySymbol(l_VoskLibHandle, "vosk_model_free");
l_vosk_recognizer_new_grm = (ptr_vosk_recognizer_new_grm) CoreGetLibrarySymbol(l_VoskLibHandle, "vosk_recognizer_new_grm");
l_vosk_recognizer_free = (ptr_vosk_recognizer_free) CoreGetLibrarySymbol(l_VoskLibHandle, "vosk_recognizer_free");
l_vosk_recognizer_accept_waveform = (ptr_vosk_recognizer_accept_waveform) CoreGetLibrarySymbol(l_VoskLibHandle, "vosk_recognizer_accept_waveform");
l_vosk_recognizer_final_result = (ptr_vosk_recognizer_final_result) CoreGetLibrarySymbol(l_VoskLibHandle, "vosk_recognizer_final_result");
l_vosk_set_log_level = (ptr_vosk_set_log_level) CoreGetLibrarySymbol(l_VoskLibHandle, "vosk_set_log_level");
l_vosk_recognizer_set_max_alternatives = (ptr_vosk_recognizer_set_max_alternatives) CoreGetLibrarySymbol(l_VoskLibHandle, "vosk_recognizer_set_max_alternatives");
l_vosk_model_new = reinterpret_cast<ptr_vosk_model_new>(CoreGetLibrarySymbol(l_VoskLibHandle, "vosk_model_new"));
l_vosk_model_free = reinterpret_cast<ptr_vosk_model_free>(CoreGetLibrarySymbol(l_VoskLibHandle, "vosk_model_free"));
l_vosk_recognizer_new_grm = reinterpret_cast<ptr_vosk_recognizer_new_grm>(CoreGetLibrarySymbol(l_VoskLibHandle, "vosk_recognizer_new_grm"));
l_vosk_recognizer_free = reinterpret_cast<ptr_vosk_recognizer_free>(CoreGetLibrarySymbol(l_VoskLibHandle, "vosk_recognizer_free"));
l_vosk_recognizer_accept_waveform = reinterpret_cast<ptr_vosk_recognizer_accept_waveform>(CoreGetLibrarySymbol(l_VoskLibHandle, "vosk_recognizer_accept_waveform"));
l_vosk_recognizer_final_result = reinterpret_cast<ptr_vosk_recognizer_final_result>(CoreGetLibrarySymbol(l_VoskLibHandle, "vosk_recognizer_final_result"));
l_vosk_set_log_level = reinterpret_cast<ptr_vosk_set_log_level>(CoreGetLibrarySymbol(l_VoskLibHandle, "vosk_set_log_level"));
l_vosk_recognizer_set_max_alternatives = reinterpret_cast<ptr_vosk_recognizer_set_max_alternatives>(CoreGetLibrarySymbol(l_VoskLibHandle, "vosk_recognizer_set_max_alternatives"));
if (l_vosk_model_new == nullptr ||
l_vosk_model_free == nullptr ||
@@ -301,7 +301,7 @@ static QByteArray words_to_byte_array(uint16_t* words, uint16_t size)
{
QByteArray byte_array;
uint8_t* bytes = (uint8_t*)words;
uint8_t* bytes = reinterpret_cast<uint8_t*>(words);
for (int i = 0; i < (size * 2); i += 2)
{
+2 -1
View File
@@ -61,7 +61,8 @@ enum class InputAxisDirection
Up = 0,
Down,
Left,
Right
Right,
Invalid
};
enum class N64ControllerPak
+30 -14
View File
@@ -508,7 +508,7 @@ static void open_controller_automatic(int index, InputProfile* profile, SDL_Joys
std::string debugMessage;
std::string debugMessageBase;
debugMessageBase = "setup_device_automatic(";
debugMessageBase = "open_controller_automatic(";
debugMessageBase += std::to_string(index);
debugMessageBase += "): ";
@@ -518,7 +518,6 @@ static void open_controller_automatic(int index, InputProfile* profile, SDL_Joys
if (SDL_IsGamepad(joystickId))
{
profile->SDLJoystick = SDL_OpenJoystick(joystickId);
profile->SDLGamepad = SDL_OpenGamepad(joystickId);
if (profile->SDLGamepad == nullptr)
{
@@ -528,6 +527,14 @@ static void open_controller_automatic(int index, InputProfile* profile, SDL_Joys
continue;
}
profile->SDLJoystick = SDL_GetGamepadJoystick(profile->SDLGamepad);
if (profile->SDLJoystick == nullptr)
{
debugMessage = debugMessageBase + "SDL_GetGamepadJoystick Failed: ";
debugMessage += SDL_GetError();
PluginDebugMessage(M64MSG_WARNING, debugMessage);
}
debugMessage = debugMessageBase + "found gamepad: ";
debugMessage += string_from_const_char(SDL_GetGamepadName(profile->SDLGamepad));
PluginDebugMessage(M64MSG_VERBOSE, debugMessage);
@@ -599,8 +606,7 @@ static void open_controller(InputProfile* profile, SDL_JoystickID* joysticks, in
if (SDL_IsGamepad(joystickId))
{
joystick = SDL_OpenJoystick(joystickId);
gamepad = SDL_OpenGamepad(joystickId);
gamepad = SDL_OpenGamepad(joystickId);
if (gamepad == nullptr)
{
errorMessage = "open_controller(): SDL_OpenGamepad Failed: ";
@@ -608,6 +614,14 @@ static void open_controller(InputProfile* profile, SDL_JoystickID* joysticks, in
PluginDebugMessage(M64MSG_ERROR, errorMessage);
continue;
}
joystick = SDL_GetGamepadJoystick(gamepad);
if (joystick == nullptr)
{
errorMessage = "open_controller(): SDL_GetGamepadJoystick Failed: ";
errorMessage += SDL_GetError();
PluginDebugMessage(M64MSG_WARNING, errorMessage);
}
deviceName = string_from_const_char(SDL_GetGamepadName(gamepad));
devicePath = string_from_const_char(SDL_GetGamepadPath(gamepad));
@@ -639,30 +653,32 @@ static void open_controller(InputProfile* profile, SDL_JoystickID* joysticks, in
return;
}
if (joystick != nullptr)
{
SDL_CloseJoystick(joystick);
joystick = nullptr;
}
if (gamepad != nullptr)
{
SDL_CloseGamepad(gamepad);
gamepad = nullptr;
joystick = nullptr; // owned by SDL3
}
else if (joystick != nullptr)
{
SDL_CloseJoystick(joystick);
joystick = nullptr;
}
}
}
static void close_controller(InputProfile* profile)
{
if (profile->SDLJoystick != nullptr)
{
SDL_CloseJoystick(profile->SDLJoystick);
profile->SDLJoystick = nullptr;
}
if (profile->SDLGamepad != nullptr)
{
SDL_CloseGamepad(profile->SDLGamepad);
profile->SDLGamepad = nullptr;
profile->SDLJoystick = nullptr; // owned by SDL3
}
else if (profile->SDLJoystick != nullptr)
{
SDL_CloseJoystick(profile->SDLJoystick);
profile->SDLJoystick = nullptr;
}
}
+8 -3
View File
@@ -24,9 +24,10 @@ if (APPIMAGE_UPDATER)
add_definitions(-DAPPIMAGE_UPDATER)
endif(APPIMAGE_UPDATER)
if (DRAG_DROP)
add_definitions(-DDRAG_DROP)
endif(DRAG_DROP)
if (KCA_DRAG_DROP)
find_package(KF6CoreAddons REQUIRED)
add_definitions(-DKCA_DRAG_DROP)
endif(KCA_DRAG_DROP)
if (NETPLAY)
find_package(Qt6 COMPONENTS WebSockets REQUIRED)
@@ -148,6 +149,10 @@ if (UPDATER)
target_link_libraries(RMG Qt6::Network)
endif(UPDATER)
if (KCA_DRAG_DROP)
target_link_libraries(RMG KF6::CoreAddons)
endif(KCA_DRAG_DROP)
if (NETPLAY)
target_link_libraries(RMG Qt6::WebSockets)
endif(NETPLAY)
@@ -93,8 +93,8 @@ li.checked::marker { content: &quot;\2612&quot;; }
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;- Jaffacakelover (&lt;a href=&quot;https://github.com/Jaffacakelover&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#1d99f3;&quot;&gt;https://github.com/Jaffacakelover&lt;/span&gt;&lt;/a&gt;) for making the controller image for RMG-Input&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;It uses the following third party projects:&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;- discord-rpc (&lt;a href=&quot;https://github.com/discord/discord-rpc&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#1d99f3;&quot;&gt;https://github.com/discord/discord-rpc&lt;/span&gt;&lt;/a&gt;)&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;- fmt (&lt;a href=&quot;https://github.com/fmtlib/fmt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#1d99f3;&quot;&gt;https://github.com/fmtlib/fmt&lt;/span&gt;&lt;/a&gt;)&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;- Qt6 (&lt;a href=&quot;https://www.qt.io/product/qt6&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#1d99f3;&quot;&gt;https://www.qt.io/product/qt6&lt;/span&gt;&lt;/a&gt;)&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;- SDL3 (&lt;a href=&quot;https://github.com/libsdl-org/SDL&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#1d99f3;&quot;&gt;https://github.com/libsdl-org/SDL&lt;/span&gt;&lt;/a&gt;)&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;- imgui (&lt;a href=&quot;https://github.com/ocornut/imgui&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#1d99f3;&quot;&gt;https://github.com/ocornut/imgui&lt;/span&gt;&lt;/a&gt;)&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;- lzma (&lt;a href=&quot;https://7-zip.org/sdk.html&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#1d99f3;&quot;&gt;https://7-zip.org/sdk.html&lt;/span&gt;&lt;/a&gt;)&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;- vosk-api (&lt;a href=&quot;https://github.com/alphacep/vosk-api&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#1d99f3;&quot;&gt;https://github.com/alphacep/vosk-api&lt;/span&gt;&lt;/a&gt;)&lt;/p&gt;
@@ -158,7 +158,7 @@ bool CheatsCommon::GetCheatOption(bool netplay, const QJsonArray& json, QString
return CoreGetCheatOption(file.toStdU32String(), cheat, option);
}
}
bool CheatsCommon::SetCheatOption(bool netplay, QJsonArray& json, QString file, const CoreCheat& cheat, CoreCheatOption& option)
bool CheatsCommon::SetCheatOption(bool netplay, QJsonArray& json, QString file, const CoreCheat& cheat, const CoreCheatOption& option)
{
if (netplay)
{
@@ -195,7 +195,7 @@ bool CheatsCommon::SetCheatOption(bool netplay, QJsonArray& json, QString file,
}
else
{
return CoreGetCheatOption(file.toStdU32String(), cheat, option);
return CoreSetCheatOption(file.toStdU32String(), cheat, option);
}
}
@@ -31,7 +31,7 @@ namespace CheatsCommon
bool GetCheatOption(bool netplay, const QJsonArray& json, QString file, const CoreCheat& cheat, CoreCheatOption& option);
// Sets cheat option
bool SetCheatOption(bool netplay, QJsonArray& json, QString file, const CoreCheat& cheat, CoreCheatOption& option);
bool SetCheatOption(bool netplay, QJsonArray& json, QString file, const CoreCheat& cheat, const CoreCheatOption& option);
// Parses json into cheat list
bool ParseCheatJson(const QJsonArray& json, std::vector<CoreCheat>& cheats);
@@ -23,10 +23,10 @@
</sizepolicy>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAsNeeded</enum>
<enum>Qt::ScrollBarPolicy::ScrollBarAsNeeded</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContentsOnFirstShow</enum>
<enum>QAbstractScrollArea::SizeAdjustPolicy::AdjustToContentsOnFirstShow</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
@@ -46,7 +46,7 @@
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@@ -61,6 +61,9 @@
<property name="text">
<string>Add</string>
</property>
<property name="icon">
<iconset theme="add-line"/>
</property>
</widget>
</item>
<item>
@@ -71,6 +74,9 @@
<property name="text">
<string>Edit</string>
</property>
<property name="icon">
<iconset theme="pencil-line"/>
</property>
</widget>
</item>
<item>
@@ -81,12 +87,15 @@
<property name="text">
<string>Remove</string>
</property>
<property name="icon">
<iconset theme="delete-bin-line"/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@@ -125,7 +134,7 @@
</size>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContentsOnFirstShow</enum>
<enum>QAbstractScrollArea::SizeAdjustPolicy::AdjustToContentsOnFirstShow</enum>
</property>
<property name="readOnly">
<bool>true</bool>
@@ -138,10 +147,10 @@
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
<set>QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
@@ -114,10 +114,7 @@ CreateNetplaySessionDialog::CreateNetplaySessionDialog(QWidget *parent, QWebSock
connect(networkAccessManager, &QNetworkAccessManager::finished, this, &CreateNetplaySessionDialog::on_dispatcherRegionListDownload_Finished);
networkAccessManager->setTransferTimeout(15000);
QNetworkRequest networkRequest(QUrl(this->dispatcherUrl + "/getRegions"));
networkRequest.setRawHeader("netplay-id", "RMG");
networkAccessManager->get(networkRequest);
networkAccessManager->get(NetplayCommon::GetNetworkRequest(this->dispatcherUrl + "/getRegions"));
}
}
@@ -422,9 +419,7 @@ void CreateNetplaySessionDialog::accept()
urlQuery.addQueryItem("region", NetplayCommon::GetServerData(this->serverComboBox));
url.setQuery(urlQuery);
QNetworkRequest networkRequest(url);
networkRequest.setRawHeader("netplay-id", "RMG");
networkAccessManager->get(networkRequest);
networkAccessManager->get(NetplayCommon::GetNetworkRequest(url));
// creating a server can take a while,
// so show a loading screen
@@ -87,7 +87,7 @@ QList<QString> NetplayCommon::GetPluginNames(QString md5QString)
return pluginNames;
}
void NetplayCommon::AddServers(QComboBox* comboBox, QJsonDocument document, bool dispatcher)
void NetplayCommon::AddServers(QComboBox* comboBox, const QJsonDocument& document, bool dispatcher)
{
if (dispatcher)
{
@@ -156,3 +156,10 @@ QString NetplayCommon::GetServerData(QComboBox* comboBox, int index)
return comboBox->itemData(index).value<NetplayServerData>().Data;
}
QNetworkRequest NetplayCommon::GetNetworkRequest(QUrl url)
{
QNetworkRequest networkRequest(url);
networkRequest.setRawHeader("netplay-id", "RMG");
return networkRequest;
}
@@ -14,6 +14,11 @@
#include <QJsonObject>
#include <QComboBox>
#include <QString>
#ifdef NETPLAY
#include <QNetworkRequest>
#include <QUrl>
#endif // NETPLAY
namespace NetplayCommon
{
@@ -34,7 +39,7 @@ namespace NetplayCommon
QList<QString> GetPluginNames(QString md5QString);
// Adds servers from json to combobox
void AddServers(QComboBox* comboBox, QJsonDocument document, bool dispatcher = false);
void AddServers(QComboBox* comboBox, const QJsonDocument& document, bool dispatcher = false);
// Restores previously selected server
void RestoreSelectedServer(QComboBox* comboBox);
@@ -44,6 +49,11 @@ namespace NetplayCommon
// Returns server data from comboBox
QString GetServerData(QComboBox* comboBox, int index = -1);
#ifdef NETPLAY
// Returns network request from url with emulator id
QNetworkRequest GetNetworkRequest(QUrl url);
#endif // NETPLAY
}
#endif // NETPLAYCOMMON_HPP
@@ -111,10 +111,7 @@ NetplaySessionBrowserDialog::NetplaySessionBrowserDialog(QWidget *parent, QWebSo
connect(networkAccessManager, &QNetworkAccessManager::finished, this, &NetplaySessionBrowserDialog::on_dispatcherRegionListDownload_Finished);
networkAccessManager->setTransferTimeout(15000);
QNetworkRequest networkRequest(QUrl(this->dispatcherUrl + "/getRegions"));
networkRequest.setRawHeader("netplay-id", "RMG");
networkAccessManager->get(networkRequest);
networkAccessManager->get(NetplayCommon::GetNetworkRequest(this->dispatcherUrl + "/getRegions"));
}
this->validateJoinButton();
@@ -198,7 +195,7 @@ void NetplaySessionBrowserDialog::validateJoinButton(void)
joinButton->setEnabled(this->validate());
}
void NetplaySessionBrowserDialog::toggleUI(bool enable, bool enableJoinButton)
void NetplaySessionBrowserDialog::toggleUI(bool enable, bool enableJoinButton, bool toggleOtherUI)
{
QPushButton* joinButton = this->buttonBox->button(QDialogButtonBox::Ok);
joinButton->setEnabled(enableJoinButton);
@@ -206,19 +203,17 @@ void NetplaySessionBrowserDialog::toggleUI(bool enable, bool enableJoinButton)
QPushButton* refreshButton = this->buttonBox->button(QDialogButtonBox::RestoreDefaults);
refreshButton->setEnabled(enable);
this->serverComboBox->setEnabled(enable);
this->nickNameLineEdit->setReadOnly(!enable);
if (toggleOtherUI)
{
this->serverComboBox->setEnabled(enable);
this->nickNameLineEdit->setReadOnly(!enable);
}
}
void NetplaySessionBrowserDialog::refreshSessions(void)
{
// disable refresh button while refreshing
QPushButton* refreshButton = this->buttonBox->button(QDialogButtonBox::RestoreDefaults);
refreshButton->setEnabled(false);
// disable join button while refreshing
QPushButton* joinButton = this->buttonBox->button(QDialogButtonBox::Ok);
joinButton->setEnabled(false);
// disable join and refresh button while refreshing
this->toggleUI(false, false, false);
if (NetplayCommon::IsServerDispatcher(this->serverComboBox))
{
@@ -515,14 +510,14 @@ void NetplaySessionBrowserDialog::on_dispatcherRetrieveServers_Finished(QNetwork
void NetplaySessionBrowserDialog::on_serverComboBox_currentIndexChanged(int index)
{
QPushButton* refreshButton = this->buttonBox->button(QDialogButtonBox::RestoreDefaults);
refreshButton->setEnabled(index != -1);
if (index == -1)
{
return;
}
// disable join and refresh button while refreshing
this->toggleUI(false, false, false);
bool dispatcher = NetplayCommon::IsServerDispatcher(this->serverComboBox, index);
if (this->pingTimerId != -1)
@@ -545,8 +540,7 @@ void NetplaySessionBrowserDialog::on_serverComboBox_currentIndexChanged(int inde
urlQuery.addQueryItem("region", region);
url.setQuery(urlQuery);
QNetworkRequest networkRequest(url);
networkRequest.setRawHeader("netplay-id", "RMG");
QNetworkRequest networkRequest = NetplayCommon::GetNetworkRequest(url);
// sadly we have to force HTTP/1 here due to a Qt bug,
// we abort the connection which sadly in Qt6 causes an error and eventually segfault,
@@ -65,7 +65,7 @@ class NetplaySessionBrowserDialog : public QDialog, private Ui::NetplaySessionBr
bool validate(void);
void validateJoinButton(void);
void toggleUI(bool enable, bool enableJoinButton);
void toggleUI(bool enable, bool enableJoinButton, bool toggleOtherUI = true);
void refreshSessions(void);
void joinSession(void);
@@ -14,7 +14,7 @@
using namespace UserInterface::Dialog;
RomInfoDialog::RomInfoDialog(QString file, CoreRomHeader romHeader, CoreRomSettings romSettings, QWidget *parent) : QDialog(parent)
RomInfoDialog::RomInfoDialog(QWidget *parent, QString file, CoreRomType romType, CoreRomHeader romHeader, CoreRomSettings romSettings) : QDialog(parent)
{
this->setupUi(this);
@@ -33,6 +33,7 @@ RomInfoDialog::RomInfoDialog(QString file, CoreRomHeader romHeader, CoreRomSetti
this->gameIDLineEdit->setText(QString::fromStdString(romHeader.GameID));
this->gameRegionLineEdit->setText(QString::fromStdString(romHeader.Region));
this->systemRegionLineEdit->setText(romHeader.SystemType == CoreSystemType::NTSC ? "NTSC" : "PAL");
this->gameTypeLineEdit->setText(romType == CoreRomType::Disk ? "Disk" : "Cartridge");
}
RomInfoDialog::~RomInfoDialog(void)
@@ -12,6 +12,7 @@
#include <RMG-Core/RomSettings.hpp>
#include <RMG-Core/RomHeader.hpp>
#include <RMG-Core/Rom.hpp>
#include <QWidget>
#include <QDialog>
@@ -27,7 +28,7 @@ class RomInfoDialog : public QDialog, private Ui::RomInfoDialog
Q_OBJECT
public:
RomInfoDialog(QString file, CoreRomHeader romHeader, CoreRomSettings romSettings, QWidget *parent);
RomInfoDialog(QWidget *parent, QString file, CoreRomType romType, CoreRomHeader romHeader, CoreRomSettings romSettings);
~RomInfoDialog(void);
};
} // namespace Dialog
@@ -242,6 +242,30 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QLabel" name="label_10">
<property name="text">
<string>Game Type</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="gameTypeLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
@@ -20,6 +20,7 @@
#include <QColorDialog>
#include <QDirIterator>
#include <QLabel>
#include <QDir>
#include <RMG-Core/CachedRomHeaderAndSettings.hpp>
#include <RMG-Core/Directories.hpp>
@@ -109,7 +110,7 @@ SettingsDialog::SettingsDialog(QWidget *parent, QString file) : QDialog(parent)
pluginList = CoreGetAllPlugins();
for (int i = 0; i < (int)SettingsDialogTab::Invalid; i++)
for (int i = 0; i < static_cast<int>(SettingsDialogTab::Invalid); i++)
{
this->loadSettings(i);
}
@@ -117,16 +118,8 @@ SettingsDialog::SettingsDialog(QWidget *parent, QString file) : QDialog(parent)
// connect hotkey settings to slot
this->commonHotkeySettings(SettingsDialogAction::ConnectSignals);
// set icons for certain buttons
this->clearJapaneseIPLRomPathButton->setText("");
this->clearJapaneseIPLRomPathButton->setIcon(QIcon::fromTheme("delete-bin-line"));
this->clearAmericanIPLRomPathButton->setText("");
this->clearAmericanIPLRomPathButton->setIcon(QIcon::fromTheme("delete-bin-line"));
this->clearDevelopmentIPLRomPathButton->setText("");
this->clearDevelopmentIPLRomPathButton->setIcon(QIcon::fromTheme("delete-bin-line"));
#ifndef NETPLAY
this->innerInterfaceTabWidget->setTabVisible((int)SettingsDialogTab::InterfaceNetplay, false);
this->innerInterfaceTabWidget->setTabVisible(static_cast<int>(SettingsDialogTab::InterfaceNetplay), false);
#endif // !NETPLAY
#ifndef UPDATER
@@ -172,12 +165,12 @@ int SettingsDialog::currentIndex(void)
void SettingsDialog::restoreDefaults(int stackedWidgetIndex)
{
if (stackedWidgetIndex < 0 ||
stackedWidgetIndex > (int)SettingsDialogTab::Invalid)
stackedWidgetIndex > static_cast<int>(SettingsDialogTab::Invalid))
{
return;
}
switch ((SettingsDialogTab)stackedWidgetIndex)
switch (static_cast<SettingsDialogTab>(stackedWidgetIndex))
{
default:
break;
@@ -229,12 +222,12 @@ void SettingsDialog::restoreDefaults(int stackedWidgetIndex)
void SettingsDialog::loadSettings(int stackedWidgetIndex)
{
if (stackedWidgetIndex < 0 ||
stackedWidgetIndex > (int)SettingsDialogTab::Invalid)
stackedWidgetIndex > static_cast<int>(SettingsDialogTab::Invalid))
{
return;
}
switch ((SettingsDialogTab)stackedWidgetIndex)
switch (static_cast<SettingsDialogTab>(stackedWidgetIndex))
{
default:
break;
@@ -377,7 +370,7 @@ void SettingsDialog::loadGamePluginSettings(void)
for (const auto &p : this->pluginList)
{
index = ((int)p.Type - 1);
index = (static_cast<int>(p.Type) - 1);
comboBox = comboBoxArray[index];
comboBox->addItem(QString::fromStdString(p.Name), QString::fromStdString(p.File));
@@ -418,16 +411,10 @@ void SettingsDialog::loadDirectorySettings(void)
const std::string screenshotDir = CoreSettingsGetStringValue(SettingsID::Core_ScreenshotPath);
const std::string saveStateDir = CoreSettingsGetStringValue(SettingsID::Core_SaveStatePath);
const std::string saveSramDir = CoreSettingsGetStringValue(SettingsID::Core_SaveSRAMPath);
const bool overrideUserDirs = CoreSettingsGetBoolValue(SettingsID::Core_OverrideUserDirs);
const std::string userDataDir = CoreSettingsGetStringValue(SettingsID::Core_UserDataDirOverride);
const std::string userCacheDir = CoreSettingsGetStringValue(SettingsID::Core_UserCacheDirOverride);
this->screenshotDirLineEdit->setText(QString::fromStdString(screenshotDir));
this->saveStateDirLineEdit->setText(QString::fromStdString(saveStateDir));
this->saveSramDirLineEdit->setText(QString::fromStdString(saveSramDir));
this->overrideUserDirsGroupBox->setChecked(overrideUserDirs);
this->userDataDirLineEdit->setText(QString::fromStdString(userDataDir));
this->userCacheDirLineEdit->setText(QString::fromStdString(userCacheDir));
}
void SettingsDialog::load64DDSettings(void)
@@ -474,7 +461,6 @@ void SettingsDialog::loadInterfaceGeneralSettings(void)
// select currently chosen theme in UI
this->themeComboBox->setCurrentText(QString::fromStdString(CoreSettingsGetStringValue(SettingsID::GUI_Theme)));
this->iconThemeComboBox->setCurrentText(QString::fromStdString(CoreSettingsGetStringValue(SettingsID::GUI_IconTheme)));
this->confirmExitWhileInGameCheckBox->setChecked(CoreSettingsGetBoolValue(SettingsID::GUI_ConfirmExitWhileInGame));
#ifdef UPDATER
this->checkForUpdatesCheckBox->setChecked(CoreSettingsGetBoolValue(SettingsID::GUI_CheckForUpdates));
#endif // UPDATER
@@ -488,6 +474,7 @@ void SettingsDialog::loadInterfaceEmulationSettings(void)
this->hideCursorFullscreenCheckBox->setChecked(CoreSettingsGetBoolValue(SettingsID::GUI_HideCursorInFullscreenEmulation));
this->automaticFullscreenCheckbox->setChecked(CoreSettingsGetBoolValue(SettingsID::GUI_AutomaticFullscreen));
this->confirmDragDropCheckBox->setChecked(CoreSettingsGetBoolValue(SettingsID::GUI_ConfirmDragDrop));
this->confirmExitWhileInGameCheckBox->setChecked(CoreSettingsGetBoolValue(SettingsID::GUI_ConfirmExitWhileInGame));
this->statusBarMessageDurationSpinBox->setValue(CoreSettingsGetIntValue(SettingsID::GUI_StatusbarMessageDuration));
this->openglTypeComboBox->setCurrentIndex(CoreSettingsGetBoolValue(SettingsID::GUI_OpenGLES));
}
@@ -515,7 +502,8 @@ void SettingsDialog::loadInterfaceOSDSettings(void)
std::vector<int> textColor = CoreSettingsGetIntListValue(SettingsID::GUI_OnScreenDisplayTextColor);
if (backgroundColor.size() == 4)
{
this->currentBackgroundColor = QColor(backgroundColor.at(0), backgroundColor.at(1), backgroundColor.at(2), backgroundColor.at(3));
this->currentBackgroundColor = QColor(backgroundColor.at(0), backgroundColor.at(1),
backgroundColor.at(2), backgroundColor.at(3));
this->chooseColor(this->changeBackgroundColorButton, &this->currentBackgroundColor, true);
}
if (textColor.size() == 4)
@@ -617,9 +605,6 @@ void SettingsDialog::loadDefaultDirectorySettings(void)
this->screenshotDirLineEdit->setText(QString::fromStdString(CoreSettingsGetDefaultStringValue(SettingsID::Core_ScreenshotPath)));
this->saveStateDirLineEdit->setText(QString::fromStdString(CoreSettingsGetDefaultStringValue(SettingsID::Core_SaveStatePath)));
this->saveSramDirLineEdit->setText(QString::fromStdString(CoreSettingsGetDefaultStringValue(SettingsID::Core_SaveSRAMPath)));
this->overrideUserDirsGroupBox->setChecked(CoreSettingsGetDefaultBoolValue(SettingsID::Core_OverrideUserDirs));
this->userDataDirLineEdit->setText(QString::fromStdString(CoreSettingsGetDefaultStringValue(SettingsID::Core_UserDataDirOverride)));
this->userCacheDirLineEdit->setText(QString::fromStdString(CoreSettingsGetDefaultStringValue(SettingsID::Core_UserCacheDirOverride)));
}
void SettingsDialog::loadDefault64DDSettings(void)
@@ -639,7 +624,6 @@ void SettingsDialog::loadDefaultInterfaceGeneralSettings(void)
{
this->themeComboBox->setCurrentText(QString::fromStdString(CoreSettingsGetDefaultStringValue(SettingsID::GUI_Theme)));
this->iconThemeComboBox->setCurrentText(QString::fromStdString(CoreSettingsGetDefaultStringValue(SettingsID::GUI_IconTheme)));
this->confirmExitWhileInGameCheckBox->setChecked(CoreSettingsGetDefaultBoolValue(SettingsID::GUI_ConfirmExitWhileInGame));
#ifdef UPDATER
this->checkForUpdatesCheckBox->setChecked(CoreSettingsGetDefaultBoolValue(SettingsID::GUI_CheckForUpdates));
#endif // UPDATER
@@ -653,6 +637,7 @@ void SettingsDialog::loadDefaultInterfaceEmulationSettings(void)
this->hideCursorFullscreenCheckBox->setChecked(CoreSettingsGetDefaultBoolValue(SettingsID::GUI_HideCursorInFullscreenEmulation));
this->automaticFullscreenCheckbox->setChecked(CoreSettingsGetDefaultBoolValue(SettingsID::GUI_AutomaticFullscreen));
this->confirmDragDropCheckBox->setChecked(CoreSettingsGetDefaultBoolValue(SettingsID::GUI_ConfirmDragDrop));
this->confirmExitWhileInGameCheckBox->setChecked(CoreSettingsGetDefaultBoolValue(SettingsID::GUI_ConfirmExitWhileInGame));
this->statusBarMessageDurationSpinBox->setValue(CoreSettingsGetDefaultIntValue(SettingsID::GUI_StatusbarMessageDuration));
this->openglTypeComboBox->setCurrentIndex(CoreSettingsGetDefaultBoolValue(SettingsID::GUI_OpenGLES));
}
@@ -680,7 +665,8 @@ void SettingsDialog::loadDefaultInterfaceOSDSettings(void)
const std::vector<int> textColor = CoreSettingsGetDefaultIntListValue(SettingsID::GUI_OnScreenDisplayTextColor);
if (backgroundColor.size() == 4)
{
this->currentBackgroundColor = QColor(backgroundColor.at(0), backgroundColor.at(1), backgroundColor.at(2), backgroundColor.at(3));
this->currentBackgroundColor = QColor(backgroundColor.at(0), backgroundColor.at(1),
backgroundColor.at(2), backgroundColor.at(3));
this->chooseColor(this->changeBackgroundColorButton, &this->currentBackgroundColor, true);
}
if (textColor.size() == 4)
@@ -829,7 +815,7 @@ void SettingsDialog::saveGamePluginSettings(void)
comboBox = comboBoxArray[i];
id = settingsIdArray[i];
if (comboBox->currentIndex() != 0 )
if (comboBox->currentIndex() != 0)
{
CoreSettingsSetValue(id, this->gameSection, comboBox->currentData().toString().toStdString());
}
@@ -859,10 +845,6 @@ void SettingsDialog::saveDirectorySettings(void)
CoreSettingsSetValue(SettingsID::Core_ScreenshotPath, this->screenshotDirLineEdit->text().toStdString());
CoreSettingsSetValue(SettingsID::Core_SaveStatePath, this->saveStateDirLineEdit->text().toStdString());
CoreSettingsSetValue(SettingsID::Core_SaveSRAMPath, this->saveSramDirLineEdit->text().toStdString());
CoreSettingsSetValue(SettingsID::Core_OverrideUserDirs, this->overrideUserDirsGroupBox->isChecked());
CoreSettingsSetValue(SettingsID::Core_UserDataDirOverride, this->userDataDirLineEdit->text().toStdString());
CoreSettingsSetValue(SettingsID::Core_UserCacheDirOverride, this->userCacheDirLineEdit->text().toStdString());
}
void SettingsDialog::save64DDSettings(void)
@@ -882,7 +864,6 @@ void SettingsDialog::saveInterfaceGeneralSettings(void)
{
CoreSettingsSetValue(SettingsID::GUI_Theme, this->themeComboBox->currentText().toStdString());
CoreSettingsSetValue(SettingsID::GUI_IconTheme, this->iconThemeComboBox->currentText().toStdString());
CoreSettingsSetValue(SettingsID::GUI_ConfirmExitWhileInGame, this->confirmExitWhileInGameCheckBox->isChecked());
#ifdef UPDATER
CoreSettingsSetValue(SettingsID::GUI_CheckForUpdates, this->checkForUpdatesCheckBox->isChecked());
#endif // UPDATER
@@ -896,6 +877,7 @@ void SettingsDialog::saveInterfaceEmulationSettings(void)
CoreSettingsSetValue(SettingsID::GUI_ResumeEmulationOnFocus, this->resumeEmulationOnFocusCheckBox->isChecked());
CoreSettingsSetValue(SettingsID::GUI_AutomaticFullscreen, this->automaticFullscreenCheckbox->isChecked());
CoreSettingsSetValue(SettingsID::GUI_ConfirmDragDrop, this->confirmDragDropCheckBox->isChecked());
CoreSettingsSetValue(SettingsID::GUI_ConfirmExitWhileInGame, this->confirmExitWhileInGameCheckBox->isChecked());
CoreSettingsSetValue(SettingsID::GUI_StatusbarMessageDuration, this->statusBarMessageDurationSpinBox->value());
CoreSettingsSetValue(SettingsID::GUI_OpenGLES, this->openglTypeComboBox->currentIndex() == 1);
}
@@ -1129,7 +1111,7 @@ void SettingsDialog::commonPluginSettings(SettingsDialogAction action)
for (const auto &p : this->pluginList)
{
index = ((int)p.Type - 1);
index = (static_cast<int>(p.Type) - 1);
comboBox = comboBoxArray[index];
pluginFileName = pluginFileNames[index];
pluginName = QString::fromStdString(p.Name);
@@ -1214,7 +1196,13 @@ void SettingsDialog::hideEmulationInfoText(void)
void SettingsDialog::chooseDirectory(QLineEdit *lineEdit, QString caption)
{
QString dir = QFileDialog::getExistingDirectory(this, caption);
QString currentDir = lineEdit->text();
if (!QDir(currentDir).exists())
{
currentDir = "";
}
QString dir = QFileDialog::getExistingDirectory(this, caption, currentDir);
if (dir.isEmpty())
{
return;
@@ -1223,7 +1211,7 @@ void SettingsDialog::chooseDirectory(QLineEdit *lineEdit, QString caption)
lineEdit->setText(QDir::toNativeSeparators(dir));
}
void SettingsDialog::chooseFile(QLineEdit *lineEdit, QString caption, QString filter, QString md5)
void SettingsDialog::chooseFile(QLineEdit *lineEdit, QString caption, QString filter, QStringList md5List)
{
QString file = QFileDialog::getOpenFileName(this, caption, "", filter);
if (file.isEmpty())
@@ -1231,7 +1219,7 @@ void SettingsDialog::chooseFile(QLineEdit *lineEdit, QString caption, QString fi
return;
}
if (!md5.isEmpty())
if (!md5List.isEmpty())
{
QFile qFile(file);
if (!qFile.open(QFile::ReadOnly))
@@ -1241,15 +1229,29 @@ void SettingsDialog::chooseFile(QLineEdit *lineEdit, QString caption, QString fi
}
QCryptographicHash hash(QCryptographicHash::Algorithm::Md5);
if (hash.addData(&qFile))
if (!hash.addData(&qFile))
{
QString md5Hash = QString(hash.result().toHex());
if (md5Hash != md5)
QtMessageBox::Error(this, "MD5 hashing failed", "QCryptographicHash::addData() Failed");
return;
}
QString md5Hash = QString(hash.result().toHex());
bool hashFound = false;
for (const QString& md5 : md5List)
{
if (md5Hash == md5)
{
QtMessageBox::Error(this, "MD5 mismatch", "Expected file with MD5: \"" + md5 + "\"");
return;
hashFound = true;
break;
}
}
if (!hashFound)
{
QtMessageBox::Error(this, "MD5 mismatch", "Expected file with MD5: \"" + md5List.first() + "\"");
return;
}
}
lineEdit->setText(QDir::toNativeSeparators(file));
@@ -1375,16 +1377,6 @@ void SettingsDialog::on_changeSaveSramDirButton_clicked(void)
this->chooseDirectory(this->saveSramDirLineEdit, tr("Select Save (SRAM) Directory"));
}
void SettingsDialog::on_changeUserDataDirButton_clicked(void)
{
this->chooseDirectory(this->userDataDirLineEdit, tr("Select User Data Directory"));
}
void SettingsDialog::on_changeUserCacheDirButton_clicked(void)
{
this->chooseDirectory(this->userCacheDirLineEdit, tr("Select User Cache Directory"));
}
void SettingsDialog::on_changeJapaneseIPLRomPathButton_clicked(void)
{
this->chooseFile(this->japaneseIPLRomLineEdit, tr("Open Japanese Retail 64DD IPL"), "IPL ROMs (*.n64)");
@@ -1573,10 +1565,10 @@ void SettingsDialog::on_coreCpuEmulatorComboBox_currentIndexChanged(int index)
void SettingsDialog::on_changeNTSCPifRomButton_clicked(void)
{
this->chooseFile(this->ntscPifRomLineEdit, tr("Open NTSC PIF ROM"), "PIF ROMs (*.rom)", "5c124e7948ada85da603a522782940d0");
this->chooseFile(this->ntscPifRomLineEdit, tr("Open NTSC PIF ROM"), "PIF ROMs (*.rom)", { "5C124E7948ADA85DA603A522782940D0", "4921D5F2165DEE6E2496F4388C4C81DA" });
}
void SettingsDialog::on_changePALPifRomButton_clicked(void)
{
this->chooseFile(this->palPifRomLineEdit, tr("Open PAL PIF ROM"), "PIF ROMs (*.rom)", "d4232dc935cad0650ac2664d52281f3a");
this->chooseFile(this->palPifRomLineEdit, tr("Open PAL PIF ROM"), "PIF ROMs (*.rom)", { "D4232DC935CAD0650AC2664D52281F3A", "2B6EEC586FAA43F3462333B844834554" });
}
@@ -15,8 +15,10 @@
#include <QStandardItemModel>
#include <QHBoxLayout>
#include <QStringList>
#include <QTreeWidget>
#include <QDialog>
#include <QString>
#include <QWidget>
#include <QColor>
#include <QMutex>
@@ -127,7 +129,7 @@ class SettingsDialog : public QDialog, private Ui::SettingsDialog
void hideEmulationInfoText(void);
void chooseDirectory(QLineEdit *, QString caption);
void chooseFile(QLineEdit *, QString caption, QString filter = "", QString md5 = "");
void chooseFile(QLineEdit *, QString caption, QString filter = "", QStringList md5List = {});
void chooseColor(QPushButton *, QColor *, bool skipChoice = false);
bool applyPluginSettings(void);
@@ -142,8 +144,6 @@ class SettingsDialog : public QDialog, private Ui::SettingsDialog
void on_changeScreenShotDirButton_clicked(void);
void on_changeSaveStateDirButton_clicked(void);
void on_changeSaveSramDirButton_clicked(void);
void on_changeUserDataDirButton_clicked(void);
void on_changeUserCacheDirButton_clicked(void);
void on_changeJapaneseIPLRomPathButton_clicked(void);
void on_changeAmericanIPLRomPathButton_clicked(void);
@@ -116,13 +116,6 @@
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="confirmExitWhileInGameCheckBox">
<property name="text">
<string>Ask for confirmation on exit while in-game</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkForUpdatesCheckBox">
<property name="text">
@@ -234,6 +227,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="confirmExitWhileInGameCheckBox">
<property name="text">
<string>Ask for confirmation on exit</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_17">
<item>
@@ -2824,6 +2824,9 @@
<property name="text">
<string>Change</string>
</property>
<property name="icon">
<iconset theme="folder-open-line"/>
</property>
</widget>
</item>
</layout>
@@ -2847,6 +2850,9 @@
<property name="text">
<string>Change</string>
</property>
<property name="icon">
<iconset theme="folder-open-line"/>
</property>
</widget>
</item>
</layout>
@@ -2870,70 +2876,9 @@
<property name="text">
<string>Change</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="overrideUserDirsGroupBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="title">
<string>Override &amp;user directories</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_27">
<item>
<widget class="QGroupBox" name="groupBox_30">
<property name="title">
<string>User Data Directory</string>
<property name="icon">
<iconset theme="folder-open-line"/>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_67">
<item>
<widget class="QLineEdit" name="userDataDirLineEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="changeUserDataDirButton">
<property name="text">
<string>Change</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_31">
<property name="title">
<string>User Cache Directory</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_68">
<item>
<widget class="QLineEdit" name="userCacheDirLineEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="changeUserCacheDirButton">
<property name="text">
<string>Change</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
@@ -3022,12 +2967,18 @@
<property name="text">
<string>Change</string>
</property>
<property name="icon">
<iconset theme="folder-open-line"/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="clearJapaneseIPLRomPathButton">
<property name="text">
<string>Clear</string>
<string/>
</property>
<property name="icon">
<iconset theme="delete-bin-line"/>
</property>
</widget>
</item>
@@ -3052,12 +3003,18 @@
<property name="text">
<string>Change</string>
</property>
<property name="icon">
<iconset theme="folder-open-line"/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="clearAmericanIPLRomPathButton">
<property name="text">
<string>Clear</string>
<string/>
</property>
<property name="icon">
<iconset theme="delete-bin-line"/>
</property>
</widget>
</item>
@@ -3082,12 +3039,18 @@
<property name="text">
<string>Change</string>
</property>
<property name="icon">
<iconset theme="folder-open-line"/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="clearDevelopmentIPLRomPathButton">
<property name="text">
<string>Clear</string>
<string/>
</property>
<property name="icon">
<iconset theme="delete-bin-line"/>
</property>
</widget>
</item>
+3 -3
View File
@@ -24,13 +24,13 @@ bool EventFilter::eventFilter(QObject *object, QEvent *event)
switch (event->type())
{
case QEvent::Type::KeyPress:
emit this->on_EventFilter_KeyPressed((QKeyEvent *)event);
emit this->on_EventFilter_KeyPressed(static_cast<QKeyEvent*>(event));
return true;
case QEvent::Type::Drop:
emit this->on_EventFilter_FileDropped((QDropEvent *)event);
emit this->on_EventFilter_FileDropped(static_cast<QDropEvent*>(event));
return true;
case QEvent::Type::KeyRelease:
emit this->on_EventFilter_KeyReleased((QKeyEvent *)event);
emit this->on_EventFilter_KeyReleased(static_cast<QKeyEvent*>(event));
return true;
default:
break;
+52 -42
View File
@@ -49,12 +49,18 @@
#include <QSettings>
#include <QStatusBar>
#include <QMenuBar>
#include <cstdlib>
#include <QString>
#include <QTimer>
#include <cmath>
#include <QDir>
#include <QUrl>
#ifdef KCA_DRAG_DROP
#include <KUrlMimeData>
#endif
#include <cstdlib>
#include <cmath>
#include <RMG-Core/CachedRomHeaderAndSettings.hpp>
#include <RMG-Core/SpeedLimiter.hpp>
#include <RMG-Core/Directories.hpp>
@@ -165,7 +171,8 @@ void MainWindow::closeEvent(QCloseEvent *event)
{
bool inEmulation = this->emulationThread->isRunning();
if (!this->ui_ForceClose &&
if (this->ui_ShowUI &&
!this->ui_ForceClose &&
inEmulation &&
CoreSettingsGetBoolValue(SettingsID::GUI_ConfirmExitWhileInGame))
{
@@ -194,9 +201,16 @@ void MainWindow::closeEvent(QCloseEvent *event)
Qt::ToolBarArea toolbarArea = this->toolBarArea(this->toolBar);
CoreSettingsSetValue(SettingsID::GUI_ToolbarArea, this->getToolbarSettingAreaFromArea(toolbarArea));
// attempt to shutdown emulation
this->ui_NoSwitchToRomBrowser = true;
this->on_Action_System_Shutdown();
// wait until emulation has shut down
while (this->emulationThread->isRunning())
{
QCoreApplication::processEvents();
}
this->ui_Widget_RomBrowser->StopRefreshRomList();
this->coreCallBacks->Stop();
@@ -208,13 +222,6 @@ void MainWindow::closeEvent(QCloseEvent *event)
}
#endif // NETPLAY
this->logDialog.close();
while (this->emulationThread->isRunning())
{
QCoreApplication::processEvents();
}
CoreSettingsSave();
CoreShutdown();
@@ -1149,23 +1156,19 @@ void MainWindow::configureActions(void)
for (int i = 0; i < 12; i++)
{
QAction* speedAction = speedActions[i];
int speedActionNumber = speedActionNumbers[i];
speedAction->setCheckable(true);
speedAction->setChecked(currentSpeedFactor == speedActionNumbers[i]);
speedAction->setChecked(currentSpeedFactor == speedActionNumber);
speedAction->setActionGroup(speedActionGroup);
// connect emulation speed action here because we need to do
// something special for them
connect(speedAction, &QAction::triggered, [=, this](bool checked)
connect(speedAction, &QAction::triggered, [this, speedActionNumber](bool checked)
{
if (checked)
{
QString factorText = speedAction->text().split("%").first();
// sometimes the text can contain a '&'
// which will make the toInt() function return 0
// so strip it out
factorText.remove('&');
this->on_Action_System_SpeedFactor(factorText.toInt());
this->on_Action_System_SpeedFactor(speedActionNumber);
}
});
}
@@ -1182,16 +1185,11 @@ void MainWindow::configureActions(void)
// connect slot action here because we need to do
// something special for them
connect(slotAction, &QAction::triggered, [=, this](bool checked)
connect(slotAction, &QAction::triggered, [this, i](bool checked)
{
if (checked)
{
QString slotText = slotAction->text().split(" ").at(1);
// sometimes the text can contain a '&'
// which will make the toInt() function return 0
// so strip it out
slotText.remove('&');
this->on_Action_System_CurrentSaveState(slotText.toInt());
this->on_Action_System_CurrentSaveState(i);
}
});
}
@@ -1465,11 +1463,15 @@ void MainWindow::on_EventFilter_KeyReleased(QKeyEvent *event)
void MainWindow::on_EventFilter_FileDropped(QDropEvent *event)
{
#ifdef DRAG_DROP
const QMimeData *mimeData = event->mimeData();
if (!mimeData->hasUrls() || mimeData->urls().empty() ||
!mimeData->urls().first().isLocalFile())
QList<QUrl> urls = mimeData->urls();
#ifdef KCA_DRAG_DROP
urls = KUrlMimeData::urlsFromMimeData(mimeData, KUrlMimeData::PreferLocalUrls);
#endif
if (!mimeData->hasUrls() || urls.empty() ||
!urls.first().isLocalFile())
{
return;
}
@@ -1488,16 +1490,18 @@ void MainWindow::on_EventFilter_FileDropped(QDropEvent *event)
if (inEmulation && confirmDragDrop)
{
QMessageBox::StandardButton reply = QMessageBox::question(this, "",
"Are you sure you want to launch the drag & dropped ROM?",
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::No)
confirmDragDrop = false;
bool ret = QtMessageBox::Question(this, "Are you sure you want to launch the drag & dropped ROM?",
"Don't ask for confirmation again", confirmDragDrop);
if (!ret)
{
return;
}
CoreSettingsSetValue(SettingsID::GUI_ConfirmDragDrop, !confirmDragDrop);
}
file = mimeData->urls().first().toLocalFile();
file = urls.first().toLocalFile();
if (inEmulation)
{
@@ -1512,7 +1516,6 @@ void MainWindow::on_EventFilter_FileDropped(QDropEvent *event)
}
this->launchEmulationThread(file, "", refreshRomList, -1, false, true);
#endif // DRAG_DROP
}
void MainWindow::on_QGuiApplication_applicationStateChanged(Qt::ApplicationState state)
@@ -2234,8 +2237,14 @@ void MainWindow::on_RomBrowser_PlayGameWithSlot(QString file, int slot)
void MainWindow::on_RomBrowser_ChangeRomDirectory(void)
{
QString dir = QFileDialog::getExistingDirectory(this, tr("Select ROM Directory"));
if (!dir.isEmpty())
QString currentDir = QString::fromStdString(CoreSettingsGetStringValue(SettingsID::RomBrowser_Directory));
if (!QDir(currentDir).exists())
{
currentDir = "";
}
QString dir = QFileDialog::getExistingDirectory(this, tr("Select ROM Directory"), currentDir);
if (!dir.isEmpty() && currentDir != dir)
{
CoreSettingsSetValue(SettingsID::RomBrowser_Directory, dir.toStdString());
this->ui_Widget_RomBrowser->RefreshRomList();
@@ -2250,16 +2259,17 @@ void MainWindow::on_RomBrowser_RomInformation(QString file)
this->ui_Widget_RomBrowser->StopRefreshRomList();
}
CoreRomType romType;
CoreRomHeader romHeader;
CoreRomSettings romSettings;
if (!CoreGetCachedRomHeaderAndSettings(file.toStdU32String(), nullptr, &romHeader, nullptr, &romSettings))
if (!CoreGetCachedRomHeaderAndSettings(file.toStdU32String(), &romType, &romHeader, nullptr, &romSettings))
{
this->showErrorMessage("CoreGetCachedRomHeaderAndSettings() Failed", QString::fromStdString(CoreGetError()));
return;
}
Dialog::RomInfoDialog dialog(file, romHeader, romSettings, this);
Dialog::RomInfoDialog dialog(this, file, romType, romHeader, romSettings);
dialog.exec();
if (isRefreshingRomList)
@@ -2297,7 +2307,7 @@ void MainWindow::on_RomBrowser_EditGameInputSettings(QString file)
this->ui_Widget_RomBrowser->StopRefreshRomList();
}
if (!CorePluginsOpenROMConfig(CorePluginType::Input, (void*)this, file.toStdU32String()))
if (!CorePluginsOpenROMConfig(CorePluginType::Input, this, file.toStdU32String()))
{
this->showErrorMessage("CorePluginsOpenROMConfig() Failed", QString::fromStdString(CoreGetError()));
return;
@@ -2505,8 +2515,8 @@ void MainWindow::on_VidExt_ResizeWindow(int width, int height)
// account for HiDPI scaling
if (this->devicePixelRatio() != 1)
{
width = (int)std::ceil((double)((double)width / this->devicePixelRatio()));
height = (int)std::ceil((double)((double)height / this->devicePixelRatio()));
width = static_cast<int>(std::ceil(static_cast<double>(static_cast<double>(width) / this->devicePixelRatio())));
height = static_cast<int>(std::ceil(static_cast<double>(static_cast<double>(height) / this->devicePixelRatio())));
}
if (!this->menuBar()->isHidden())
@@ -2708,7 +2718,7 @@ void MainWindow::on_Core_StateCallback(CoreStateCallbackType type, int value)
case CoreStateCallbackType::EmulationState:
{
// update Pause button
this->action_System_Pause->setChecked(value == (int)CoreEmulationState::Paused);
this->action_System_Pause->setChecked(value == static_cast<int>(CoreEmulationState::Paused));
// update OSD state
if (value == (int)CoreEmulationState::Paused)
{
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M12.0003 3C17.3924 3 21.8784 6.87976 22.8189 12C21.8784 17.1202 17.3924 21 12.0003 21C6.60812 21 2.12215 17.1202 1.18164 12C2.12215 6.87976 6.60812 3 12.0003 3ZM12.0003 19C16.2359 19 19.8603 16.052 20.7777 12C19.8603 7.94803 16.2359 5 12.0003 5C7.7646 5 4.14022 7.94803 3.22278 12C4.14022 16.052 7.7646 19 12.0003 19ZM12.0003 16.5C9.51498 16.5 7.50026 14.4853 7.50026 12C7.50026 9.51472 9.51498 7.5 12.0003 7.5C14.4855 7.5 16.5003 9.51472 16.5003 12C16.5003 14.4853 14.4855 16.5 12.0003 16.5ZM12.0003 14.5C13.381 14.5 14.5003 13.3807 14.5003 12C14.5003 10.6193 13.381 9.5 12.0003 9.5C10.6196 9.5 9.50026 10.6193 9.50026 12C9.50026 13.3807 10.6196 14.5 12.0003 14.5Z"></path></svg>

After

Width:  |  Height:  |  Size: 769 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M5 10.9381C8.61872 10.4869 11.4869 7.61872 11.9381 4H5V10.9381ZM5 12.9506V20H19V4H13.9506C13.4816 8.72442 9.72442 12.4816 5 12.9506ZM4 2H20C20.5523 2 21 2.44772 21 3V21C21 21.5523 20.5523 22 20 22H4C3.44772 22 3 21.5523 3 21V3C3 2.44772 3.44772 2 4 2ZM15 16H17V18H15V16Z"></path></svg>

After

Width:  |  Height:  |  Size: 374 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M15.1986 9.94447C14.7649 9.5337 14.4859 8.98613 14.4085 8.39384L14.0056 5.31138L11.275 6.79724C10.7503 7.08274 10.1433 7.17888 9.55608 7.06948L6.49998 6.50015L7.06931 9.55625C7.17871 10.1435 7.08257 10.7505 6.79707 11.2751L5.31121 14.0057L8.39367 14.4086C8.98596 14.4861 9.53353 14.7651 9.94431 15.1987L12.0821 17.4557L13.4178 14.6486C13.6745 14.1092 14.109 13.6747 14.6484 13.418L17.4555 12.0823L15.1986 9.94447ZM15.2238 15.5079L13.0111 20.1581C12.8687 20.4573 12.5107 20.5844 12.2115 20.442C12.1448 20.4103 12.0845 20.3665 12.0337 20.3129L8.49229 16.5741C8.39749 16.474 8.27113 16.4096 8.13445 16.3918L3.02816 15.7243C2.69958 15.6814 2.46804 15.3802 2.51099 15.0516C2.52056 14.9784 2.54359 14.9075 2.5789 14.8426L5.04031 10.3192C5.1062 10.1981 5.12839 10.058 5.10314 9.92253L4.16 4.85991C4.09931 4.53414 4.3142 4.22086 4.63997 4.16017C4.7126 4.14664 4.78711 4.14664 4.85974 4.16017L9.92237 5.10331C10.0579 5.12855 10.198 5.10637 10.319 5.04048L14.8424 2.57907C15.1335 2.42068 15.4979 2.52825 15.6562 2.81931C15.6916 2.88421 15.7146 2.95507 15.7241 3.02833L16.3916 8.13462C16.4095 8.2713 16.4739 8.39766 16.5739 8.49245L20.3127 12.0338C20.5533 12.2617 20.5636 12.6415 20.3357 12.8821C20.2849 12.9357 20.2246 12.9795 20.1579 13.0112L15.5078 15.224C15.3833 15.2832 15.283 15.3835 15.2238 15.5079ZM16.0206 17.435L17.4348 16.0208L21.6775 20.2634L20.2633 21.6776L16.0206 17.435Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M15.7279 9.57627L14.3137 8.16206L5 17.4758V18.89H6.41421L15.7279 9.57627ZM17.1421 8.16206L18.5563 6.74785L17.1421 5.33363L15.7279 6.74785L17.1421 8.16206ZM7.24264 20.89H3V16.6473L16.435 3.21231C16.8256 2.82179 17.4587 2.82179 17.8492 3.21231L20.6777 6.04074C21.0682 6.43126 21.0682 7.06443 20.6777 7.45495L7.24264 20.89Z"></path></svg>

After

Width:  |  Height:  |  Size: 424 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M12.0003 3C17.3924 3 21.8784 6.87976 22.8189 12C21.8784 17.1202 17.3924 21 12.0003 21C6.60812 21 2.12215 17.1202 1.18164 12C2.12215 6.87976 6.60812 3 12.0003 3ZM12.0003 19C16.2359 19 19.8603 16.052 20.7777 12C19.8603 7.94803 16.2359 5 12.0003 5C7.7646 5 4.14022 7.94803 3.22278 12C4.14022 16.052 7.7646 19 12.0003 19ZM12.0003 16.5C9.51498 16.5 7.50026 14.4853 7.50026 12C7.50026 9.51472 9.51498 7.5 12.0003 7.5C14.4855 7.5 16.5003 9.51472 16.5003 12C16.5003 14.4853 14.4855 16.5 12.0003 16.5ZM12.0003 14.5C13.381 14.5 14.5003 13.3807 14.5003 12C14.5003 10.6193 13.381 9.5 12.0003 9.5C10.6196 9.5 9.50026 10.6193 9.50026 12C9.50026 13.3807 10.6196 14.5 12.0003 14.5Z" fill="#ffffff"></path></svg>

After

Width:  |  Height:  |  Size: 784 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M5 10.9381C8.61872 10.4869 11.4869 7.61872 11.9381 4H5V10.9381ZM5 12.9506V20H19V4H13.9506C13.4816 8.72442 9.72442 12.4816 5 12.9506ZM4 2H20C20.5523 2 21 2.44772 21 3V21C21 21.5523 20.5523 22 20 22H4C3.44772 22 3 21.5523 3 21V3C3 2.44772 3.44772 2 4 2ZM15 16H17V18H15V16Z" fill="#ffffff"></path></svg>

After

Width:  |  Height:  |  Size: 389 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M15.1986 9.94447C14.7649 9.5337 14.4859 8.98613 14.4085 8.39384L14.0056 5.31138L11.275 6.79724C10.7503 7.08274 10.1433 7.17888 9.55608 7.06948L6.49998 6.50015L7.06931 9.55625C7.17871 10.1435 7.08257 10.7505 6.79707 11.2751L5.31121 14.0057L8.39367 14.4086C8.98596 14.4861 9.53353 14.7651 9.94431 15.1987L12.0821 17.4557L13.4178 14.6486C13.6745 14.1092 14.109 13.6747 14.6484 13.418L17.4555 12.0823L15.1986 9.94447ZM15.2238 15.5079L13.0111 20.1581C12.8687 20.4573 12.5107 20.5844 12.2115 20.442C12.1448 20.4103 12.0845 20.3665 12.0337 20.3129L8.49229 16.5741C8.39749 16.474 8.27113 16.4096 8.13445 16.3918L3.02816 15.7243C2.69958 15.6814 2.46804 15.3802 2.51099 15.0516C2.52056 14.9784 2.54359 14.9075 2.5789 14.8426L5.04031 10.3192C5.1062 10.1981 5.12839 10.058 5.10314 9.92253L4.16 4.85991C4.09931 4.53414 4.3142 4.22086 4.63997 4.16017C4.7126 4.14664 4.78711 4.14664 4.85974 4.16017L9.92237 5.10331C10.0579 5.12855 10.198 5.10637 10.319 5.04048L14.8424 2.57907C15.1335 2.42068 15.4979 2.52825 15.6562 2.81931C15.6916 2.88421 15.7146 2.95507 15.7241 3.02833L16.3916 8.13462C16.4095 8.2713 16.4739 8.39766 16.5739 8.49245L20.3127 12.0338C20.5533 12.2617 20.5636 12.6415 20.3357 12.8821C20.2849 12.9357 20.2246 12.9795 20.1579 13.0112L15.5078 15.224C15.3833 15.2832 15.283 15.3835 15.2238 15.5079ZM16.0206 17.435L17.4348 16.0208L21.6775 20.2634L20.2633 21.6776L16.0206 17.435Z" fill="#ffffff"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M15.7279 9.57627L14.3137 8.16206L5 17.4758V18.89H6.41421L15.7279 9.57627ZM17.1421 8.16206L18.5563 6.74785L17.1421 5.33363L15.7279 6.74785L17.1421 8.16206ZM7.24264 20.89H3V16.6473L16.435 3.21231C16.8256 2.82179 17.4587 2.82179 17.8492 3.21231L20.6777 6.04074C21.0682 6.43126 21.0682 7.06443 20.6777 7.45495L7.24264 20.89Z" fill="#ffffff"></path></svg>

After

Width:  |  Height:  |  Size: 439 B

+8
View File
@@ -12,6 +12,7 @@
<file alias="icons/black/svg/delete-bin-line.svg">Resource/icons/black/svg/delete-bin-line.svg</file>
<file alias="icons/black/svg/door-open-line.svg">Resource/icons/black/svg/door-open-line.svg</file>
<file alias="icons/black/svg/download-line.svg">Resource/icons/black/svg/download-line.svg</file>
<file alias="icons/black/svg/eye-line.svg">Resource/icons/black/svg/eye-line.svg</file>
<file alias="icons/black/svg/file-line.svg">Resource/icons/black/svg/file-line.svg</file>
<file alias="icons/black/svg/file-list-line.svg">Resource/icons/black/svg/file-list-line.svg</file>
<file alias="icons/black/svg/folder-open-line.svg">Resource/icons/black/svg/folder-open-line.svg</file>
@@ -19,9 +20,12 @@
<file alias="icons/black/svg/function-line.svg">Resource/icons/black/svg/function-line.svg</file>
<file alias="icons/black/svg/gamepad-line.svg">Resource/icons/black/svg/gamepad-line.svg</file>
<file alias="icons/black/svg/github-fill.svg">Resource/icons/black/svg/github-fill.svg</file>
<file alias="icons/black/svg/hard-drive-line.svg">Resource/icons/black/svg/hard-drive-line.svg</file>
<file alias="icons/black/svg/information-line.svg">Resource/icons/black/svg/information-line.svg</file>
<file alias="icons/black/svg/list-check.svg">Resource/icons/black/svg/list-check.svg</file>
<file alias="icons/black/svg/magic-line.svg">Resource/icons/black/svg/magic-line.svg</file>
<file alias="icons/black/svg/pause-line.svg">Resource/icons/black/svg/pause-line.svg</file>
<file alias="icons/black/svg/pencil-line.svg">Resource/icons/black/svg/pencil-line.svg</file>
<file alias="icons/black/svg/play-line.svg">Resource/icons/black/svg/play-line.svg</file>
<file alias="icons/black/svg/plug-line.svg">Resource/icons/black/svg/plug-line.svg</file>
<file alias="icons/black/svg/refresh-line.svg">Resource/icons/black/svg/refresh-line.svg</file>
@@ -45,6 +49,7 @@
<file alias="icons/white/svg/delete-bin-line.svg">Resource/icons/white/svg/delete-bin-line.svg</file>
<file alias="icons/white/svg/door-open-line.svg">Resource/icons/white/svg/door-open-line.svg</file>
<file alias="icons/white/svg/download-line.svg">Resource/icons/white/svg/download-line.svg</file>
<file alias="icons/white/svg/eye-line.svg">Resource/icons/white/svg/eye-line.svg</file>
<file alias="icons/white/svg/file-line.svg">Resource/icons/white/svg/file-line.svg</file>
<file alias="icons/white/svg/file-list-line.svg">Resource/icons/white/svg/file-list-line.svg</file>
<file alias="icons/white/svg/folder-open-line.svg">Resource/icons/white/svg/folder-open-line.svg</file>
@@ -52,9 +57,12 @@
<file alias="icons/white/svg/function-line.svg">Resource/icons/white/svg/function-line.svg</file>
<file alias="icons/white/svg/gamepad-line.svg">Resource/icons/white/svg/gamepad-line.svg</file>
<file alias="icons/white/svg/github-fill.svg">Resource/icons/white/svg/github-fill.svg</file>
<file alias="icons/white/svg/hard-drive-line.svg">Resource/icons/white/svg/hard-drive-line.svg</file>
<file alias="icons/white/svg/information-line.svg">Resource/icons/white/svg/information-line.svg</file>
<file alias="icons/white/svg/list-check.svg">Resource/icons/white/svg/list-check.svg</file>
<file alias="icons/white/svg/magic-line.svg">Resource/icons/white/svg/magic-line.svg</file>
<file alias="icons/white/svg/pause-line.svg">Resource/icons/white/svg/pause-line.svg</file>
<file alias="icons/white/svg/pencil-line.svg">Resource/icons/white/svg/pencil-line.svg</file>
<file alias="icons/white/svg/play-line.svg">Resource/icons/white/svg/play-line.svg</file>
<file alias="icons/white/svg/plug-line.svg">Resource/icons/white/svg/plug-line.svg</file>
<file alias="icons/white/svg/refresh-line.svg">Resource/icons/white/svg/refresh-line.svg</file>
@@ -20,9 +20,7 @@ using namespace UserInterface::Widget;
RomBrowserEmptyWidget::RomBrowserEmptyWidget(QWidget* parent) : QWidget(parent)
{
this->setupUi(this);
#ifdef DRAG_DROP
this->setAcceptDrops(true);
#endif // DRAG_DROP
}
RomBrowserEmptyWidget::~RomBrowserEmptyWidget()
@@ -31,7 +29,6 @@ RomBrowserEmptyWidget::~RomBrowserEmptyWidget()
void RomBrowserEmptyWidget::dragMoveEvent(QDragMoveEvent* event)
{
#ifdef DRAG_DROP
const QMimeData* mimeData = event->mimeData();
if (!mimeData->hasUrls() || !mimeData->urls().first().isLocalFile())
@@ -41,12 +38,10 @@ void RomBrowserEmptyWidget::dragMoveEvent(QDragMoveEvent* event)
}
event->acceptProposedAction();
#endif // DRAG_DROP
}
void RomBrowserEmptyWidget::dragEnterEvent(QDragEnterEvent* event)
{
#ifdef DRAG_DROP
const QMimeData* mimeData = event->mimeData();
if (!mimeData->hasUrls() || !mimeData->urls().first().isLocalFile())
@@ -56,7 +51,6 @@ void RomBrowserEmptyWidget::dragEnterEvent(QDragEnterEvent* event)
}
event->acceptProposedAction();
#endif // DRAG_DROP
}
void RomBrowserEmptyWidget::dropEvent(QDropEvent* event)
@@ -17,12 +17,10 @@ using namespace UserInterface::Widget;
RomBrowserGridViewWidget::RomBrowserGridViewWidget(QWidget* parent) : QListView(parent)
{
#ifdef DRAG_DROP
// configure drag & drop
this->setDragDropMode(QAbstractItemView::DragDropMode::DropOnly);
this->setAcceptDrops(true);
this->setDropIndicatorShown(true);
#endif // DRAG_DROP
}
RomBrowserGridViewWidget::~RomBrowserGridViewWidget()
@@ -31,7 +29,6 @@ RomBrowserGridViewWidget::~RomBrowserGridViewWidget()
void RomBrowserGridViewWidget::dragMoveEvent(QDragMoveEvent* event)
{
#ifdef DRAG_DROP
const QMimeData* mimeData = event->mimeData();
if (!mimeData->hasUrls() || !mimeData->urls().first().isLocalFile())
@@ -41,12 +38,10 @@ void RomBrowserGridViewWidget::dragMoveEvent(QDragMoveEvent* event)
}
event->acceptProposedAction();
#endif // DRAG_DROP
}
void RomBrowserGridViewWidget::dragEnterEvent(QDragEnterEvent* event)
{
#ifdef DRAG_DROP
const QMimeData* mimeData = event->mimeData();
if (!mimeData->hasUrls() || !mimeData->urls().first().isLocalFile())
@@ -56,7 +51,6 @@ void RomBrowserGridViewWidget::dragEnterEvent(QDragEnterEvent* event)
}
event->acceptProposedAction();
#endif // DRAG_DROP
}
void RomBrowserGridViewWidget::dropEvent(QDropEvent* event)
@@ -17,12 +17,10 @@ using namespace UserInterface::Widget;
RomBrowserListViewWidget::RomBrowserListViewWidget(QWidget* parent) : QTableView(parent)
{
#ifdef DRAG_DROP
// configure drag & drop
this->setDragDropMode(QAbstractItemView::DragDropMode::DropOnly);
this->setAcceptDrops(true);
this->setDropIndicatorShown(true);
#endif // DRAG_DROP
}
RomBrowserListViewWidget::~RomBrowserListViewWidget()
@@ -31,7 +29,6 @@ RomBrowserListViewWidget::~RomBrowserListViewWidget()
void RomBrowserListViewWidget::dragMoveEvent(QDragMoveEvent* event)
{
#ifdef DRAG_DROP
const QMimeData* mimeData = event->mimeData();
if (!mimeData->hasUrls() || !mimeData->urls().first().isLocalFile())
@@ -41,12 +38,10 @@ void RomBrowserListViewWidget::dragMoveEvent(QDragMoveEvent* event)
}
event->acceptProposedAction();
#endif // DRAG_DROP
}
void RomBrowserListViewWidget::dragEnterEvent(QDragEnterEvent* event)
{
#ifdef DRAG_DROP
const QMimeData* mimeData = event->mimeData();
if (!mimeData->hasUrls() || !mimeData->urls().first().isLocalFile())
@@ -56,7 +51,6 @@ void RomBrowserListViewWidget::dragEnterEvent(QDragEnterEvent* event)
}
event->acceptProposedAction();
#endif // DRAG_DROP
}
void RomBrowserListViewWidget::dropEvent(QDropEvent* event)
@@ -30,10 +30,8 @@ RomBrowserLoadingWidget::RomBrowserLoadingWidget(QWidget* parent) : QWidget(pare
this->setLayout(layout);
#ifdef DRAG_DROP
// configure drag & drop
this->setAcceptDrops(true);
#endif // DRAG_DROP
}
RomBrowserLoadingWidget::~RomBrowserLoadingWidget()
@@ -53,7 +51,6 @@ void RomBrowserLoadingWidget::SetCurrentRomIndex(int index, int count)
void RomBrowserLoadingWidget::dragMoveEvent(QDragMoveEvent* event)
{
#ifdef DRAG_DROP
const QMimeData* mimeData = event->mimeData();
if (!mimeData->hasUrls() || !mimeData->urls().first().isLocalFile())
@@ -63,12 +60,10 @@ void RomBrowserLoadingWidget::dragMoveEvent(QDragMoveEvent* event)
}
event->acceptProposedAction();
#endif // DRAG_DROP
}
void RomBrowserLoadingWidget::dragEnterEvent(QDragEnterEvent* event)
{
#ifdef DRAG_DROP
const QMimeData* mimeData = event->mimeData();
if (!mimeData->hasUrls() || !mimeData->urls().first().isLocalFile())
@@ -78,7 +73,6 @@ void RomBrowserLoadingWidget::dragEnterEvent(QDragEnterEvent* event)
}
event->acceptProposedAction();
#endif // DRAG_DROP
}
void RomBrowserLoadingWidget::dropEvent(QDropEvent* event)
@@ -161,9 +161,6 @@ RomBrowserWidget::RomBrowserWidget(QWidget *parent) : QWidget(parent)
this->gridViewWidget->setModel(this->gridViewProxyModel);
this->gridViewWidget->setFlow(QListView::Flow::LeftToRight);
this->gridViewWidget->setResizeMode(QListView::Adjust);
#ifndef DRAG_DROP
this->gridViewWidget->setMovement(QListView::Static);
#endif // DRAG_DROP
this->gridViewWidget->setUniformItemSizes(CoreSettingsGetBoolValue(SettingsID::RomBrowser_GridViewUniformItemSizes));
this->gridViewWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
this->gridViewWidget->setViewMode(QListView::ViewMode::IconMode);
@@ -183,10 +180,8 @@ RomBrowserWidget::RomBrowserWidget(QWidget *parent) : QWidget(parent)
connect(this->gridViewWidget, &Widget::RomBrowserGridViewWidget::ZoomOut, this, &RomBrowserWidget::on_ZoomOut);
connect(this->gridViewWidget, &Widget::RomBrowserGridViewWidget::FileDropped, this, &RomBrowserWidget::FileDropped);
#ifdef DRAG_DROP
// configure drag & drop
this->setAcceptDrops(true);
#endif // DRAG_DROP
// configure context menu policy
this->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
@@ -215,20 +210,35 @@ RomBrowserWidget::RomBrowserWidget(QWidget *parent) : QWidget(parent)
// configure context menu contents
this->action_PlayGame->setText("Play Game");
this->action_PlayGame->setIcon(QIcon::fromTheme("gamepad-line"));
this->action_PlayGameWith->setText("Play Game with Disk");
this->action_PlayGameWith->setIcon(QIcon::fromTheme("hard-drive-line"));
this->menu_PlayGameWithDisk->menuAction()->setText("Play Game with Disk");
this->menu_PlayGameWithDisk->menuAction()->setIcon(QIcon::fromTheme("hard-drive-line"));
this->menu_PlayGameWithSlot->menuAction()->setText("Play Game with State");
this->menu_PlayGameWithSlot->menuAction()->setIcon(QIcon::fromTheme("save-3-line"));
this->action_RefreshRomList->setText("Refresh ROM List");
this->action_RefreshRomList->setIcon(QIcon::fromTheme("refresh-line"));
this->action_OpenRomDirectory->setText("Open ROM Directory");
this->action_OpenRomDirectory->setIcon(QIcon::fromTheme("folder-open-line"));
this->action_ChangeRomDirectory->setText("Change ROM Directory...");
this->action_ChangeRomDirectory->setIcon(QIcon::fromTheme("pencil-line"));
this->action_RomInformation->setText("ROM Information");
this->action_RomInformation->setIcon(QIcon::fromTheme("information-line"));
this->action_EditGameSettings->setText("Edit Game Settings");
this->action_EditGameSettings->setIcon(QIcon::fromTheme("settings-3-line"));
this->action_EditGameInputSettings->setText("Edit Game Input Settings");
this->action_EditGameInputSettings->setIcon(QIcon::fromTheme("gamepad-line"));
this->action_EditCheats->setText("Edit Cheats");
this->action_EditCheats->setIcon(QIcon::fromTheme("code-box-line"));
this->action_ResetColumnSizes->setText("Reset Column Sizes");
this->action_ResetColumnSizes->setIcon(QIcon::fromTheme("restart-line"));
this->menu_Columns->menuAction()->setText("Show/Hide Columns");
this->menu_Columns->menuAction()->setIcon(QIcon::fromTheme("eye-line"));
this->action_SetCoverImage->setText("Set Cover Image...");
this->action_SetCoverImage->setIcon(QIcon::fromTheme("file-line"));
this->action_RemoveCoverImage->setText("Remove Cover Image");
this->action_RemoveCoverImage->setIcon(QIcon::fromTheme("delete-bin-line"));
connect(this->action_PlayGame, &QAction::triggered, this, &RomBrowserWidget::on_Action_PlayGame);
connect(this->action_PlayGameWith, &QAction::triggered, this, &RomBrowserWidget::on_Action_PlayGameWith);
connect(this->menu_PlayGameWithDisk, &QMenu::triggered, this, &RomBrowserWidget::on_Menu_PlayGameWithDisk);
@@ -828,14 +838,9 @@ void RomBrowserWidget::generateStateMenu(void)
saveStateSlotText += saveStateFileInfo.lastModified().toString("dd/MM/yyyy hh:mm");
QAction* slotAction = this->menu_PlayGameWithSlot->addAction(saveStateSlotText);
connect(slotAction, &QAction::triggered, [=, this]()
connect(slotAction, &QAction::triggered, [this, i]()
{
QString slotText = slotAction->text().split(" ").at(1);
// sometimes the text can contain a '&'
// which will make the toInt() function return 0
// so strip it out
slotText.remove('&');
this->on_Action_PlayGameWithSlot(slotText.toInt());
this->on_Action_PlayGameWithSlot(i);
});
}
}
@@ -859,7 +864,7 @@ void RomBrowserWidget::on_searchWidget_SearchTextChanged(const QString& text)
void RomBrowserWidget::on_listViewWidget_sortIndicatorChanged(int logicalIndex, Qt::SortOrder sortOrder)
{
CoreSettingsSetValue(SettingsID::RomBrowser_ListViewSortSection, logicalIndex);
CoreSettingsSetValue(SettingsID::RomBrowser_ListViewSortOrder, (int)sortOrder);
CoreSettingsSetValue(SettingsID::RomBrowser_ListViewSortOrder, static_cast<int>(sortOrder));
}
void RomBrowserWidget::on_listViewWidget_sectionResized(int logicalIndex, int oldWidth, int newWidth)
@@ -972,7 +977,7 @@ void RomBrowserWidget::on_RomBrowserThread_RomsFound(QList<RomSearcherThreadData
void RomBrowserWidget::on_RomBrowserThread_Finished(bool canceled)
{
// sort data
this->listViewProxyModel->sort(this->listViewSortSection, (Qt::SortOrder)this->listViewSortOrder);
this->listViewProxyModel->sort(this->listViewSortSection, static_cast<Qt::SortOrder>(this->listViewSortOrder));
this->gridViewProxyModel->sort(0, Qt::SortOrder::AscendingOrder);
// retrieve column settings
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
file="./mupen64plus.cfg"
device_name=""
current_button=""
echo " {"
while read -r line
do
if [[ "$line" == "DeviceName ="* ]] ||
[[ "$line" == "DeviceType ="* ]]
then
key="$(cut -d'=' -f1 <<< $line | tr -d ' ')"
value="$(cut -d'=' -f2 <<< $line | awk '{$1=$1};1')"
if [[ "$line" == "DeviceName ="* ]]
then
value="[ $value ]"
fi
echo " \"$key\": $value,"
elif [[ "$line" == "A_"* ]] ||
[[ "$line" == "B_"* ]] ||
[[ "$line" == "Start_"* ]] ||
[[ "$line" == "Dpad"* ]] ||
[[ "$line" == "CButton"* ]] ||
[[ "$line" == *"Trigger"* ]] ||
[[ "$line" == "AnalogStick"* ]]
then
key="$(cut -d'=' -f1 <<< $line | tr -d ' ')"
value="$(cut -d'=' -f2 <<< $line | tr -d ' ')"
echo " \"$key\": $value,"
fi
done < "$file"
echo " },"
+1 -1
View File
@@ -1 +1 @@
v0.8.5
v0.8.8