Merge branch 'PCSX2:master' into master

This commit is contained in:
987123879113 2022-06-21 09:34:38 +09:00 committed by GitHub
commit b1d016887a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
98 changed files with 2843 additions and 1071 deletions

1
.gitignore vendored
View File

@ -68,6 +68,7 @@ oprofile_data/
/.vscode*
/bin/**/*.dll
/bin/**/*.dmp
/bin/**/*.exp
/bin/**/*.ilk
/bin/**/*.lib

View File

@ -241,6 +241,24 @@ PBPX-95224:
PBPX-95228:
name: "DVD Player Version 3.00"
region: "NTSC-J"
PBPX-95239:
name: "Online Start-Up Disc v3.0"
region: "NTSC-U"
PBPX-95242:
name: "Online Start-Up Disc v3.0"
region: "NTSC-U"
PBPX-95245:
name: "Online Start-Up Disc v1.0"
region: "NTSC-U"
PBPX-95246:
name: "Online Start-Up Disc v3.5 - Broadband Only"
region: "NTSC-U"
PBPX-95247:
name: "Online Start-Up Disc v3.5 - Broadband Only"
region: "NTSC-U"
PBPX-95248:
name: "Online Start-Up Disc 4.0 - Broadband Only"
region: "NTSC-U"
PBPX-95503:
name: "Gran Turismo 3 - A-Spec [PS2 Bundle]"
region: "NTSC-U"
@ -388,8 +406,10 @@ SCAJ-10008:
deinterlace: 4 # Game requires bob bff deinterlacing when auto.
alignSprite: 1 # Fixes vertical lines.
SCAJ-10009:
name: "Psikyo Shooting Collection Vol.1 - Strikers 1&2"
name: "Psikyo Shooting Collection Vol.1 - Strikers 1-2"
region: "NTSC-Unk"
gameFixes:
- EETimingHack # Fixes part II intro screen.
SCAJ-10010:
name: "Mahjong Party - Play Mahjong with Swimsuit Beauty"
region: "NTSC-Unk"
@ -2975,7 +2995,7 @@ SCES-51176:
- EETimingHack # Fixes hang before going ingame.
SCES-51177:
name: "Disney's Treasure Planet"
region: "PAL"
region: "PAL-M5"
clampModes:
vuClampMode: 3 # Fixes SPS when the solar surfer's engines are inactive.
gameFixes:
@ -3034,7 +3054,7 @@ SCES-51533:
roundSprite: 1 # Fixes vertical lines and minor ghosting.
wrapGSMem: 1 # Fixes FMV when in progressive mode.
SCES-51578:
name: "Network Access Disc [Original, v4.02 & v4.03]"
name: "Network Access Disc [v1.03 - v6.00]"
region: "PAL-M7"
SCES-51592:
name: "Formula One 2003"
@ -3935,6 +3955,9 @@ SCES-54499:
SCES-54500:
name: "Buzz! El Mega Concurso"
region: "PAL-S"
SCES-54505:
name: "Buzz! The Mega Quiz"
region: "PAL-M3"
SCES-54507:
name: "Buzz! The Mega Quiz"
region: "PAL-M3"
@ -4000,6 +4023,9 @@ SCES-54601:
SCES-54625:
name: "Buzz! Junior - Monster Rumble"
region: "PAL-M7"
SCES-54637:
name: "Gaelic Games - Hurling"
region: "PAL-M2"
SCES-54638:
name: "Gaelic Games - Football 2"
region: "PAL-E-GA"
@ -4096,6 +4122,9 @@ SCES-54909:
SCES-54910:
name: "SingStar Rock Ballads"
region: "PAL-S"
SCES-54924:
name: "NBA 08"
region: "PAL-M5"
SCES-55019:
name: "Ratchet & Clank - Size Matters"
region: "PAL-M5"
@ -4137,6 +4166,9 @@ SCES-55094:
SCES-55097:
name: "Buzz! The Pop Quiz"
region: "PAL-FI"
SCES-55098:
name: "Buzz! The Pop Quiz"
region: "PAL-M3"
SCES-55127:
name: "SingStar Summer Party"
region: "PAL-NL"
@ -4336,6 +4368,9 @@ SCES-55571:
SCES-55573:
name: "MotorStorm - Arctic Edge"
region: "PAL-M14"
SCES-55591:
name: "Street Cricket Champions"
region: "PAL-IN"
SCES-55594:
name: "SingStar Vasco"
region: "PAL-I"
@ -4369,12 +4404,15 @@ SCES-55640:
SCES-55641:
name: "SingStar Après-Ski Party 2"
region: "PAL-G"
SCES-55649:
name: "Cart Kings"
region: "PAL-IN"
SCES-55650:
name: "SingStar SuomiSuosikit"
region: "PAL-FI"
SCES-55661:
name: "RA.ONE - The Game"
region: "PAL-E"
region: "PAL-IN"
compat: 5
SCES-55662:
name: "Chandragupta - Warrior Prince"
@ -7446,7 +7484,7 @@ SCUS-97424:
name: "Kiosk Demo Disc Q3-Q4 2005"
region: "NTSC-U"
SCUS-97425:
name: "Network Adapter Start-Up Disc Ver.3.0"
name: "Online Start-Up Disc v3.0"
region: "NTSC-U"
SCUS-97427:
name: "Hot Shots Golf FORE! [Online Public Beta]"
@ -12155,8 +12193,10 @@ SLES-51735:
name: "Perfect Ace - Pro Tournament Tennis"
region: "PAL-M5"
SLES-51741:
name: "1945 I & II - The Arcade Games"
name: "1945 I-II - The Arcade Games"
region: "PAL-M5"
gameFixes:
- EETimingHack # Fixes part II into screen.
SLES-51746:
name: "Space Invaders - Invasion Day"
region: "PAL-M5"
@ -14017,6 +14057,9 @@ SLES-52638:
SLES-52639:
name: "V8 Supercars 2"
region: "PAL-M5"
SLES-52640:
name: "Dance-UK XL"
region: "PAL-UK"
SLES-52641:
name: "Leisure Suit Larry - Magna Cum Laude (Uncut)"
region: "PAL-E"
@ -14055,7 +14098,7 @@ SLES-52652:
region: "PAL-E"
SLES-52653:
name: "Liverpool FC - Club Football 2005"
region: "PAL-Unk"
region: "PAL-M3"
SLES-52654:
name: "Club Football - Real Madri 2005"
region: "PAL-M5"
@ -14593,10 +14636,10 @@ SLES-52902:
name: "Arcade Classics Vol.1"
region: "PAL-E"
SLES-52906:
name: "Eyetoy -Spongebob and Friends - Movin'"
name: "Nickelodeon SpongeBob SquarePants - Movin' with Friends"
region: "PAL-E"
SLES-52907:
name: "Eyetoy - Spongebob and Friends - Movin'"
name: "Nickelodeon SpongeBob SquarePants - Movin' with Friends"
region: "PAL-M4"
SLES-52908:
name: "Urbz, The - Sims in the City"
@ -15269,16 +15312,22 @@ SLES-53155:
compat: 5
gsHWFixes:
halfPixelOffset: 1 # Fixes bloom misalignment.
gameFixes:
- SoftwareRendererFMVHack # Fixes FMVs.
SLES-53156:
name: "Star Wars - Episode III - La Revanche des Sith"
region: "PAL-F"
gsHWFixes:
halfPixelOffset: 1 # Fixes bloom misalignment.
gameFixes:
- SoftwareRendererFMVHack # Fixes FMVs.
SLES-53157:
name: "Star Wars - Episode III - Die Rache der Sith"
region: "PAL-G"
gsHWFixes:
halfPixelOffset: 1 # Fixes bloom misalignment.
gameFixes:
- SoftwareRendererFMVHack # Fixes FMVs.
SLES-53158:
name: "Cold Fear"
region: "PAL-M5"
@ -18748,7 +18797,7 @@ SLES-54666:
region: "PAL-M11"
compat: 5
SLES-54668:
name: "EyeToy - Thomas & Friends - A Day at the Races"
name: "Thomas & Friends - A Day at the Races"
region: "PAL-E"
SLES-54669:
name: "Lassie"
@ -19529,6 +19578,9 @@ SLES-54983:
SLES-54984:
name: "Water Horse, The - Legend of the Deep"
region: "PAL-M11"
SLES-54985:
name: "Thomas & Friends - A Day at the Races"
region: "PAL-M4"
SLES-54986:
name: "Bratz - The Movie"
region: "PAL-E-F"
@ -20698,6 +20750,9 @@ SLES-55537:
SLES-55542:
name: "Disney Sing It - Pop Hits"
region: "PAL-M6"
SLES-55544:
name: "Guitar Hero - Greatest Hits"
region: "PAL-M5"
SLES-55545:
name: "WWE SmackDown vs. Raw 2010"
region: "PAL-M5"
@ -21184,9 +21239,11 @@ SLKA-15004:
name: "Gunbird - Premium Package"
region: "NTSC-J"
SLKA-15005:
name: "Strikers 1945 III"
name: "Strikers 1945 I-II"
region: "NTSC-J"
compat: 5
gameFixes:
- EETimingHack # Fixes part II intro screen.
SLKA-15007:
name: "GrowLanser2"
region: "NTSC-K"
@ -21195,7 +21252,7 @@ SLKA-15007:
- OPHFlagHack
SLKA-15008:
name: "Choro Q HG2"
region: "NTSC-E-F-G"
region: "NTSC-K"
gsHWFixes:
roundSprite: 2 # Fixes sprite ghosting.
SLKA-15021:
@ -23889,9 +23946,11 @@ SLPM-62514:
name: "Sim People [EA Best Hits]"
region: "NTSC-J"
SLPM-62515:
name: "Psikyo Shooting Collection Vol.1 - Strikers 1945 1&2"
name: "Psikyo Shooting Collection Vol.1 - Strikers 1945 1-2"
region: "NTSC-J"
compat: 5
gameFixes:
- EETimingHack # Fixes part II intro screen.
SLPM-62516:
name: "EyeToy Sports - Let's Play Sports!"
region: "NTSC-J"
@ -24312,8 +24371,10 @@ SLPM-62652:
roundSprite: 1 # Fixes HUD artifacts.
wildArmsHack: 1 # Lessens the bloom misalignment but still an issue.
SLPM-62653:
name: "Psikyo Shooting Collection Vol.1 - Strikers 1945 1&2 [Taito The Best]"
name: "Psikyo Shooting Collection Vol.1 - Strikers 1945 1-2 [Taito The Best]"
region: "NTSC-J"
gameFixes:
- EETimingHack # Fixes part II intro screen.
SLPM-62655:
name: "Sega Ages 2500 Series Vol.11 - Hokuto no Ken"
region: "NTSC-J"
@ -28073,6 +28134,8 @@ SLPM-66046:
region: "NTSC-J"
gsHWFixes:
halfPixelOffset: 1 # Fixes bloom misalignment.
gameFixes:
- SoftwareRendererFMVHack # Fixes FMVs.
SLPM-66047:
name: "FIFA Street"
region: "NTSC-J"
@ -29734,6 +29797,10 @@ SLPM-66514:
SLPM-66515:
name: "Star Wars - Episode III - Sith no Fukushuu [EA Best Hits]"
region: "NTSC-J"
gsHWFixes:
halfPixelOffset: 1 # Fixes bloom misalignment.
gameFixes:
- SoftwareRendererFMVHack # Fixes FMVs.
SLPM-66516:
name: "Sims, The & The Urbz - Sims in the City [EA Best Hits]"
region: "NTSC-J"
@ -32331,6 +32398,9 @@ SLPS-20194:
SLPS-20196:
name: "Akagawa Jiro - Tsuki no Hikari - Shizumeru Kane no Satsujin"
region: "NTSC-J"
SLPS-20197:
name: "Surfing Air Show with RatBoy"
region: "NTSC-J"
SLPS-20198:
name: "Raging Bless"
region: "NTSC-J"
@ -40327,6 +40397,8 @@ SLUS-20831:
compat: 5
gsHWFixes:
alignSprite: 1 # Fixes vertical lines.
wildArmsHack: 1 # Improves visual clarity whilst upscaling.
roundSprite: 1 # Reduces graphics garbage on UI whilst upscaling.
SLUS-20833:
name: "Mega Man Anniversary Collection"
region: "NTSC-U"
@ -41841,6 +41913,8 @@ SLUS-21143:
compat: 5
gsHWFixes:
halfPixelOffset: 1 # Fixes bloom misalignment.
gameFixes:
- SoftwareRendererFMVHack # Fixes FMVs.
SLUS-21144:
name: "Tom Clancy's Rainbow Six - Lockdown"
region: "NTSC-U"

View File

@ -47,10 +47,10 @@
03000000c82d00000130000000000000,8BitDo SF30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000060000000000000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000061000000000000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
03000000102800000900000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d000021ab000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00003028000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000030000000000000,8BitDo SN30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000102800000900000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d000021ab000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00003028000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000030000000000000,8BitDo SN30,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00001290000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d000020ab000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00004028000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
@ -63,7 +63,7 @@
03000000c82d00000260000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000261000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
03000000a00500003232000000000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00003032000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
030000008f0e00001200000000000000,Acme GA02,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,
03000000c01100000355000000000000,Acrux,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
@ -91,7 +91,7 @@
03000000120c0000100e000000000000,Armor 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000490b00004406000000000000,ASCII Seamic Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
03000000869800002500000000000000,Astro C40 TR PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000a30c00002700000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
03000000a30c00002700000000000000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
03000000a30c00002800000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
03000000e4150000103f000000000000,Batarang,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000d6200000e557000000000000,Batarang PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
@ -117,7 +117,7 @@
030000006b1400000103000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,
03000000120c0000200e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000120c0000210e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000120c0000310c000000000000,Brook Super Converter,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
03000000120c0000310c000000000000,Brook Super Converter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
03000000d81d00000b00000000000000,Buffalo BSGP1601 Series,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows,
030000005b1c00002400000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows,
030000005b1c00002500000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows,
@ -178,7 +178,7 @@
03000000151900004000000000000000,Flydigi Vader 2,a:b11,b:b10,back:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,leftstick:b1,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b0,righttrigger:b4,rightx:a3,righty:a4,start:b2,x:b9,y:b8,platform:Windows,
03000000b40400001124000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b4,paddle2:b5,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,y:b3,platform:Windows,
03000000b40400001224000000000000,Flydigi Vader 2 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,paddle3:b17,paddle4:b18,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,
030000008305000000a0000000000000,G08XU,a:b0,b:b1,back:b4,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b5,x:b2,y:b3,platform:Windows,
030000008305000000a0000000000000,G08XU,a:b0,b:b1,back:b4,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b5,x:b2,y:b3,platform:Windows,
0300000066f700000100000000000000,Game VIB Joystick,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows,
03000000260900002625000000000000,GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows,
03000000341a000005f7000000000000,GameCube Controller,a:b2,b:b3,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b1,y:b0,platform:Windows,
@ -186,7 +186,7 @@
03000000790000004718000000000000,GameCube Controller,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,
03000000790000004618000000000000,GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,
030000008f0e00000d31000000000000,Gamepad 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000280400000140000000000000,GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
03000000280400000140000000000000,GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,dpdown:+a1,dpleft:-a0,dpright:+a3,dpup:-a4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
03000000ac0500003d03000000000000,GameSir G3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000ac0500005b05000000000000,GameSir G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000ac0500002d02000000000000,GameSir G4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
@ -211,7 +211,7 @@
030000004f04000026b3000000000000,GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
0300000079000000d418000000000000,GPD Win,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000c6240000025b000000000000,GPX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000007d0400000540000000000000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
030000007d0400000540000000000000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
030000008f0e00000610000000000000,GreenAsia,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a5,righty:a2,start:b11,x:b3,y:b0,platform:Windows,
03000000ac0500006b05000000000000,GT2a,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
@ -231,7 +231,6 @@
030000000d0f00008600000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000000d0f0000ba00000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000000d0f00001000000000000000,Hori Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000000f0d00000010000000000000,Hori Fightstick 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f00003200000000000000,Hori Fightstick 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f0000c000000000000000,Hori Fightstick 4,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000000d0f00000d00000000000000,Hori Fightstick EX2,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,
@ -285,7 +284,7 @@
030000000d0f00006700000000000000,Horipad One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000000d0f0000dc00000000000000,Horipad Switch,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000008f0e00001330000000000000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Windows,
03000000790000004e95000000000000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Windows,
03000000790000004e95000000000000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Windows,
03000000d81d00000e00000000000000,iBuffalo AC02 Arcade Joystick,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,rightx:a2,righty:a5,start:b8,x:b4,y:b5,platform:Windows,
03000000d81d00000f00000000000000,iBuffalo BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000d81d00001000000000000000,iBuffalo BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
@ -293,9 +292,10 @@
03000000696400006964000000000000,iDroidCon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000b50700001403000000000000,Impact Black,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,
030000006f0e00002401000000000000,Injustice Fightstick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
03000000830500005130000000000000,InterAct ActionPad,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
03000000830500005130000000000000,InterAct ActionPad,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
03000000ef0500000300000000000000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows,
03000000fd0500000230000000000000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a5,start:b11,x:b0,y:b1,platform:Windows,
03000000fd0500000030000000000000,Interact GoPad,a:b3,b:b4,x:b0,y:b1,leftshoulder:b6,rightshoulder:b2,leftx:a0,lefty:a1,lefttrigger:b7,righttrigger:b5,platform:Windows,
03000000fd0500003902000000000000,InterAct Hammerhead,a:b3,b:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b2,lefttrigger:b8,rightshoulder:b7,rightstick:b5,righttrigger:b9,start:b10,x:b0,y:b1,platform:Windows,
03000000fd0500002a26000000000000,InterAct Hammerhead FX,a:b3,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b0,y:b1,platform:Windows,
03000000fd0500002f26000000000000,InterAct Hammerhead FX,a:b4,b:b5,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b1,y:b2,platform:Windows,
@ -344,7 +344,6 @@
03000000380700008483000000000000,Mad Catz Fightstick TE S PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000380700008134000000000000,Mad Catz Fightstick TE2 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000380700008184000000000000,Mad Catz Fightstick TE2 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
78696e70757403000000000000000000,Mad Catz Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows,
03000000380700006252000000000000,Mad Catz Micro CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,
03000000380700008232000000000000,Mad Catz PlayStation Brawlpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000380700008731000000000000,Mad Catz PlayStation Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
@ -370,7 +369,7 @@
03000000790000000018000000000000,Mayflash Wii U Pro Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000790000002418000000000000,Mega Drive Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b2,start:b9,x:b3,y:b4,platform:Windows,
0300000079000000ae18000000000000,Mega Drive Controller,a:b0,b:b1,back:b7,dpdown:b14,dpleft:b15,dpright:b13,dpup:b2,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
03000000c0160000990a000000000000,Mega Drive Controller,a:b0,b:b1,leftx:a0,lefty:a1,righttrigger:b2,start:b3,platform:Windows,
03000000c0160000990a000000000000,Mega Drive Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,righttrigger:b2,start:b3,platform:Windows,
030000005e0400002800000000000000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Windows,
030000005e0400000300000000000000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,
030000005e0400000700000000000000,Microsoft SideWinder,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
@ -401,9 +400,8 @@
0300000038070000efbe000000000000,NEO SE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
0300000092120000474e000000000000,NeoGeo X Arcade Stick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b3,y:b2,platform:Windows,
03000000921200004b46000000000000,NES 2 port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows,
03000000000f00000100000000000000,NES Controller,a:b1,b:b0,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows,
03000000571d00002100000000000000,NES Controller,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows,
03000000921200004346000000000000,NES Controller,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows,
03000000000f00000100000000000000,NES Controller,a:b1,b:b0,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Windows,
03000000921200004346000000000000,NES Controller,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Windows,
03000000790000004518000000000000,NEXILUX GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,
030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Windows,
03000000050b00000045000000000000,Nexus,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Windows,
@ -515,10 +513,10 @@
030000009b2800000200000000000000,Raphnet NES Adapter,a:b7,b:b6,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b4,platform:Windows,
030000009b2800004400000000000000,Raphnet PS1 and PS2 Adapter,a:b1,b:b2,back:b5,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b9,rightx:a3,righty:a4,start:b4,x:b0,y:b3,platform:Windows,
030000009b2800004300000000000000,Raphnet Saturn,a:b0,b:b1,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,
030000009b2800000500000000000000,Raphnet Saturn Adapter 2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,
030000009b2800000500000000000000,Raphnet Saturn Adapter 2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,
030000009b2800000300000000000000,Raphnet SNES Adapter,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
030000009b2800005600000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,
030000009b2800005700000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,
030000009b2800005600000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,
030000009b2800005700000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,
030000009b2800001e00000000000000,Raphnet Vectrex Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a1,lefty:a2,x:b2,y:b3,platform:Windows,
030000009b2800002b00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,
030000009b2800002c00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,
@ -534,12 +532,13 @@
03000000321500000910000000000000,Razer Raiju UE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000321500000011000000000000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000321500000009000000000000,Razer Serval,+lefty:+a2,-lefty:-a1,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,leftx:a0,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000921200004547000000000000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b1,x:b3,y:b4,start:b6,rightshoulder:b5,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,lefttrigger:b7,righttrigger:b2,platform:Windows,
03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,
03000000830500006020000000000000,Retro Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b8,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows,
03000000bd12000013d0000000000000,Retrolink Sega Saturn Classic Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,lefttrigger:b6,rightshoulder:b2,righttrigger:b7,start:b8,x:b3,y:b4,platform:Windows,
03000000bd12000015d0000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,
0300000000f000000300000000000000,RetroUSB RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,
0300000000f00000f100000000000000,RetroUSB Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,
0300000000f000000300000000000000,RetroUSB RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,
0300000000f00000f100000000000000,RetroUSB Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,
03000000830500000960000000000000,Revenger,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b3,x:b4,y:b5,platform:Windows,
030000006b140000010d000000000000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000006b140000020d000000000000,Revolution Pro Controller 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
@ -581,26 +580,25 @@
03000000411200004550000000000000,Sanwa Micro Grip Pro,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a1,righty:a2,start:b9,x:b1,y:b3,platform:Windows,
03000000c01100004150000000000000,Sanwa Micro Grip Pro,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,
03000000c01100004450000000000000,Sanwa Online Grip,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b11,righttrigger:b9,rightx:a3,righty:a2,start:b14,x:b3,y:b4,platform:Windows,
03000000730700000401000000000000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows,
03000000730700000401000000000000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Windows,
03000000830500006120000000000000,Sanwa Smart Grip II,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,x:b1,y:b3,platform:Windows,
03000000c01100000051000000000000,Satechi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
030000004f04000028b3000000000000,Score A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000952e00002577000000000000,Scuf PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000a30c00002500000000000000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Windows,
03000000a30c00002400000000000000,Sega Mega Drive Mini 6B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
0300000000050000289b000000000000,Sega Saturn Adapter,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,
0300000000f000000800000000000000,Sega Saturn Controller,a:b1,b:b2,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b3,start:b0,x:b5,y:b6,platform:Windows,
0300000000050000289b000000000000,Sega Saturn Adapter,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,
0300000000f000000800000000000000,Sega Saturn Controller,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,righttrigger:b3,start:b0,x:b5,y:b6,platform:Windows,
03000000730700000601000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
03000000b40400000a01000000000000,Sega Saturn Controller,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,
03000000b40400000a01000000000000,Sega Saturn Controller,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,
030000003b07000004a1000000000000,SFX,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Windows,
03000000f82100001900000000000000,Shogun Bros Chameleon X1,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
03000000120c00001c1e000000000000,SnakeByte 4S PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
0300000003040000c197000000000000,SNES Controller,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
03000000571d00002000000000000000,SNES Controller,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,
0300000081170000960a000000000000,SNES Controller,a:b4,b:b0,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b5,y:b1,platform:Windows,
0300000003040000c197000000000000,SNES Controller,a:b0,b:b4,back:b2,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
0300000081170000960a000000000000,SNES Controller,a:b4,b:b0,back:b2,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b3,x:b5,y:b1,platform:Windows,
03000000811700009d0a000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
030000008b2800000300000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
03000000921200004653000000000000,SNES Controller,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
03000000921200004653000000000000,SNES Controller,a:b0,b:b4,back:b2,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
03000000ff000000cb01000000000000,Sony PlayStation Portable,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,
03000000341a00000208000000000000,Speedlink 6555,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows,
03000000341a00000908000000000000,Speedlink 6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
@ -614,12 +612,12 @@
03000000110100001914000000000000,SteelSeries,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000381000001214000000000000,SteelSeries Free,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,
03000000110100003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000381000003014000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000381000003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000381000001814000000000000,SteelSeries Stratus XL,a:b0,b:b1,back:b18,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b19,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b2,y:b3,platform:Windows,
03000000790000001c18000000000000,STK 7024X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000381000003014000000000000,Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000381000003114000000000000,Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000380700003847000000000000,Street Fighter Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,start:b7,x:b2,y:b3,platform:Windows,
030000001f08000001e4000000000000,Super Famicom Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,
030000001f08000001e4000000000000,Super Famicom Controller,a:b2,b:b1,back:b8,leftshoulder:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,
03000000790000000418000000000000,Super Famicom Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b33,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,
03000000341200001300000000000000,Super Racer,a:b2,b:b3,back:b8,leftshoulder:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b7,x:b0,y:b1,platform:Windows,
03000000d620000011a7000000000000,Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
@ -645,6 +643,8 @@
030000006d04000088ca000000000000,Thunderpad,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,
03000000666600000488000000000000,TigerGame PlayStation Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
030000004f04000007d0000000000000,TMini,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000571d00002100000000000000,Tomee NES Controller Adapter,a:b1,b:b0,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,start:b3,platform:Windows,
03000000571d00002000000000000000,Tomee SNES Controller Adapter,a:b0,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,
03000000d62000006000000000000000,Tournament PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000c01100000055000000000000,Tronsmart,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
030000005f140000c501000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
@ -738,7 +738,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000a30c00002400000006020000,8BitDo M30,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,guide:b9,leftshoulder:b6,lefttrigger:b5,rightshoulder:b4,righttrigger:b7,start:b8,x:b3,y:b0,platform:Mac OS X,
03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000c82d00005106000000010000,8BitDo M30,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00005106000000010000,8BitDo M30,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:a5,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00001590000001000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
030000003512000012ab000001000000,8BitDo NES30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
@ -753,8 +753,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00000231000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00000331000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00000431000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000102800000900000000000000,8BitDo SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00001290000001000000,8BitDo SN30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000102800000900000000000000,8BitDo SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00001290000001000000,8BitDo SN30,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00004028000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00000160000001000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Mac OS X,
@ -762,11 +762,11 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00000261000000010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000a00500003232000009010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000c82d00001890000001000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00001890000001000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a31,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000491900001904000001010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Mac OS X,
03000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,
03000000a30c00002700000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000a30c00002700000003030000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000a30c00002800000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000c62400001a89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,
@ -786,7 +786,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000c01100000140000000010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000006f0e00000102000000000000,GameStop Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000007d0400000540000001010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
030000007d0400000540000001010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000280400000140000000020000,Gravis Gamepad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
030000008f0e00000300000007010000,GreenAsia Joystick,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Mac OS X,
030000000d0f00002d00000000100000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
@ -805,8 +805,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000000d0f0000ee00000000010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000008f0e00001330000011010000,HuiJia SNES Controller,a:b4,b:b2,back:b16,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b12,rightshoulder:b14,start:b18,x:b6,y:b0,platform:Mac OS X,
03000000790000004e95000000010000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Mac OS X,
03000000830500006020000000000000,iBuffalo Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,
03000000830500006020000000000000,iBuffalo Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,
03000000ef0500000300000000020000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Mac OS X,
03000000fd0500000030000010010000,Interact GoPad,a:b3,b:b4,x:b0,y:b1,leftshoulder:b6,rightshoulder:b2,leftx:a0,lefty:a1,lefttrigger:b7,righttrigger:b5,platform:Mac OS X,
030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Mac OS X,
030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000242f00002d00000007010000,JYS Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
@ -832,7 +833,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
0300000025090000e803000000000000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X,
03000000790000000318000000010000,Mayflash Wii DolphinBar,a:b8,b:b12,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b44,leftshoulder:b16,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b4,platform:Mac OS X,
03000000790000000018000000000000,Mayflash Wii U Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,
03000000790000000018000000010000,Mayflash Wii U Pro Adapter Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,
03000000790000000018000000010000,Mayflash Wii U Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,
030000005e0400002800000002010000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Mac OS X,
030000005e0400002700000001010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Mac OS X,
03000000d62000007162000001000000,Moga Pro 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,
@ -850,7 +851,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000550900001472000025050000,NVIDIA Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Mac OS X,
030000006f0e00000901000002010000,PDP Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
030000008f0e00000300000000000000,Piranha Xtreme PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,
030000004c050000da0c000000010000,PlayStation Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
030000004c050000da0c000000010000,PlayStation Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
030000004c0500003713000000010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000d62000006dca000000010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000100800000300000006010000,PS2 Adapter,a:b2,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
@ -874,6 +875,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,
0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000000d0f0000c100000072050000,Retro Bit Sega Genesis 6B Controller,a:b2,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b8,rightshoulder:b6,righttrigger:b7,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000921200004547000000020000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b2,x:b6,y:b8,start:b12,rightshoulder:b10,dpup:-a2,dpdown:+a2,dpleft:-a0,dpright:+a0,lefttrigger:b14,righttrigger:b4,platform:Mac OS X,
03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000790000001100000005010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000830500006020000000010000,Retro Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b8,righttrigger:b9,start:b7,x:b2,y:b3,platform:Mac OS X,
@ -881,17 +883,17 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000006b140000130d000000010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000c6240000fefa000000000000,Rock Candy PS3,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Mac OS X,
03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Mac OS X,
03000000a30c00002500000006020000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Mac OS X,
03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X,
03000000b40400000a01000000000000,Sega Saturn,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X,
030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000b40400000a01000000000000,Sega Saturn,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X,
030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
0300000000f00000f100000000000000,SNES RetroPort,a:b2,b:b3,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,rightshoulder:b7,start:b6,x:b0,y:b1,platform:Mac OS X,
030000004c050000a00b000000000000,Sony DualShock 4 Adapter,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000d11800000094000000010000,Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,
030000005e0400008e02000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X,
03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,
03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,
03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,
05000000484944204465766963650000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,platform:Mac OS X,
@ -934,6 +936,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000130b000009050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000130b000013050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000120c0000100e000000010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000120c0000101e000000010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
@ -948,7 +951,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000c82d00005106000000010000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Linux,
03000000c82d00001590000011010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
05000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
03000000008000000210000011010000,8BitDo NES30,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
03000000008000000210000011010000,8BitDo NES30,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
03000000c82d00000310000011010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux,
05000000c82d00008010000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux,
03000000022000000090000011010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
@ -965,9 +968,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000c82d00000061000000010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
030000003512000012ab000010010000,8BitDo SFC30,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Linux,
030000003512000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
05000000102800000900000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
05000000c82d00003028000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
05000000102800000900000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
05000000c82d00003028000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d00000160000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,
@ -977,9 +980,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00000260000011010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
05000000c82d00000261000000010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
05000000202800000900000000010000,8BitDo SNES30,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
05000000a00500003232000001000000,8BitDo Zero,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,
05000000a00500003232000001000000,8BitDo Zero,a:b0,b:b1,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,
05000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,
03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
050000005e040000e002000030110000,8BitDo Zero 2,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,
05000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
03000000c01100000355000011010000,Acrux Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
@ -993,7 +996,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000491900001904000011010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux,
05000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,
03000000790000003018000011010000,Arcade Fightstick F300,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
03000000a30c00002700000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
03000000a30c00002700000011010000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
03000000a30c00002800000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
05000000050b00000045000031000000,Asus Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,
05000000050b00000045000040000000,Asus Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,
@ -1045,14 +1048,14 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000451300000010000010010000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
03000000f0250000c183000010010000,Goodbetterbest Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000007d0400000540000000010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
03000000280400000140000000010000,Gravis Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
030000007d0400000540000000010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
03000000280400000140000000010000,Gravis Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
030000008f0e00000610000000010000,GreenAsia Electronics Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux,
030000008f0e00001200000010010000,GreenAsia Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,
0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
03000000f0250000c383000010010000,GT VX2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
06000000adde0000efbe000002010000,Hidromancer Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000d81400000862000011010000,HitBox PS3 PC Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux,
03000000d81400000862000011010000,HitBox PS3 PC Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux,
03000000c9110000f055000011010000,HJC Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
03000000632500002605000010010000,HJDX,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
030000000d0f00000d00000000010000,Hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux,
@ -1089,7 +1092,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000d80400008200000003000000,IMS PCU0,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,platform:Linux,
03000000120c00000500000010010000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux,
03000000ef0500000300000000010000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux,
03000000fd0500000030000000010000,InterAct GoPad I73000,a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux,
03000000fd0500000030000000010000,InterAct GoPad,a:b3,b:b4,x:b0,y:b1,leftshoulder:b6,rightshoulder:b2,leftx:a0,lefty:a1,lefttrigger:b7,righttrigger:b5,platform:Linux,
03000000fd0500002a26000000010000,InterAct HammerHead FX,a:b3,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux,
0500000049190000020400001b010000,Ipega PG 9069,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b161,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000632500007505000011010000,Ipega PG 9099,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
@ -1145,7 +1148,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000780000000600000010010000,Microntek Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
030000005e0400002800000000010000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Linux,
030000005e0400000e00000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,
030000005e0400000700000000010000,Microsoft SideWinder Gamepad,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Linux,
030000005e0400000700000000010000,Microsoft SideWinder Gamepad,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Linux,
030000005e0400002700000000010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Linux,
030000005e0400008502000000010000,Microsoft Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,
030000005e0400008e02000001000000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.1,dpleft:h0.2,dpright:h0.8,dpup:h0.4,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
@ -1181,7 +1184,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
0300000092120000474e000000010000,NeoGeo X Arcade Stick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b3,y:b2,platform:Linux,
03000000790000004518000010010000,Nexilux GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Linux,
030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Linux,
050000004e696d6275732b0000000000,Nimbus Plus,a:b0,b:b1,back:b10,guide:b11,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Linux,
060000007e0500003713000000000000,Nintendo 3DS,a:b0,b:b1,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,
030000007e0500003703000000016800,Nintendo GameCube Controller,a:b0,b:b2,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b1,y:b3,platform:Linux,
03000000790000004618000010010000,Nintendo GameCube Controller Adapter,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5~,righty:a2~,start:b9,x:b2,y:b3,platform:Linux,
@ -1273,6 +1275,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
050000004c050000e60c000000810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
03000000300f00001211000011010000,Qanba Arcade Joystick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux,
03000000300f00001210000010010000,Qanba Joystick Plus,a:b0,b:b1,back:b8,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,start:b9,x:b2,y:b3,platform:Linux,
03000000222c00000223000011010000,Qanba Obsidian Arcade Joystick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000222c00000023000011010000,Qanba Obsidian Arcade Joystick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
030000009b2800000300000001010000,Raphnet 4nes4snes,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,
030000009b2800004200000001010000,Raphnet Dual NES Adapter,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Linux,
030000009b2800003200000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,
@ -1316,8 +1320,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000a30c00002500000011010000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Linux,
03000000790000001100000011010000,Sega Saturn,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Linux,
03000000790000002201000011010000,Sega Saturn,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,
03000000b40400000a01000000010000,Sega Saturn,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux,
030000001f08000001e4000010010000,SFC Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,
03000000b40400000a01000000010000,Sega Saturn,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux,
030000001f08000001e4000010010000,SFC Controller,a:b2,b:b1,back:b8,leftshoulder:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,
03000000632500002305000010010000,ShanWan Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000f025000021c1000010010000,Shanwan Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000632500007505000010010000,Shanwan PS3 PC,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
@ -1341,14 +1345,15 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,
05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,
03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b10,guide:b11,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Linux,
03000000381000003014000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000381000003114000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
0500000011010000311400001b010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b32,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
05000000110100001914000009010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000ad1b000038f0000090040000,Street Fighter IV Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000003b07000004a1000000010000,Suncom SFX Plus,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Linux,
030000003b07000004a1000000010000,Suncom SFX Plus,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Linux,
03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,
0300000000f00000f100000000010000,Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,
0300000000f00000f100000000010000,Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,
030000008f0e00000d31000010010000,SZMY Power 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000457500002211000010010000,SZMY Power Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
030000008f0e00001431000010010000,SZMY Power PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,

View File

@ -284,7 +284,7 @@ void Texture::TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, u
cmdlist->ResourceBarrier(1, &barrier);
}
bool Texture::BeginStreamUpdate(u32 level, u32 x, u32 y, u32 width, u32 height, void** out_data, u32* out_data_pitch)
ID3D12GraphicsCommandList* Texture::BeginStreamUpdate(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, void** out_data, u32* out_data_pitch)
{
const u32 copy_pitch = Common::AlignUpPow2(width * GetTexelSize(m_format), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
const u32 upload_size = copy_pitch * height;
@ -297,16 +297,19 @@ bool Texture::BeginStreamUpdate(u32 level, u32 x, u32 y, u32 width, u32 height,
if (!g_d3d12_context->GetTextureStreamBuffer().ReserveMemory(upload_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT))
{
Console.Error("Failed to reserve %u bytes for %ux%u upload", upload_size, width, height);
return false;
return nullptr;
}
// cmdlist change
return g_d3d12_context->GetInitCommandList();
}
*out_data = g_d3d12_context->GetTextureStreamBuffer().GetCurrentHostPointer();
*out_data_pitch = copy_pitch;
return true;
return cmdlist;
}
void Texture::EndStreamUpdate(u32 level, u32 x, u32 y, u32 width, u32 height)
void Texture::EndStreamUpdate(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height)
{
const u32 copy_pitch = Common::AlignUpPow2(width * GetTexelSize(m_format), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
const u32 upload_size = copy_pitch * height;
@ -315,10 +318,10 @@ void Texture::EndStreamUpdate(u32 level, u32 x, u32 y, u32 width, u32 height)
const u32 sb_offset = sb.GetCurrentOffset();
sb.CommitMemory(upload_size);
CopyFromBuffer(level, x, y, width, height, copy_pitch, sb.GetBuffer(), sb_offset);
CopyFromBuffer(cmdlist, level, x, y, width, height, copy_pitch, sb.GetBuffer(), sb_offset);
}
void Texture::CopyFromBuffer(u32 level, u32 x, u32 y, u32 width, u32 height, u32 pitch, ID3D12Resource* buffer, u32 buffer_offset)
void Texture::CopyFromBuffer(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, u32 pitch, ID3D12Resource* buffer, u32 buffer_offset)
{
D3D12_TEXTURE_COPY_LOCATION src;
src.pResource = buffer;
@ -337,7 +340,6 @@ void Texture::CopyFromBuffer(u32 level, u32 x, u32 y, u32 width, u32 height, u32
const D3D12_BOX src_box{0u, 0u, 0u, width, height, 1u};
const D3D12_RESOURCE_STATES old_state = m_state;
ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList();
TransitionToState(cmdlist, D3D12_RESOURCE_STATE_COPY_DEST);
cmdlist->CopyTextureRegion(&dst, x, y, 0, &src, &src_box);
TransitionToState(cmdlist, old_state);
@ -381,7 +383,7 @@ static ID3D12Resource* CreateStagingBuffer(u32 height, const void* data, u32 pit
return resource.get();
}
bool Texture::LoadData(u32 level, u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch)
bool Texture::LoadData(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch)
{
const u32 texel_size = GetTexelSize(m_format);
const u32 upload_pitch = Common::AlignUpPow2(width * texel_size, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
@ -392,17 +394,17 @@ bool Texture::LoadData(u32 level, u32 x, u32 y, u32 width, u32 height, const voi
if (!staging_buffer)
return false;
CopyFromBuffer(level, x, y, width, height, upload_pitch, staging_buffer, 0);
CopyFromBuffer(cmdlist, level, x, y, width, height, upload_pitch, staging_buffer, 0);
return true;
}
void* write_ptr;
u32 write_pitch;
if (!BeginStreamUpdate(level, x, y, width, height, &write_ptr, &write_pitch))
if (!(cmdlist = BeginStreamUpdate(cmdlist, level, x, y, width, height, &write_ptr, &write_pitch)))
return false;
StringUtil::StrideMemCpy(write_ptr, write_pitch, data, pitch, std::min(pitch, upload_pitch), height);
EndStreamUpdate(level, x, y, width, height);
EndStreamUpdate(cmdlist, level, x, y, width, height);
return true;
}

View File

@ -74,10 +74,10 @@ namespace D3D12
Texture& operator=(Texture&& texture);
// NOTE: Does not handle compressed formats.
bool BeginStreamUpdate(u32 level, u32 x, u32 y, u32 width, u32 height, void** out_data, u32* out_data_pitch);
void EndStreamUpdate(u32 level, u32 x, u32 y, u32 width, u32 height);
bool LoadData(u32 level, u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch);
void CopyFromBuffer(u32 level, u32 x, u32 y, u32 width, u32 height, u32 pitch, ID3D12Resource* buffer, u32 buffer_offset);
ID3D12GraphicsCommandList* BeginStreamUpdate(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, void** out_data, u32* out_data_pitch);
void EndStreamUpdate(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height);
bool LoadData(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch);
void CopyFromBuffer(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, u32 pitch, ID3D12Resource* buffer, u32 buffer_offset);
private:
static bool CreateSRVDescriptor(ID3D12Resource* resource, u32 levels, DXGI_FORMAT format, DescriptorHandle* dh);

View File

@ -13,10 +13,10 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "common/PrecompiledHeader.h"
#include "common/GL/ContextWGL.h"
#include "common/Assertions.h"
#include "common/Console.h"
#include "ContextWGL.h"
#include "common/ScopedGuard.h"
static void* GetProcAddressCallback(const char* name)
{
@ -43,8 +43,7 @@ namespace GL
if (m_rc)
wglDeleteContext(m_rc);
if (m_dc)
ReleaseDC(GetHWND(), m_dc);
ReleaseDC();
}
std::unique_ptr<Context> ContextWGL::Create(const WindowInfo& wi, const Version* versions_to_try,
@ -66,7 +65,7 @@ namespace GL
}
else
{
Console.Error("PBuffer not implemented");
Console.Error("ContextWGL must always start with a valid surface.");
return false;
}
@ -102,11 +101,7 @@ namespace GL
{
const bool was_current = (wglGetCurrentContext() == m_rc);
if (m_dc)
{
ReleaseDC(GetHWND(), m_dc);
m_dc = {};
}
ReleaseDC();
m_wi = new_wi;
if (!InitializeDC())
@ -187,7 +182,7 @@ namespace GL
return context;
}
bool ContextWGL::InitializeDC()
HDC ContextWGL::GetDCAndSetPixelFormat(HWND hwnd)
{
PIXELFORMATDESCRIPTOR pfd = {};
pfd.nSize = sizeof(pfd);
@ -200,26 +195,154 @@ namespace GL
pfd.cBlueBits = 8;
pfd.cColorBits = 24;
m_dc = GetDC(GetHWND());
if (!m_dc)
HDC hDC = ::GetDC(hwnd);
if (!hDC)
{
Console.Error("GetDC() failed: 0x%08X", GetLastError());
return false;
}
const int pf = ChoosePixelFormat(m_dc, &pfd);
if (pf == 0)
if (!m_pixel_format.has_value())
{
Console.Error("ChoosePixelFormat() failed: 0x%08X", GetLastError());
return false;
const int pf = ChoosePixelFormat(hDC, &pfd);
if (pf == 0)
{
Console.Error("ChoosePixelFormat() failed: 0x%08X", GetLastError());
::ReleaseDC(hwnd, hDC);
return false;
}
m_pixel_format = pf;
}
if (!SetPixelFormat(m_dc, pf, &pfd))
if (!SetPixelFormat(hDC, m_pixel_format.value(), &pfd))
{
Console.Error("SetPixelFormat() failed: 0x%08X", GetLastError());
::ReleaseDC(hwnd, hDC);
return {};
}
return hDC;
}
bool ContextWGL::InitializeDC()
{
if (m_wi.type == WindowInfo::Type::Win32)
{
m_dc = GetDCAndSetPixelFormat(GetHWND());
if (!m_dc)
{
Console.Error("Failed to get DC for window");
return false;
}
return true;
}
else if (m_wi.type == WindowInfo::Type::Surfaceless)
{
return CreatePBuffer();
}
else
{
Console.Error("Unknown window info type %u", static_cast<unsigned>(m_wi.type));
return false;
}
}
void ContextWGL::ReleaseDC()
{
if (m_pbuffer)
{
wglReleasePbufferDCARB(m_pbuffer, m_dc);
m_dc = {};
wglDestroyPbufferARB(m_pbuffer);
m_pbuffer = {};
::ReleaseDC(m_dummy_window, m_dummy_dc);
m_dummy_dc = {};
DestroyWindow(m_dummy_window);
m_dummy_window = {};
}
else if (m_dc)
{
::ReleaseDC(GetHWND(), m_dc);
m_dc = {};
}
}
bool ContextWGL::CreatePBuffer()
{
static bool window_class_registered = false;
static const wchar_t* window_class_name = L"ContextWGLPBuffer";
if (!window_class_registered)
{
WNDCLASSEXW wc = {};
wc.cbSize = sizeof(WNDCLASSEXW);
wc.style = 0;
wc.lpfnWndProc = DefWindowProcW;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(nullptr);
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = window_class_name;
wc.hIconSm = NULL;
if (!RegisterClassExW(&wc))
{
Console.Error("(ContextWGL::CreatePBuffer) RegisterClassExW() failed");
return false;
}
window_class_registered = true;
}
HWND hwnd = CreateWindowExW(0, window_class_name, window_class_name, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
if (!hwnd)
{
Console.Error("(ContextWGL::CreatePBuffer) CreateWindowEx() failed");
return false;
}
ScopedGuard hwnd_guard([hwnd]() { DestroyWindow(hwnd); });
HDC hdc = GetDCAndSetPixelFormat(hwnd);
if (!hdc)
return false;
ScopedGuard hdc_guard([hdc, hwnd]() { ::ReleaseDC(hwnd, hdc); });
static constexpr const int pb_attribs[] = {0, 0};
pxAssertRel(m_pixel_format.has_value(), "Has pixel format for pbuffer");
HPBUFFERARB pbuffer = wglCreatePbufferARB(hdc, m_pixel_format.value(), 1, 1, pb_attribs);
if (!pbuffer)
{
Console.Error("(ContextWGL::CreatePBuffer) wglCreatePbufferARB() failed");
return false;
}
ScopedGuard pbuffer_guard([pbuffer]() { wglDestroyPbufferARB(pbuffer); });
m_dc = wglGetPbufferDCARB(pbuffer);
if (!m_dc)
{
Console.Error("(ContextWGL::CreatePbuffer) wglGetPbufferDCARB() failed");
return false;
}
m_dummy_window = hwnd;
m_dummy_dc = hdc;
m_pbuffer = pbuffer;
pbuffer_guard.Cancel();
hdc_guard.Cancel();
hwnd_guard.Cancel();
return true;
}

View File

@ -24,6 +24,7 @@
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <optional>
namespace GL
{
@ -48,12 +49,24 @@ namespace GL
private:
__fi HWND GetHWND() const { return static_cast<HWND>(m_wi.window_handle); }
HDC GetDCAndSetPixelFormat(HWND hwnd);
bool Initialize(const Version* versions_to_try, size_t num_versions_to_try);
bool InitializeDC();
void ReleaseDC();
bool CreatePBuffer();
bool CreateAnyContext(HGLRC share_context, bool make_current);
bool CreateVersionContext(const Version& version, HGLRC share_context, bool make_current);
HDC m_dc = {};
HGLRC m_rc = {};
// Can't change pixel format once it's set for a RC.
std::optional<int> m_pixel_format;
// Dummy window for creating a PBuffer off when we're surfaceless.
HWND m_dummy_window = {};
HDC m_dummy_dc = {};
HPBUFFERARB m_pbuffer = {};
};
} // namespace GL

View File

@ -634,8 +634,14 @@ namespace Vulkan
vkGetDeviceQueue(m_device, m_present_queue_family_index, 0, &m_present_queue);
}
m_gpu_timing_supported = (queue_family_properties[m_graphics_queue_family_index].timestampValidBits > 0);
DevCon.WriteLn("GPU timing is %s", m_gpu_timing_supported ? "supported" : "not supported");
m_gpu_timing_supported = (m_device_properties.limits.timestampComputeAndGraphics != 0 &&
queue_family_properties[m_graphics_queue_family_index].timestampValidBits > 0 &&
m_device_properties.limits.timestampPeriod > 0);
DevCon.WriteLn("GPU timing is %s (TS=%u TS valid bits=%u, TS period=%f)",
m_gpu_timing_supported ? "supported" : "not supported",
static_cast<u32>(m_device_properties.limits.timestampComputeAndGraphics),
queue_family_properties[m_graphics_queue_family_index].timestampValidBits,
m_device_properties.limits.timestampPeriod);
ProcessDeviceExtensions();
return true;
@ -845,6 +851,7 @@ namespace Vulkan
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateQueryPool failed: ");
m_gpu_timing_supported = false;
return false;
}
}
@ -959,9 +966,10 @@ namespace Vulkan
return time;
}
void Context::SetEnableGPUTiming(bool enabled)
bool Context::SetEnableGPUTiming(bool enabled)
{
m_gpu_timing_enabled = enabled && m_gpu_timing_supported;
return (enabled == m_gpu_timing_enabled);
}
void Context::WaitForCommandBufferCompletion(u32 index)
@ -1199,8 +1207,8 @@ namespace Vulkan
// if we didn't write the timestamp at the start of the cmdbuffer (just enabled timing), the first TS will be zero
if (timestamps[0] > 0)
{
const u64 ns_diff = (timestamps[1] - timestamps[0]) * static_cast<u64>(m_device_properties.limits.timestampPeriod);
m_accumulated_gpu_time += static_cast<double>(ns_diff) / 1000000.0;
const double ns_diff = (timestamps[1] - timestamps[0]) * static_cast<double>(m_device_properties.limits.timestampPeriod);
m_accumulated_gpu_time += ns_diff / 1000000.0;
}
}
else

View File

@ -221,7 +221,7 @@ namespace Vulkan
void WaitForGPUIdle();
float GetAndResetAccumulatedGPUTime();
void SetEnableGPUTiming(bool enabled);
bool SetEnableGPUTiming(bool enabled);
private:
Context(VkInstance instance, VkPhysicalDevice physical_device);

View File

@ -79,7 +79,7 @@
<PropertyGroup>
<MocDefines></MocDefines>
<MocDefines Condition="!$(Configuration.Contains(Debug))">-DQT_NO_DEBUG -DNDEBUG $(MocDefines)</MocDefines>
<MocIncludes>"-I$(QtIncludeDir)" "-I$(SolutionDir)pcsx2" "-I$(SolutionDir)" -I.</MocIncludes>
<MocIncludes>-I"$(QtIncludeDir)." -I"$(SolutionDir)pcsx2" "-I$(SolutionDir)." -I.</MocIncludes>
</PropertyGroup>
<Target Name="QtMoc"
BeforeTargets="ClCompile"

View File

@ -67,6 +67,9 @@ target_sources(pcsx2-qt PRIVATE
Settings/EmulationSettingsWidget.cpp
Settings/EmulationSettingsWidget.h
Settings/EmulationSettingsWidget.ui
Settings/FolderSettingsWidget.cpp
Settings/FolderSettingsWidget.h
Settings/FolderSettingsWidget.ui
Settings/GameFixSettingsWidget.cpp
Settings/GameFixSettingsWidget.h
Settings/GameFixSettingsWidget.ui

View File

@ -33,7 +33,9 @@
#include <QtGui/QWindowStateChangeEvent>
#include <cmath>
#if !defined(_WIN32) && !defined(APPLE)
#if defined(_WIN32)
#include "common/RedtapeWindows.h"
#elif !defined(APPLE)
#include <qpa/qplatformnativeinterface.h>
#endif
@ -50,7 +52,13 @@ DisplayWidget::DisplayWidget(QWidget* parent)
setMouseTracking(true);
}
DisplayWidget::~DisplayWidget() = default;
DisplayWidget::~DisplayWidget()
{
#ifdef _WIN32
if (m_clip_mouse_enabled)
ClipCursor(nullptr);
#endif
}
qreal DisplayWidget::devicePixelRatioFromScreen() const
{
@ -110,27 +118,96 @@ std::optional<WindowInfo> DisplayWidget::getWindowInfo()
return wi;
}
void DisplayWidget::setRelativeMode(bool enabled)
void DisplayWidget::updateRelativeMode(bool master_enable)
{
if (m_relative_mouse_enabled == enabled)
bool relative_mode = master_enable && InputManager::HasPointerAxisBinds();
#ifdef _WIN32
// prefer ClipCursor() over warping movement when we're using raw input
bool clip_cursor = relative_mode && false /*InputManager::IsUsingRawInput()*/;
if (m_relative_mouse_enabled == relative_mode && m_clip_mouse_enabled == clip_cursor)
return;
if (enabled)
{
m_relative_mouse_start_position = QCursor::pos();
DevCon.WriteLn("updateRelativeMode(): relative=%s, clip=%s", relative_mode ? "yes" : "no", clip_cursor ? "yes" : "no");
const QPoint center_pos = mapToGlobal(QPoint(width() / 2, height() / 2));
QCursor::setPos(center_pos);
m_relative_mouse_last_position = center_pos;
if (!clip_cursor && m_clip_mouse_enabled)
{
m_clip_mouse_enabled = false;
ClipCursor(nullptr);
}
#else
if (m_relative_mouse_enabled == relative_mode)
return;
DevCon.WriteLn("updateRelativeMode(): relative=%s", relative_mode ? "yes" : "no");
#endif
if (relative_mode)
{
#ifdef _WIN32
m_relative_mouse_enabled = !clip_cursor;
m_clip_mouse_enabled = clip_cursor;
#else
m_relative_mouse_enabled = true;
#endif
m_relative_mouse_start_pos = QCursor::pos();
updateCenterPos();
grabMouse();
}
else
else if (m_relative_mouse_enabled)
{
QCursor::setPos(m_relative_mouse_start_position);
m_relative_mouse_enabled = false;
QCursor::setPos(m_relative_mouse_start_pos);
releaseMouse();
}
m_relative_mouse_enabled = enabled;
}
void DisplayWidget::updateCursor(bool master_enable)
{
#ifdef _WIN32
const bool hide = master_enable && (m_should_hide_cursor || m_relative_mouse_enabled || m_clip_mouse_enabled);
#else
const bool hide = master_enable && (m_should_hide_cursor || m_relative_mouse_enabled);
#endif
if (m_cursor_hidden == hide)
return;
m_cursor_hidden = hide;
if (hide)
setCursor(Qt::BlankCursor);
else
unsetCursor();
}
void DisplayWidget::updateCenterPos()
{
#ifdef _WIN32
if (m_clip_mouse_enabled)
{
RECT rc;
if (GetWindowRect(reinterpret_cast<HWND>(winId()), &rc))
ClipCursor(&rc);
}
else if (m_relative_mouse_enabled)
{
RECT rc;
if (GetWindowRect(reinterpret_cast<HWND>(winId()), &rc))
{
m_relative_mouse_center_pos.setX(((rc.right - rc.left) / 2) + rc.left);
m_relative_mouse_center_pos.setY(((rc.bottom - rc.top) / 2) + rc.top);
SetCursorPos(m_relative_mouse_center_pos.x(), m_relative_mouse_center_pos.y());
}
}
#else
if (m_relative_mouse_enabled)
{
// we do a round trip here because these coordinates are dpi-unscaled
m_relative_mouse_center_pos = mapToGlobal(QPoint((width() + 1) / 2, (height() + 1) / 2));
QCursor::setPos(m_relative_mouse_center_pos);
m_relative_mouse_center_pos = QCursor::pos();
}
#endif
}
QPaintEngine* DisplayWidget::paintEngine() const
@ -172,7 +249,10 @@ bool DisplayWidget::event(QEvent* event)
m_keys_pressed_with_modifiers.push_back(key);
}
emit windowKeyEvent(key, pressed);
Host::RunOnCPUThread([key, pressed]() {
InputManager::InvokeEvents(InputManager::MakeHostKeyboardKey(key), static_cast<float>(pressed));
});
return true;
}
@ -184,22 +264,39 @@ bool DisplayWidget::event(QEvent* event)
{
const qreal dpr = devicePixelRatioFromScreen();
const QPoint mouse_pos = mouse_event->pos();
const int scaled_x = static_cast<int>(static_cast<qreal>(mouse_pos.x()) * dpr);
const int scaled_y = static_cast<int>(static_cast<qreal>(mouse_pos.y()) * dpr);
windowMouseMoveEvent(scaled_x, scaled_y);
const float scaled_x = static_cast<float>(static_cast<qreal>(mouse_pos.x()) * dpr);
const float scaled_y = static_cast<float>(static_cast<qreal>(mouse_pos.y()) * dpr);
InputManager::UpdatePointerAbsolutePosition(0, scaled_x, scaled_y);
}
else
{
const QPoint center_pos = mapToGlobal(QPoint((width() + 1) / 2, (height() + 1) / 2));
const QPoint mouse_pos = mapToGlobal(mouse_event->pos());
// On windows, we use winapi here. The reason being that the coordinates in QCursor
// are un-dpi-scaled, so we lose precision at higher desktop scalings.
float dx = 0.0f, dy = 0.0f;
const int dx = mouse_pos.x() - center_pos.x();
const int dy = mouse_pos.y() - center_pos.y();
m_relative_mouse_last_position.setX(m_relative_mouse_last_position.x() + dx);
m_relative_mouse_last_position.setY(m_relative_mouse_last_position.y() + dy);
windowMouseMoveEvent(m_relative_mouse_last_position.x(), m_relative_mouse_last_position.y());
QCursor::setPos(center_pos);
#ifndef _WIN32
const QPoint mouse_pos = QCursor::pos();
if (mouse_pos != m_relative_mouse_center_pos)
{
dx = static_cast<float>(mouse_pos.x() - m_relative_mouse_center_pos.x());
dy = static_cast<float>(mouse_pos.y() - m_relative_mouse_center_pos.y());
QCursor::setPos(m_relative_mouse_center_pos);
}
#else
POINT mouse_pos;
if (GetCursorPos(&mouse_pos))
{
dx = static_cast<float>(mouse_pos.x - m_relative_mouse_center_pos.x());
dy = static_cast<float>(mouse_pos.y - m_relative_mouse_center_pos.y());
SetCursorPos(m_relative_mouse_center_pos.x(), m_relative_mouse_center_pos.y());
}
#endif
if (dx != 0.0f)
InputManager::UpdatePointerRelativeDelta(0, InputPointerAxis::X, dx);
if (dy != 0.0f)
InputManager::UpdatePointerRelativeDelta(0, InputPointerAxis::Y, dy);
}
return true;
@ -211,10 +308,16 @@ bool DisplayWidget::event(QEvent* event)
{
unsigned long button_index;
if (_BitScanForward(&button_index, static_cast<u32>(static_cast<const QMouseEvent*>(event)->button())))
emit windowMouseButtonEvent(static_cast<int>(button_index + 1u), event->type() != QEvent::MouseButtonRelease);
{
Host::RunOnCPUThread([button_index, pressed = (event->type() != QEvent::MouseButtonRelease)]() {
InputManager::InvokeEvents(InputManager::MakePointerButtonKey(0, button_index), static_cast<float>(pressed));
});
}
// don't toggle fullscreen when we're bound.. that wouldn't end well.
if (event->type() == QEvent::MouseButtonDblClick &&
static_cast<const QMouseEvent*>(event)->button() == Qt::LeftButton &&
!InputManager::HasAnyBindingsForKey(InputManager::MakePointerButtonKey(0, 0)) &&
Host::GetBoolSettingValue("UI", "DoubleClickTogglesFullscreen", true))
{
g_emu_thread->toggleFullscreen();
@ -225,8 +328,18 @@ bool DisplayWidget::event(QEvent* event)
case QEvent::Wheel:
{
const QWheelEvent* wheel_event = static_cast<QWheelEvent*>(event);
emit windowMouseWheelEvent(wheel_event->angleDelta());
// wheel delta is 120 as in winapi
const QPoint delta_angle(static_cast<QWheelEvent*>(event)->angleDelta());
constexpr float DELTA = 120.0f;
const float dx = std::clamp(static_cast<float>(delta_angle.x()) / DELTA, -1.0f, 1.0f);
if (dx != 0.0f)
InputManager::UpdatePointerRelativeDelta(0, InputPointerAxis::WheelX, dx);
const float dy = std::clamp(static_cast<float>(delta_angle.y()) / DELTA, -1.0f, 1.0f);
if (dy != 0.0f)
InputManager::UpdatePointerRelativeDelta(0, InputPointerAxis::WheelY, dy);
return true;
}
@ -249,6 +362,13 @@ bool DisplayWidget::event(QEvent* event)
emit windowResizedEvent(scaled_width, scaled_height, dpr);
}
updateCenterPos();
return true;
}
case QEvent::Move:
{
updateCenterPos();
return true;
}

View File

@ -30,30 +30,37 @@ public:
QPaintEngine* paintEngine() const override;
__fi void setShouldHideCursor(bool hide) { m_should_hide_cursor = hide; }
int scaledWindowWidth() const;
int scaledWindowHeight() const;
qreal devicePixelRatioFromScreen() const;
std::optional<WindowInfo> getWindowInfo();
void setRelativeMode(bool enabled);
void updateRelativeMode(bool master_enable);
void updateCursor(bool master_enable);
Q_SIGNALS:
void windowFocusEvent();
void windowResizedEvent(int width, int height, float scale);
void windowRestoredEvent();
void windowKeyEvent(int key_code, bool pressed);
void windowMouseMoveEvent(int x, int y);
void windowMouseButtonEvent(int button, bool pressed);
void windowMouseWheelEvent(const QPoint& angle_delta);
protected:
bool event(QEvent* event) override;
private:
QPoint m_relative_mouse_start_position{};
QPoint m_relative_mouse_last_position{};
void updateCenterPos();
QPoint m_relative_mouse_start_pos{};
QPoint m_relative_mouse_center_pos{};
bool m_relative_mouse_enabled = false;
#ifdef _WIN32
bool m_clip_mouse_enabled = false;
#endif
bool m_should_hide_cursor = false;
bool m_cursor_hidden = false;
std::vector<int> m_keys_pressed_with_modifiers;
u32 m_last_window_width = 0;

View File

@ -47,7 +47,6 @@
#include "QtUtils.h"
EmuThread* g_emu_thread = nullptr;
WindowInfo g_gs_window_info;
static std::unique_ptr<HostDisplay> s_host_display;
@ -412,6 +411,17 @@ void EmuThread::reloadGameSettings()
}
}
void EmuThread::updateEmuFolders()
{
if (!isOnEmuThread())
{
QMetaObject::invokeMethod(this, &EmuThread::updateEmuFolders, Qt::QueuedConnection);
return;
}
Host::Internal::UpdateEmuFolders();
}
void EmuThread::loadOurSettings()
{
m_verbose_status = Host::GetBaseBoolSettingValue("UI", "VerboseStatusBar", false);
@ -419,6 +429,8 @@ void EmuThread::loadOurSettings()
void EmuThread::checkForSettingChanges()
{
QMetaObject::invokeMethod(g_main_window, &MainWindow::checkForSettingChanges, Qt::QueuedConnection);
if (VMManager::HasValidVM())
{
const bool render_to_main = Host::GetBaseBoolSettingValue("UI", "RenderToMainWindow", true);
@ -585,29 +597,11 @@ void EmuThread::connectDisplaySignals(DisplayWidget* widget)
connect(widget, &DisplayWidget::windowFocusEvent, this, &EmuThread::onDisplayWindowFocused);
connect(widget, &DisplayWidget::windowResizedEvent, this, &EmuThread::onDisplayWindowResized);
// connect(widget, &DisplayWidget::windowRestoredEvent, this, &EmuThread::redrawDisplayWindow);
connect(widget, &DisplayWidget::windowKeyEvent, this, &EmuThread::onDisplayWindowKeyEvent);
connect(widget, &DisplayWidget::windowMouseMoveEvent, this, &EmuThread::onDisplayWindowMouseMoveEvent);
connect(widget, &DisplayWidget::windowMouseButtonEvent, this, &EmuThread::onDisplayWindowMouseButtonEvent);
connect(widget, &DisplayWidget::windowMouseWheelEvent, this, &EmuThread::onDisplayWindowMouseWheelEvent);
}
void EmuThread::onDisplayWindowMouseMoveEvent(int x, int y) {}
void EmuThread::onDisplayWindowMouseButtonEvent(int button, bool pressed)
{
InputManager::InvokeEvents(InputManager::MakeHostMouseButtonKey(button), pressed ? 1.0f : 0.0f);
}
void EmuThread::onDisplayWindowMouseWheelEvent(const QPoint& delta_angle) {}
void EmuThread::onDisplayWindowKeyEvent(int key, bool pressed)
{
InputManager::InvokeEvents(InputManager::MakeHostKeyboardKey(key), pressed ? 1.0f : 0.0f);
}
void EmuThread::onDisplayWindowResized(int width, int height, float scale)
{
if (!VMManager::HasValidVM())
if (!s_host_display)
return;
GetMTGS().ResizeDisplayWindow(width, height, scale);
@ -683,8 +677,6 @@ HostDisplay* EmuThread::acquireHostDisplay(HostDisplay::RenderAPI api)
return nullptr;
}
g_gs_window_info = s_host_display->GetWindowInfo();
Console.WriteLn(Color_StrongGreen, "%s Graphics Driver Info:", HostDisplay::RenderAPIToString(s_host_display->GetRenderAPI()));
Console.Indent().WriteLn(s_host_display->GetDriverInfo());
@ -695,17 +687,8 @@ void EmuThread::releaseHostDisplay()
{
ImGuiManager::Shutdown();
if (s_host_display)
{
s_host_display->DestroyRenderSurface();
s_host_display->DestroyRenderDevice();
}
g_gs_window_info = WindowInfo();
emit onDestroyDisplayRequested();
s_host_display.reset();
emit onDestroyDisplayRequested();
}
HostDisplay* Host::GetHostDisplay()

View File

@ -70,6 +70,7 @@ public Q_SLOTS:
void setSurfaceless(bool surfaceless);
void applySettings();
void reloadGameSettings();
void updateEmuFolders();
void toggleSoftwareRendering();
void switchRenderer(GSRendererType renderer);
void changeDisc(const QString& path);
@ -142,12 +143,8 @@ private:
private Q_SLOTS:
void stopInThread();
void doBackgroundControllerPoll();
void onDisplayWindowMouseMoveEvent(int x, int y);
void onDisplayWindowMouseButtonEvent(int button, bool pressed);
void onDisplayWindowMouseWheelEvent(const QPoint& delta_angle);
void onDisplayWindowResized(int width, int height, float scale);
void onDisplayWindowFocused();
void onDisplayWindowKeyEvent(int key, bool pressed);
private:
QThread* m_ui_thread;

View File

@ -26,12 +26,13 @@
#include "CDVD/CDVD.h"
#include "Frontend/GameList.h"
#include "Frontend/LogSink.h"
#include "common/CrashHandler.h"
static void PrintCommandLineVersion()
{
QtHost::InitializeEarlyConsole();
Host::InitializeEarlyConsole();
std::fprintf(stderr, "%s\n", (QtHost::GetAppNameAndVersion() + QtHost::GetAppConfigSuffix()).toUtf8().constData());
std::fprintf(stderr, "https://pcsx2.net/\n");
std::fprintf(stderr, "\n");
@ -143,7 +144,7 @@ static bool ParseCommandLineOptions(int argc, char* argv[], std::shared_ptr<VMBo
}
else if (CHECK_ARG("-earlyconsolelog"))
{
QtHost::InitializeEarlyConsole();
Host::InitializeEarlyConsole();
continue;
}
else if (CHECK_ARG("--"))
@ -153,7 +154,7 @@ static bool ParseCommandLineOptions(int argc, char* argv[], std::shared_ptr<VMBo
}
else if (argv[i][0] == '-')
{
QtHost::InitializeEarlyConsole();
Host::InitializeEarlyConsole();
std::fprintf(stderr, "Unknown parameter: '%s'", argv[i]);
return false;
}
@ -172,7 +173,7 @@ static bool ParseCommandLineOptions(int argc, char* argv[], std::shared_ptr<VMBo
// or disc, we don't want to actually start.
if (autoboot && !autoboot->source_type.has_value() && autoboot->filename.empty() && autoboot->elf_override.empty())
{
QtHost::InitializeEarlyConsole();
Host::InitializeEarlyConsole();
Console.Warning("Skipping autoboot due to no boot parameters.");
autoboot.reset();
}
@ -181,7 +182,7 @@ static bool ParseCommandLineOptions(int argc, char* argv[], std::shared_ptr<VMBo
// scanning the game list).
if (QtHost::InBatchMode() && !autoboot)
{
QtHost::InitializeEarlyConsole();
Host::InitializeEarlyConsole();
Console.Warning("Disabling batch mode, because we have no autoboot.");
QtHost::SetBatchMode(false);
}

View File

@ -29,6 +29,7 @@
#include "pcsx2/CDVD/CDVDaccess.h"
#include "pcsx2/Frontend/GameList.h"
#include "pcsx2/Frontend/LogSink.h"
#include "pcsx2/GSDumpReplayer.h"
#include "pcsx2/HostDisplay.h"
#include "pcsx2/HostSettings.h"
@ -210,6 +211,7 @@ void MainWindow::connectSignals()
connect(m_ui.actionAudioSettings, &QAction::triggered, [this]() { doSettings("Audio"); });
connect(m_ui.actionMemoryCardSettings, &QAction::triggered, [this]() { doSettings("Memory Cards"); });
connect(m_ui.actionDEV9Settings, &QAction::triggered, [this]() { doSettings("Network & HDD"); });
connect(m_ui.actionFolderSettings, &QAction::triggered, [this]() { doSettings("Folders"); });
connect(
m_ui.actionControllerSettings, &QAction::triggered, [this]() { doControllerSettings(ControllerSettingsDialog::Category::GlobalSettings); });
connect(m_ui.actionHotkeySettings, &QAction::triggered, [this]() { doControllerSettings(ControllerSettingsDialog::Category::HotkeySettings); });
@ -766,6 +768,11 @@ bool MainWindow::isRenderingToMain() const
return (m_display_widget && m_display_widget->parent() == this);
}
bool MainWindow::shouldHideMouseCursor() const
{
return isRenderingFullscreen() && Host::GetBoolSettingValue("UI", "HideMouseCursor", false);
}
void MainWindow::switchToGameListView()
{
if (centralWidget() == m_game_list_widget)
@ -843,11 +850,11 @@ bool MainWindow::requestShutdown(bool allow_confirm /* = true */, bool allow_sav
if (!VMManager::HasValidVM())
return true;
// if we don't have a crc, we can't save state
// If we don't have a crc, we can't save state.
allow_save_to_state &= (m_current_game_crc != 0);
bool save_state = allow_save_to_state && EmuConfig.SaveStateOnShutdown;
// only confirm on UI thread because we need to display a msgbox
// Only confirm on UI thread because we need to display a msgbox.
if (allow_confirm && !GSDumpReplayer::IsReplayingDump() && Host::GetBaseBoolSettingValue("UI", "ConfirmShutdown", true))
{
VMLock lock(pauseAndLockVM());
@ -868,20 +875,23 @@ bool MainWindow::requestShutdown(bool allow_confirm /* = true */, bool allow_sav
return false;
save_state = save_cb->isChecked();
// Don't switch back to fullscreen when we're shutting down anyway.
lock.cancelResume();
}
g_emu_thread->shutdownVM(save_state);
if (block_until_done || QtHost::InBatchMode())
{
// we need to yield here, since the display gets destroyed
// We need to yield here, since the display gets destroyed.
while (VMManager::GetState() != VMState::Shutdown)
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1);
}
if (QtHost::InBatchMode())
{
// closing the window should shut down everything.
// Closing the window should shut down everything.
close();
}
@ -897,6 +907,12 @@ void MainWindow::requestExit()
close();
}
void MainWindow::checkForSettingChanges()
{
if (m_display_widget)
m_display_widget->updateRelativeMode(m_vm_valid && !m_vm_paused);
}
void Host::InvalidateSaveStateCache()
{
QMetaObject::invokeMethod(g_main_window, &MainWindow::invalidateSaveStateCache, Qt::QueuedConnection);
@ -1241,7 +1257,7 @@ void MainWindow::onThemeChangedFromSettings()
void MainWindow::onLoggingOptionChanged()
{
QtHost::UpdateLogging();
Host::UpdateLogging();
}
void MainWindow::onInputRecNewActionTriggered()
@ -1354,6 +1370,11 @@ void MainWindow::onVMPaused()
updateWindowTitle();
updateStatusBarWidgetVisibility();
m_status_fps_widget->setText(tr("Paused"));
if (m_display_widget)
{
m_display_widget->updateRelativeMode(false);
m_display_widget->updateCursor(false);
}
}
void MainWindow::onVMResumed()
@ -1370,7 +1391,11 @@ void MainWindow::onVMResumed()
updateStatusBarWidgetVisibility();
m_status_fps_widget->setText(m_last_fps_status);
if (m_display_widget)
{
m_display_widget->updateRelativeMode(true);
m_display_widget->updateCursor(true);
m_display_widget->setFocus();
}
}
void MainWindow::onVMStopped()
@ -1381,7 +1406,16 @@ void MainWindow::onVMStopped()
updateEmulationActions(false, false);
updateWindowTitle();
updateStatusBarWidgetVisibility();
switchToGameListView();
if (m_display_widget)
{
m_display_widget->updateRelativeMode(false);
m_display_widget->updateCursor(false);
}
else
{
switchToGameListView();
}
}
void MainWindow::onGameChanged(const QString& path, const QString& serial, const QString& name, quint32 crc)
@ -1554,6 +1588,10 @@ DisplayWidget* MainWindow::createDisplay(bool fullscreen, bool render_to_main)
updateWindowTitle();
m_display_widget->setFocus();
m_display_widget->setShouldHideCursor(shouldHideMouseCursor());
m_display_widget->updateRelativeMode(m_vm_valid && !m_vm_paused);
m_display_widget->updateCursor(m_vm_valid && !m_vm_paused);
host_display->DoneRenderContextCurrent();
return m_display_widget;
}
@ -1597,6 +1635,11 @@ DisplayWidget* MainWindow::updateDisplay(bool fullscreen, bool render_to_main, b
container->showNormal();
}
m_display_widget->setFocus();
m_display_widget->setShouldHideCursor(shouldHideMouseCursor());
m_display_widget->updateRelativeMode(m_vm_valid && !m_vm_paused);
m_display_widget->updateCursor(m_vm_valid && !m_vm_paused);
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
return m_display_widget;
}
@ -1676,6 +1719,9 @@ DisplayWidget* MainWindow::updateDisplay(bool fullscreen, bool render_to_main, b
updateWindowTitle();
m_display_widget->setFocus();
m_display_widget->setShouldHideCursor(shouldHideMouseCursor());
m_display_widget->updateRelativeMode(m_vm_valid && !m_vm_paused);
m_display_widget->updateCursor(m_vm_valid && !m_vm_paused);
QSignalBlocker blocker(m_ui.actionFullscreen);
m_ui.actionFullscreen->setChecked(fullscreen);
@ -2045,7 +2091,7 @@ void MainWindow::populateLoadStateMenu(QMenu* menu, const QString& filename, con
if (!FileSystem::StatFile(state_filename.c_str(), &sd))
continue;
action = menu->addAction(tr("Save Slot %1 (%2)").arg(i).arg(formatTimestampForSaveStateMenu(sd.ModificationTime)));
action = menu->addAction(tr("Load Slot %1 (%2)").arg(i).arg(formatTimestampForSaveStateMenu(sd.ModificationTime)));
connect(action, &QAction::triggered, [this, i]() { loadSaveStateSlot(i); });
}
}
@ -2171,7 +2217,7 @@ MainWindow::VMLock::VMLock(VMLock&& lock)
, m_was_fullscreen(lock.m_was_fullscreen)
{
lock.m_dialog_parent = nullptr;
lock.m_was_paused = false;
lock.m_was_paused = true;
lock.m_was_fullscreen = false;
}
@ -2183,3 +2229,9 @@ MainWindow::VMLock::~VMLock()
g_emu_thread->setVMPaused(false);
}
void MainWindow::VMLock::cancelResume()
{
m_was_paused = true;
m_was_fullscreen = false;
}

View File

@ -93,6 +93,7 @@ public Q_SLOTS:
void runOnUIThread(const std::function<void()>& func);
bool requestShutdown(bool allow_confirm = true, bool allow_save_to_state = true, bool block_until_done = false);
void requestExit();
void checkForSettingChanges();
private Q_SLOTS:
void onUpdateCheckComplete();
@ -184,6 +185,7 @@ private:
bool isShowingGameList() const;
bool isRenderingFullscreen() const;
bool isRenderingToMain() const;
bool shouldHideMouseCursor() const;
void switchToGameListView();
void switchToEmulationView();

View File

@ -93,6 +93,7 @@
<addaction name="actionAudioSettings"/>
<addaction name="actionMemoryCardSettings"/>
<addaction name="actionDEV9Settings"/>
<addaction name="actionFolderSettings"/>
<addaction name="actionControllerSettings"/>
<addaction name="actionHotkeySettings"/>
<addaction name="separator"/>
@ -570,6 +571,15 @@
<string>&amp;Network &amp;&amp; HDD</string>
</property>
</action>
<action name="actionFolderSettings">
<property name="icon">
<iconset theme="folder-open-line">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;Folders</string>
</property>
</action>
<action name="actionViewToolbar">
<property name="checkable">
<bool>true</bool>

View File

@ -38,6 +38,7 @@
#include "pcsx2/DebugTools/Debug.h"
#include "pcsx2/Frontend/GameList.h"
#include "pcsx2/Frontend/INISettingsInterface.h"
#include "pcsx2/Frontend/LogSink.h"
#include "pcsx2/HostSettings.h"
#include "pcsx2/PAD/Host/PAD.h"
@ -227,14 +228,6 @@ void QtHost::SetDataDirectory()
EmuFolders::DataRoot = EmuFolders::AppRoot;
}
void QtHost::UpdateFolders()
{
// TODO: This should happen with the VM thread paused.
auto lock = Host::GetSettingsLock();
EmuFolders::LoadConfig(*s_base_settings_interface.get());
EmuFolders::EnsureFoldersExist();
}
bool QtHost::InitializeConfig()
{
if (!SetCriticalFolders())
@ -260,7 +253,7 @@ bool QtHost::InitializeConfig()
// TODO: Handle reset to defaults if load fails.
EmuFolders::LoadConfig(*s_base_settings_interface.get());
EmuFolders::EnsureFoldersExist();
QtHost::UpdateLogging();
Host::UpdateLogging();
return true;
}
@ -511,359 +504,3 @@ void QtHost::HookSignals()
std::signal(SIGINT, SignalHandler);
std::signal(SIGTERM, SignalHandler);
}
// Used on both Windows and Linux.
#ifdef _WIN32
static const wchar_t s_console_colors[][ConsoleColors_Count] = {
#define CC(x) L ## x
#else
static const char s_console_colors[][ConsoleColors_Count] = {
#define CC(x) x
#endif
CC("\033[0m"), // default
CC("\033[30m\033[1m"), // black
CC("\033[32m"), // green
CC("\033[31m"), // red
CC("\033[34m"), // blue
CC("\033[35m"), // magenta
CC("\033[35m"), // orange (FIXME)
CC("\033[37m"), // gray
CC("\033[36m"), // cyan
CC("\033[33m"), // yellow
CC("\033[37m"), // white
CC("\033[30m\033[1m"), // strong black
CC("\033[31m\033[1m"), // strong red
CC("\033[32m\033[1m"), // strong green
CC("\033[34m\033[1m"), // strong blue
CC("\033[35m\033[1m"), // strong magenta
CC("\033[35m\033[1m"), // strong orange (FIXME)
CC("\033[37m\033[1m"), // strong gray
CC("\033[36m\033[1m"), // strong cyan
CC("\033[33m\033[1m"), // strong yellow
CC("\033[37m\033[1m") // strong white
};
#undef CC
static Common::Timer::Value s_log_start_timestamp = Common::Timer::GetCurrentValue();
static bool s_log_timestamps = false;
static std::mutex s_log_mutex;
// Replacement for Console so we actually get output to our console window on Windows.
#ifdef _WIN32
static bool s_debugger_attached = false;
static bool s_console_handle_set = false;
static bool s_console_allocated = false;
static HANDLE s_console_handle = INVALID_HANDLE_VALUE;
static HANDLE s_old_console_stdin = NULL;
static HANDLE s_old_console_stdout = NULL;
static HANDLE s_old_console_stderr = NULL;
static BOOL WINAPI ConsoleCtrlHandler(DWORD dwCtrlType)
{
Console.WriteLn("Handler %u", dwCtrlType);
if (dwCtrlType != CTRL_C_EVENT)
return FALSE;
SignalHandler(SIGTERM);
return TRUE;
}
static bool EnableVirtualTerminalProcessing(HANDLE hConsole)
{
if (hConsole == INVALID_HANDLE_VALUE)
return false;
DWORD old_mode;
if (!GetConsoleMode(hConsole, &old_mode))
return false;
// already enabled?
if (old_mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)
return true;
return SetConsoleMode(hConsole, old_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
}
#endif
static void ConsoleQt_SetTitle(const char* title)
{
#ifdef _WIN32
SetConsoleTitleW(StringUtil::UTF8StringToWideString(title).c_str());
#else
std::fprintf(stdout, "\033]0;%s\007", title);
#endif
}
static void ConsoleQt_DoSetColor(ConsoleColors color)
{
#ifdef _WIN32
if (s_console_handle == INVALID_HANDLE_VALUE)
return;
const wchar_t* colortext = s_console_colors[static_cast<u32>(color)];
DWORD written;
WriteConsoleW(s_console_handle, colortext, std::wcslen(colortext), &written, nullptr);
#else
const char* colortext = s_console_colors[static_cast<u32>(color)];
std::fputs(colortext, stdout);
#endif
}
static void ConsoleQt_DoWrite(const char* fmt)
{
std::unique_lock lock(s_log_mutex);
#ifdef _WIN32
if (s_console_handle != INVALID_HANDLE_VALUE || s_debugger_attached)
{
// TODO: Put this on the stack.
std::wstring wfmt(StringUtil::UTF8StringToWideString(fmt));
if (s_debugger_attached)
OutputDebugStringW(wfmt.c_str());
if (s_console_handle != INVALID_HANDLE_VALUE)
{
DWORD written;
WriteConsoleW(s_console_handle, wfmt.c_str(), static_cast<DWORD>(wfmt.length()), &written, nullptr);
}
}
#else
std::fputs(fmt, stdout);
std::fputc('\n', stdout);
#endif
if (emuLog)
{
std::fputs(fmt, emuLog);
}
}
static void ConsoleQt_DoWriteLn(const char* fmt)
{
std::unique_lock lock(s_log_mutex);
// find time since start of process, but save a syscall if we're not writing timestamps
float message_time = s_log_timestamps ?
static_cast<float>(
Common::Timer::ConvertValueToSeconds(Common::Timer::GetCurrentValue() - s_log_start_timestamp)) :
0.0f;
// split newlines up
const char* start = fmt;
do
{
const char* end = std::strchr(start, '\n');
std::string_view line;
if (end)
{
line = std::string_view(start, end - start);
start = end + 1;
}
else
{
line = std::string_view(start);
start = nullptr;
}
#ifdef _WIN32
if (s_console_handle != INVALID_HANDLE_VALUE || s_debugger_attached)
{
// TODO: Put this on the stack.
std::wstring wfmt(StringUtil::UTF8StringToWideString(line));
if (s_debugger_attached)
{
// VS already timestamps logs (at least with the productivity power tools).
if (!wfmt.empty())
OutputDebugStringW(wfmt.c_str());
OutputDebugStringW(L"\n");
}
if (s_console_handle != INVALID_HANDLE_VALUE)
{
DWORD written;
if (s_log_timestamps)
{
wchar_t timestamp_text[128];
const int timestamp_len = _swprintf(timestamp_text, L"[%10.4f] ", message_time);
WriteConsoleW(s_console_handle, timestamp_text, static_cast<DWORD>(timestamp_len), &written, nullptr);
}
if (!wfmt.empty())
WriteConsoleW(s_console_handle, wfmt.c_str(), static_cast<DWORD>(wfmt.length()), &written, nullptr);
WriteConsoleW(s_console_handle, L"\n", 1, &written, nullptr);
}
}
#else
if (s_log_timestamps)
{
std::fprintf(stdout, "[%10.4f] %.*s\n", message_time, static_cast<int>(line.length()), line.data());
}
else
{
if (!line.empty())
std::fwrite(line.data(), line.length(), 1, stdout);
std::fputc('\n', stdout);
}
#endif
if (emuLog)
{
if (s_log_timestamps)
{
std::fprintf(emuLog, "[%10.4f] %.*s\n", message_time, static_cast<int>(line.length()), line.data());
}
else
{
std::fwrite(line.data(), line.length(), 1, emuLog);
std::fputc('\n', emuLog);
}
}
} while (start);
}
static void ConsoleQt_Newline()
{
ConsoleQt_DoWriteLn("");
}
static const IConsoleWriter ConsoleWriter_WinQt =
{
ConsoleQt_DoWrite,
ConsoleQt_DoWriteLn,
ConsoleQt_DoSetColor,
ConsoleQt_DoWrite,
ConsoleQt_Newline,
ConsoleQt_SetTitle,
};
static void UpdateLoggingSinks(bool system_console, bool file_log)
{
#ifdef _WIN32
const bool debugger_attached = IsDebuggerPresent();
s_debugger_attached = debugger_attached;
if (system_console)
{
if (!s_console_handle_set)
{
s_old_console_stdin = GetStdHandle(STD_INPUT_HANDLE);
s_old_console_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
s_old_console_stderr = GetStdHandle(STD_ERROR_HANDLE);
bool handle_valid = (GetConsoleWindow() != NULL);
if (!handle_valid)
{
s_console_allocated = AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole();
handle_valid = (GetConsoleWindow() != NULL);
}
if (handle_valid)
{
s_console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
if (s_console_handle != INVALID_HANDLE_VALUE)
{
s_console_handle_set = true;
SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
// This gets us unix-style coloured output.
EnableVirtualTerminalProcessing(GetStdHandle(STD_OUTPUT_HANDLE));
EnableVirtualTerminalProcessing(GetStdHandle(STD_ERROR_HANDLE));
// Redirect stdout/stderr.
std::FILE* fp;
freopen_s(&fp, "CONIN$", "r", stdin);
freopen_s(&fp, "CONOUT$", "w", stdout);
freopen_s(&fp, "CONOUT$", "w", stderr);
}
}
}
// just in case it fails
system_console = s_console_handle_set;
}
else
{
if (s_console_handle_set)
{
s_console_handle_set = false;
SetConsoleCtrlHandler(ConsoleCtrlHandler, FALSE);
// redirect stdout/stderr back to null.
std::FILE* fp;
freopen_s(&fp, "NUL:", "w", stderr);
freopen_s(&fp, "NUL:", "w", stdout);
freopen_s(&fp, "NUL:", "w", stdin);
// release console and restore state
SetStdHandle(STD_INPUT_HANDLE, s_old_console_stdin);
SetStdHandle(STD_OUTPUT_HANDLE, s_old_console_stdout);
SetStdHandle(STD_ERROR_HANDLE, s_old_console_stderr);
s_old_console_stdin = NULL;
s_old_console_stdout = NULL;
s_old_console_stderr = NULL;
if (s_console_allocated)
{
s_console_allocated = false;
FreeConsole();
}
}
}
#else
const bool debugger_attached = false;
#endif
if (file_log)
{
if (!emuLog)
{
emuLogName = Path::Combine(EmuFolders::Logs, "emulog.txt");
emuLog = FileSystem::OpenCFile(emuLogName.c_str(), "wb");
file_log = (emuLog != nullptr);
}
}
else
{
if (emuLog)
{
std::fclose(emuLog);
emuLog = nullptr;
emuLogName = {};
}
}
// Discard logs completely if there's no sinks.
if (debugger_attached || system_console || file_log)
Console_SetActiveHandler(ConsoleWriter_WinQt);
else
Console_SetActiveHandler(ConsoleWriter_Null);
}
void QtHost::InitializeEarlyConsole()
{
UpdateLoggingSinks(true, false);
}
void QtHost::UpdateLogging()
{
const bool system_console_enabled = Host::GetBaseBoolSettingValue("Logging", "EnableSystemConsole", false);
const bool file_logging_enabled = Host::GetBaseBoolSettingValue("Logging", "EnableFileLogging", false);
s_log_timestamps = Host::GetBaseBoolSettingValue("Logging", "EnableTimestamps", true);
const bool any_logging_sinks = system_console_enabled || file_logging_enabled;
DevConWriterEnabled = any_logging_sinks && (IsDevBuild || Host::GetBaseBoolSettingValue("Logging", "EnableVerbose", false));
SysConsole.eeConsole.Enabled = any_logging_sinks && Host::GetBaseBoolSettingValue("Logging", "EnableEEConsole", false);
SysConsole.iopConsole.Enabled = any_logging_sinks && Host::GetBaseBoolSettingValue("Logging", "EnableIOPConsole", false);
// Input Recording Logs
SysConsole.recordingConsole.Enabled = any_logging_sinks && Host::GetBaseBoolSettingValue("Logging", "EnableInputRecordingLogs", true);
SysConsole.controlInfo.Enabled = any_logging_sinks && Host::GetBaseBoolSettingValue("Logging", "EnableControllerLogs", false);
UpdateLoggingSinks(system_console_enabled, file_logging_enabled);
}

View File

@ -40,12 +40,6 @@ namespace QtHost
bool Initialize();
void Shutdown();
void UpdateFolders();
void UpdateLogging();
/// Initializes early console logging (for printing command line arguments).
void InitializeEarlyConsole();
/// Sets batch mode (exit after game shutdown).
bool InBatchMode();
void SetBatchMode(bool enabled);

View File

@ -20,17 +20,23 @@
#include <QtCore/QtCore>
#include <QtGui/QAction>
#include <QtWidgets/QAbstractButton>
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QDoubleSpinBox>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QSlider>
#include <QtWidgets/QSpinBox>
#include "common/Path.h"
#include "pcsx2/Config.h"
#include "pcsx2/HostSettings.h"
#include "EmuThread.h"
#include "QtHost.h"
#include "QtUtils.h"
#include "Settings/SettingsDialog.h"
namespace SettingWidgetBinder
@ -228,7 +234,7 @@ namespace SettingWidgetBinder
static std::optional<QString> getNullableStringValue(const QCheckBox* widget)
{
return (widget->checkState() == Qt::PartiallyChecked) ?
std::nullopt :
std::nullopt :
std::optional<QString>(widget->isChecked() ? QStringLiteral("1") : QStringLiteral("0"));
}
static void setNullableStringValue(QCheckBox* widget, std::optional<QString> value)
@ -769,4 +775,70 @@ namespace SettingWidgetBinder
}
}
template <typename WidgetType>
static void BindWidgetToFolderSetting(SettingsInterface* sif, WidgetType* widget,
QAbstractButton* browse_button, QAbstractButton* open_button, QAbstractButton* reset_button,
std::string section, std::string key, std::string default_value)
{
using Accessor = SettingAccessor<WidgetType>;
std::string current_path(Host::GetBaseStringSettingValue(section.c_str(), key.c_str(), default_value.c_str()));
if (!Path::IsAbsolute(current_path))
current_path = Path::Combine(EmuFolders::DataRoot, current_path);
const QString value(QString::fromStdString(current_path));
Accessor::setStringValue(widget, value);
// if we're doing per-game settings, disable the widget, we only allow folder changes in the base config
if (sif)
{
widget->setEnabled(false);
if (browse_button)
browse_button->setEnabled(false);
if (reset_button)
reset_button->setEnabled(false);
return;
}
Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() {
const std::string new_value(Accessor::getStringValue(widget).toStdString());
if (!new_value.empty())
{
std::string relative_path(Path::MakeRelative(new_value, EmuFolders::DataRoot));
QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), relative_path.c_str());
}
else
{
QtHost::RemoveBaseSettingValue(section.c_str(), key.c_str());
}
g_emu_thread->updateEmuFolders();
});
if (browse_button)
{
QObject::connect(browse_button, &QAbstractButton::clicked, browse_button, [widget, key]() {
const QString path(QDir::toNativeSeparators(QFileDialog::getExistingDirectory(QtUtils::GetRootWidget(widget),
qApp->translate("SettingWidgetBinder", "Select folder for %1").arg(QString::fromStdString(key)))));
if (path.isEmpty())
return;
Accessor::setStringValue(widget, path);
});
}
if (open_button)
{
QObject::connect(open_button, &QAbstractButton::clicked, open_button, [widget]() {
QString path(Accessor::getStringValue(widget));
if (!path.isEmpty())
QtUtils::OpenURL(QtUtils::GetRootWidget(widget), QUrl::fromLocalFile(path));
});
}
if (reset_button)
{
QObject::connect(reset_button, &QAbstractButton::clicked, reset_button, [widget, default_value = std::move(default_value)]() {
Accessor::setStringValue(widget, QString::fromStdString(Path::Combine(EmuFolders::AppRoot, default_value)));
});
}
}
} // namespace SettingWidgetBinder

View File

@ -38,28 +38,17 @@ BIOSSettingsWidget::BIOSSettingsWidget(SettingsDialog* dialog, QWidget* parent)
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastBoot, "EmuCore", "EnableFastBoot", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.patchRegion, "EmuCore", "PatchBios", false);
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.regionComboBox, "EmuCore", "PatchRegion", BiosZoneStrings, BiosZoneBytes, BiosZoneBytes[0]);
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.searchDirectory, m_ui.browseSearchDirectory, m_ui.openSearchDirectory,
m_ui.resetSearchDirectory, "Folders", "Bios", "bios");
dialog->registerWidgetHelp(m_ui.patchRegion, tr("Patch Region"),tr("Unchecked"),
dialog->registerWidgetHelp(m_ui.patchRegion, tr("Patch Region"), tr("Unchecked"),
tr("Patches the BIOS region byte in ROM. Not recommended unless you really know what you're doing."));
dialog->registerWidgetHelp(m_ui.fastBoot, tr("Fast Boot"), tr("Unchecked"),
tr("Patches the BIOS to skip the console's boot animation."));
updateSearchDirectory();
refreshList();
connect(m_ui.searchDirectory, &QLineEdit::textChanged, [this](const QString& text) {
QtHost::SetBaseStringSettingValue("Folders", "Bios", text.toUtf8().constData());
QtHost::UpdateFolders();
refreshList();
});
connect(m_ui.resetSearchDirectory, &QPushButton::clicked, [this]() {
QtHost::RemoveBaseSettingValue("Folders", "Bios");
QtHost::UpdateFolders();
updateSearchDirectory();
refreshList();
});
connect(m_ui.browseSearchDirectory, &QPushButton::clicked, this, &BIOSSettingsWidget::browseSearchDirectory);
connect(m_ui.openSearchDirectory, &QPushButton::clicked, this, &BIOSSettingsWidget::openSearchDirectory);
connect(m_ui.searchDirectory, &QLineEdit::textChanged, this, &BIOSSettingsWidget::refreshList);
connect(m_ui.refresh, &QPushButton::clicked, this, &BIOSSettingsWidget::refreshList);
connect(m_ui.fileList, &QTreeWidget::currentItemChanged, this, &BIOSSettingsWidget::listItemChanged);
@ -89,27 +78,6 @@ void BIOSSettingsWidget::refreshList()
m_refresh_thread->start();
}
void BIOSSettingsWidget::browseSearchDirectory()
{
QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(
QtUtils::GetRootWidget(this), tr("Select Directory"), m_ui.searchDirectory->text()));
if (directory.isEmpty())
return;
m_ui.searchDirectory->setText(directory);
}
void BIOSSettingsWidget::openSearchDirectory()
{
QtUtils::OpenURL(this, QUrl::fromLocalFile(m_ui.searchDirectory->text()));
}
void BIOSSettingsWidget::updateSearchDirectory()
{
// this will generate a full path
m_ui.searchDirectory->setText(QString::fromStdString(EmuFolders::Bios));
}
void BIOSSettingsWidget::listRefreshed(const QVector<BIOSInfo>& items)
{
const std::string selected_bios(Host::GetBaseStringSettingValue("Filenames", "BIOS"));

View File

@ -47,9 +47,6 @@ public:
private Q_SLOTS:
void refreshList();
void browseSearchDirectory();
void openSearchDirectory();
void updateSearchDirectory();
void listItemChanged(const QTreeWidgetItem* current, const QTreeWidgetItem* previous);
void listRefreshed(const QVector<BIOSInfo>& items);

View File

@ -188,7 +188,7 @@
</layout>
</widget>
<resources>
<include location="resources/resources.qrc"/>
<include location="../resources/resources.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -623,10 +623,10 @@
</layout>
</widget>
</item>
<item row="2" column="1" rowspan="2" colspan="4">
<item row="2" column="3" rowspan="2" colspan="2">
<widget class="QGroupBox" name="groupBox_30">
<property name="title">
<string>Axis Scale</string>
<string>Analog Sensitivity</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
@ -642,7 +642,33 @@
<item>
<widget class="QLabel" name="AxisScaleLabel">
<property name="text">
<string>1.0x</string>
<string>100%</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="1" rowspan="2" colspan="2">
<widget class="QGroupBox" name="groupBox_33">
<property name="title">
<string>Analog Deadzone</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QSlider" name="Deadzone">
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="DeadzoneLabel">
<property name="text">
<string>0%</string>
</property>
</widget>
</item>

View File

@ -238,6 +238,21 @@ void ControllerBindingWidget_Base::initBindingWidgets()
break;
}
if (QSlider* widget = findChild<QSlider*>(QStringLiteral("Deadzone")); widget)
{
const float range = static_cast<float>(widget->maximum());
QLabel* label = findChild<QLabel*>(QStringLiteral("DeadzoneLabel"));
if (label)
{
connect(widget, &QSlider::valueChanged, this, [range, label](int value) {
label->setText(tr("%1%").arg((static_cast<float>(value) / range) * 100.0f, 0, 'f', 0));
});
}
ControllerSettingWidgetBinder::BindWidgetToInputProfileNormalized(sif, widget, config_section, "Deadzone", range,
PAD::DEFAULT_STICK_DEADZONE);
}
if (QSlider* widget = findChild<QSlider*>(QStringLiteral("AxisScale")); widget)
{
// position 1.0f at the halfway point
@ -246,11 +261,12 @@ void ControllerBindingWidget_Base::initBindingWidgets()
if (label)
{
connect(widget, &QSlider::valueChanged, this, [range, label](int value) {
label->setText(tr("%1x").arg(static_cast<float>(value) / range, 0, 'f', 2));
label->setText(tr("%1%").arg((static_cast<float>(value) / range) * 100.0f, 0, 'f', 0));
});
}
ControllerSettingWidgetBinder::BindWidgetToInputProfileNormalized(sif, widget, config_section, "AxisScale", range, 1.0f);
ControllerSettingWidgetBinder::BindWidgetToInputProfileNormalized(sif, widget, config_section, "AxisScale", range,
PAD::DEFAULT_STICK_SCALE);
}
if (QDoubleSpinBox* widget = findChild<QDoubleSpinBox*>(QStringLiteral("SmallMotorScale")); widget)

View File

@ -35,6 +35,10 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent,
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableXInputSource, "InputSources", "XInput", false);
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.multitapPort1, "Pad", "MultitapPort1", false);
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.multitapPort2, "Pad", "MultitapPort2", false);
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.pointerXInvert, "Pad", "PointerXInvert", false);
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.pointerYInvert, "Pad", "PointerYInvert", false);
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerXScale, "Pad", "PointerXScale", 8.0f);
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerYScale, "Pad", "PointerYScale", 8.0f);
if (dialog->isEditingProfile())
{
@ -56,6 +60,11 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent,
for (QCheckBox* cb : {m_ui.multitapPort1, m_ui.multitapPort2})
connect(cb, &QCheckBox::stateChanged, this, [this]() { emit bindingSetupChanged(); });
connect(m_ui.pointerXScale, &QSlider::valueChanged, this, [this](int value) { m_ui.pointerXScaleLabel->setText(QStringLiteral("%1").arg(value)); });
connect(m_ui.pointerYScale, &QSlider::valueChanged, this, [this](int value) { m_ui.pointerYScaleLabel->setText(QStringLiteral("%1").arg(value)); });
m_ui.pointerXScaleLabel->setText(QStringLiteral("%1").arg(m_ui.pointerXScale->value()));
m_ui.pointerYScaleLabel->setText(QStringLiteral("%1").arg(m_ui.pointerYScale->value()));
updateSDLOptionsEnabled();
}

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>900</width>
<height>500</height>
<height>675</height>
</rect>
</property>
<property name="windowTitle">
@ -42,20 +42,20 @@
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="enableSDLEnhancedMode">
<property name="text">
<string>DualShock 4 / DualSense Enhanced Mode</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<item row="1" column="0">
<widget class="QCheckBox" name="enableSDLSource">
<property name="text">
<string>Enable SDL Input Source</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="enableSDLEnhancedMode">
<property name="text">
<string>DualShock 4 / DualSense Enhanced Mode</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -86,6 +86,141 @@
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>Mouse/Pointer Source</string>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<item row="2" column="1">
<widget class="QSlider" name="pointerYScale">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>30</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Using raw input improves precision when you bind controller sticks to the mouse pointer. Also enables multiple mice to be used.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QCheckBox" name="pointerYInvert">
<property name="text">
<string>Invert</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="pointerXScaleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>10</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QCheckBox" name="pointerXInvert">
<property name="text">
<string>Invert</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Vertical Sensitivity:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSlider" name="pointerXScale">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>30</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="pointerYScaleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>10</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Horizontal Sensitivity:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Controller Multitap</string>
@ -101,14 +236,14 @@
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<item row="1" column="0">
<widget class="QCheckBox" name="multitapPort1">
<property name="text">
<string>Multitap on Console Port 1</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<item row="1" column="1">
<widget class="QCheckBox" name="multitapPort2">
<property name="text">
<string>Multitap on Console Port 2</string>
@ -118,7 +253,7 @@
</layout>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QGroupBox" name="profileSettings">
<property name="title">
<string>Profile Settings</string>
@ -144,7 +279,7 @@
</layout>
</widget>
</item>
<item row="4" column="0">
<item row="5" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -157,7 +292,7 @@
</property>
</spacer>
</item>
<item row="0" column="1" rowspan="5">
<item row="0" column="1" rowspan="6">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Detected Devices</string>

View File

@ -78,7 +78,7 @@ namespace ControllerSettingWidgetBinder
if (sif)
{
const float value = sif->GetFloatValue(section.c_str(), key.c_str(), default_value);
Accessor::setBoolValue(widget, value);
Accessor::setFloatValue(widget, value);
Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key)]() {
const float new_value = Accessor::getFloatValue(widget);
@ -90,7 +90,7 @@ namespace ControllerSettingWidgetBinder
else
{
const float value = Host::GetBaseFloatSettingValue(section.c_str(), key.c_str(), default_value);
Accessor::setBoolValue(widget, value);
Accessor::setFloatValue(widget, value);
Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() {
const float new_value = Accessor::getFloatValue(widget);

View File

@ -0,0 +1,38 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include <QtWidgets/QMessageBox>
#include <algorithm>
#include "FolderSettingsWidget.h"
#include "SettingWidgetBinder.h"
#include "SettingsDialog.h"
FolderSettingsWidget::FolderSettingsWidget(SettingsDialog* dialog, QWidget* parent)
: QWidget(parent)
{
SettingsInterface* sif = dialog->getSettingsInterface();
m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.cache, m_ui.cacheBrowse, m_ui.cacheOpen, m_ui.cacheReset, "Folders", "Cache", "cache");
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.covers, m_ui.coversBrowse, m_ui.coversOpen, m_ui.coversReset, "Folders", "Covers", "covers");
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.snapshots, m_ui.snapshotsBrowse, m_ui.snapshotsOpen, m_ui.snapshotsReset, "Folders", "Snapshots", "snaps");
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.saveStates, m_ui.saveStatesBrowse, m_ui.saveStatesOpen, m_ui.saveStatesReset, "Folders", "SaveStates", "sstates");
}
FolderSettingsWidget::~FolderSettingsWidget() = default;

View File

@ -0,0 +1,34 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <QtWidgets/QWidget>
#include "ui_FolderSettingsWidget.h"
class SettingsDialog;
class FolderSettingsWidget : public QWidget
{
Q_OBJECT
public:
FolderSettingsWidget(SettingsDialog* dialog, QWidget* parent);
~FolderSettingsWidget();
private:
Ui::FolderSettingsWidget m_ui;
};

View File

@ -0,0 +1,208 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FolderSettingsWidget</class>
<widget class="QWidget" name="FolderSettingsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>648</width>
<height>487</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Cache Directory</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="0">
<widget class="QLineEdit" name="cache"/>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="cacheBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="cacheOpen">
<property name="text">
<string>Open...</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="cacheReset">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Used for storing shaders, gzip indices, and game list data.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Covers Directory</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="1" column="0">
<widget class="QLineEdit" name="covers"/>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="coversBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="coversOpen">
<property name="text">
<string>Open...</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="coversReset">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Used for storing covers in the game grid/Big Picture UIs.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Snapshots Directory</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLineEdit" name="snapshots"/>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="snapshotsBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="snapshotsOpen">
<property name="text">
<string>Open...</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="snapshotsReset">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="label">
<property name="text">
<string>Used for screenshots and saving GS dumps.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Save States Directory</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<widget class="QLineEdit" name="saveStates"/>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="saveStatesBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="saveStatesOpen">
<property name="text">
<string>Open...</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="saveStatesReset">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Used for storing save states.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>132</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources>
<include location="../resources/resources.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -86,6 +86,12 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
m_ui.setupUi(this);
// start hidden, fixup in updateRendererDependentOptions()
m_ui.hardwareRendererGroup->setVisible(false);
m_ui.verticalLayout->removeWidget(m_ui.hardwareRendererGroup);
m_ui.softwareRendererGroup->setVisible(false);
m_ui.verticalLayout->removeWidget(m_ui.softwareRendererGroup);
//////////////////////////////////////////////////////////////////////////
// Global Settings
//////////////////////////////////////////////////////////////////////////
@ -105,6 +111,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.integerScaling, "EmuCore/GS", "IntegerScaling", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.PCRTCOffsets, "EmuCore/GS", "pcrtc_offsets", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.PCRTCOverscan, "EmuCore/GS", "pcrtc_overscan", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.PCRTCAntiBlur, "EmuCore/GS", "pcrtc_antiblur", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.DisableInterlaceOffset, "EmuCore/GS", "disable_interlace_offset", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.internalResolutionScreenshots, "EmuCore/GS", "InternalResolutionScreenshots", false);
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.zoom, "EmuCore/GS", "Zoom", 100.0f);
@ -208,6 +215,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.loadTextureReplacements, "EmuCore/GS", "LoadTextureReplacements", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.loadTextureReplacementsAsync, "EmuCore/GS", "LoadTextureReplacementsAsync", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.precacheTextureReplacements, "EmuCore/GS", "PrecacheTextureReplacements", false);
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.texturesDirectory, m_ui.texturesBrowse, m_ui.texturesOpen, m_ui.texturesReset,
"Folders", "Textures", "textures");
//////////////////////////////////////////////////////////////////////////
// Advanced Settings
@ -537,7 +546,7 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
const int current_tab = m_hardware_renderer_visible ? m_ui.hardwareRendererGroup->currentIndex() : m_ui.softwareRendererGroup->currentIndex();
// move advanced tab to the correct parent
static constexpr std::array<const char*, 3> move_tab_names = {{"Display", "On-Screen Display", "Advanced"}};
static constexpr std::array<const char*, 3> move_tab_names = {{"Display", "OSD", "Advanced"}};
const std::array<QWidget*, 3> move_tab_pointers = {{m_ui.gameDisplayTab, m_ui.osdTab, m_ui.advancedTab}};
for (size_t i = 0; i < move_tab_pointers.size(); i++)
{
@ -564,7 +573,7 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
{
// map first two tabs over, skip hacks
m_ui.verticalLayout->insertWidget(1, m_ui.hardwareRendererGroup);
m_ui.hardwareRendererGroup->setCurrentIndex((current_tab < 2) ? current_tab : (current_tab + 2));
m_ui.hardwareRendererGroup->setCurrentIndex((current_tab < 2) ? current_tab : (current_tab + 3));
}
m_hardware_renderer_visible = is_hardware;
@ -684,5 +693,6 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
}
m_ui.enableHWFixes->setEnabled(is_hardware);
onEnableHardwareFixesChanged();
if (is_hardware)
onEnableHardwareFixesChanged();
}

View File

@ -31,8 +31,6 @@ public:
GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* parent);
~GraphicsSettingsWidget();
void updateRendererDependentOptions();
Q_SIGNALS:
void fullscreenModesChanged(const QStringList& modes);
@ -48,11 +46,12 @@ private Q_SLOTS:
private:
GSRendererType getEffectiveRenderer() const;
void updateRendererDependentOptions();
SettingsDialog* m_dialog;
Ui::GraphicsSettingsWidget m_ui;
bool m_hardware_renderer_visible = true;
bool m_software_renderer_visible = true;
bool m_hardware_renderer_visible = false;
bool m_software_renderer_visible = false;
};

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>566</width>
<width>657</width>
<height>890</height>
</rect>
</property>
@ -323,6 +323,19 @@
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="PCRTCAntiBlur">
<property name="toolTip">
<string>Enables internal Anti-Blur hacks. LEss accurate to PS2 rendering but will make a lot of games look less blurry.</string>
</property>
<property name="text">
<string>Anti-Blur</string>
</property>
<property name="shortcut">
<string>Ctrl+S</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
@ -900,9 +913,120 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Texture Replacement</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>Search Directory</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="1" column="0">
<widget class="QLineEdit" name="texturesDirectory"/>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="texturesBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="texturesOpen">
<property name="text">
<string>Open...</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="texturesReset">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="label_38">
<property name="text">
<string>PCSX2 will dump and load texture replacements from this directory.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Options</string>
</property>
<layout class="QGridLayout" name="gridLayout_8">
<item row="0" column="0">
<widget class="QCheckBox" name="dumpReplaceableTextures">
<property name="text">
<string>Dump Textures</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="dumpReplaceableMipmaps">
<property name="text">
<string>Dump Mipmaps</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="dumpTexturesWithFMVActive">
<property name="text">
<string>Dump FMV Textures</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="loadTextureReplacementsAsync">
<property name="text">
<string>Async Texture Loading</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="loadTextureReplacements">
<property name="text">
<string>Load Textures</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="precacheTextureReplacements">
<property name="text">
<string>Precache Textures</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QGroupBox" name="osdTab">
<attribute name="title">
<string>On-Screen Display</string>
<string>OSD</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
@ -1274,57 +1398,6 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Texture Replacement</string>
</property>
<layout class="QGridLayout" name="gridLayout_8">
<item row="0" column="0">
<widget class="QCheckBox" name="dumpReplaceableTextures">
<property name="text">
<string>Dump Textures</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="dumpReplaceableMipmaps">
<property name="text">
<string>Dump Mipmaps</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="loadTextureReplacementsAsync">
<property name="text">
<string>Async Texture Loading</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="precacheTextureReplacements">
<property name="text">
<string>Precache Textures</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="loadTextureReplacements">
<property name="text">
<string>Load Textures</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="dumpTexturesWithFMVActive">
<property name="text">
<string>Dump FMV Textures</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">

View File

@ -55,7 +55,7 @@ bool InputBindingDialog::eventFilter(QObject* watched, QEvent* event)
const QEvent::Type event_type = event->type();
// if the key is being released, set the input
if (event_type == QEvent::KeyRelease || event_type == QEvent::MouseButtonPress)
if (event_type == QEvent::KeyRelease || event_type == QEvent::MouseButtonRelease)
{
addNewBinding();
stopListeningForInput();
@ -67,17 +67,43 @@ bool InputBindingDialog::eventFilter(QObject* watched, QEvent* event)
m_new_bindings.push_back(InputManager::MakeHostKeyboardKey(key_event->key()));
return true;
}
else if (event_type == QEvent::MouseButtonPress)
else if (event_type == QEvent::MouseButtonPress || event_type == QEvent::MouseButtonDblClick)
{
// double clicks get triggered if we click bind, then click again quickly.
unsigned long button_index;
if (_BitScanForward(&button_index, static_cast<u32>(static_cast<const QMouseEvent*>(event)->button())))
m_new_bindings.push_back(InputManager::MakeHostMouseButtonKey(button_index));
m_new_bindings.push_back(InputManager::MakePointerButtonKey(0, button_index));
return true;
}
else if (event_type == QEvent::MouseButtonDblClick)
else if (event_type == QEvent::MouseMove)
{
// just eat double clicks
return true;
// if we've moved more than a decent distance from the center of the widget, bind it.
// this is so we don't accidentally bind to the mouse if you bump it while reaching for your pad.
static constexpr const s32 THRESHOLD = 50;
const QPoint diff(static_cast<QMouseEvent*>(event)->globalPos() - m_input_listen_start_position);
bool has_one = false;
if (std::abs(diff.x()) >= THRESHOLD)
{
InputBindingKey key(InputManager::MakePointerAxisKey(0, InputPointerAxis::X));
key.negative = (diff.x() < 0);
m_new_bindings.push_back(key);
has_one = true;
}
if (std::abs(diff.y()) >= THRESHOLD)
{
InputBindingKey key(InputManager::MakePointerAxisKey(0, InputPointerAxis::Y));
key.negative = (diff.y() < 0);
m_new_bindings.push_back(key);
has_one = true;
}
if (has_one)
{
addNewBinding();
stopListeningForInput();
return true;
}
}
return false;
@ -98,6 +124,7 @@ void InputBindingDialog::onInputListenTimerTimeout()
void InputBindingDialog::startListeningForInput(u32 timeout_in_seconds)
{
m_new_bindings.clear();
m_input_listen_start_position = QCursor::pos();
m_input_listen_timer = new QTimer(this);
m_input_listen_timer->setSingleShot(false);
m_input_listen_timer->start(1000);
@ -114,6 +141,7 @@ void InputBindingDialog::startListeningForInput(u32 timeout_in_seconds)
installEventFilter(this);
grabKeyboard();
grabMouse();
setMouseTracking(true);
hookInputManager();
}
@ -131,6 +159,7 @@ void InputBindingDialog::stopListeningForInput()
unhookInputManager();
releaseMouse();
releaseKeyboard();
setMouseTracking(false);
removeEventFilter(this);
}

View File

@ -68,4 +68,5 @@ protected:
QTimer* m_input_listen_timer = nullptr;
u32 m_input_listen_remaining_seconds = 0;
QPoint m_input_listen_start_position{};
};

View File

@ -19,6 +19,7 @@
#include <QtGui/QKeyEvent>
#include <QtGui/QMouseEvent>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QMessageBox>
#include <cmath>
#include <sstream>
@ -105,7 +106,7 @@ bool InputBindingWidget::eventFilter(QObject* watched, QEvent* event)
const QEvent::Type event_type = event->type();
// if the key is being released, set the input
if (event_type == QEvent::KeyRelease || event_type == QEvent::MouseButtonPress)
if (event_type == QEvent::KeyRelease || event_type == QEvent::MouseButtonRelease)
{
setNewBinding();
stopListeningForInput();
@ -117,17 +118,43 @@ bool InputBindingWidget::eventFilter(QObject* watched, QEvent* event)
m_new_bindings.push_back(InputManager::MakeHostKeyboardKey(key_event->key()));
return true;
}
else if (event_type == QEvent::MouseButtonPress)
else if (event_type == QEvent::MouseButtonPress || event_type == QEvent::MouseButtonDblClick)
{
// double clicks get triggered if we click bind, then click again quickly.
unsigned long button_index;
if (_BitScanForward(&button_index, static_cast<u32>(static_cast<const QMouseEvent*>(event)->button())))
m_new_bindings.push_back(InputManager::MakeHostMouseButtonKey(button_index));
m_new_bindings.push_back(InputManager::MakePointerButtonKey(0, button_index));
return true;
}
else if (event_type == QEvent::MouseButtonDblClick)
else if (event_type == QEvent::MouseMove)
{
// just eat double clicks
return true;
// if we've moved more than a decent distance from the center of the widget, bind it.
// this is so we don't accidentally bind to the mouse if you bump it while reaching for your pad.
static constexpr const s32 THRESHOLD = 50;
const QPoint diff(static_cast<QMouseEvent*>(event)->globalPos() - m_input_listen_start_position);
bool has_one = false;
if (std::abs(diff.x()) >= THRESHOLD)
{
InputBindingKey key(InputManager::MakePointerAxisKey(0, InputPointerAxis::X));
key.negative = (diff.x() < 0);
m_new_bindings.push_back(key);
has_one = true;
}
if (std::abs(diff.y()) >= THRESHOLD)
{
InputBindingKey key(InputManager::MakePointerAxisKey(0, InputPointerAxis::Y));
key.negative = (diff.y() < 0);
m_new_bindings.push_back(key);
has_one = true;
}
if (has_one)
{
setNewBinding();
stopListeningForInput();
return true;
}
}
return false;
@ -239,6 +266,7 @@ void InputBindingWidget::onInputListenTimerTimeout()
void InputBindingWidget::startListeningForInput(u32 timeout_in_seconds)
{
m_new_bindings.clear();
m_input_listen_start_position = QCursor::pos();
m_input_listen_timer = new QTimer(this);
m_input_listen_timer->setSingleShot(false);
m_input_listen_timer->start(1000);
@ -251,6 +279,7 @@ void InputBindingWidget::startListeningForInput(u32 timeout_in_seconds)
installEventFilter(this);
grabKeyboard();
grabMouse();
setMouseTracking(true);
hookInputManager();
}
@ -262,6 +291,7 @@ void InputBindingWidget::stopListeningForInput()
std::vector<InputBindingKey>().swap(m_new_bindings);
unhookInputManager();
setMouseTracking(false);
releaseMouse();
releaseKeyboard();
removeEventFilter(this);
@ -362,7 +392,14 @@ void InputVibrationBindingWidget::onClicked()
const QString current(QString::fromStdString(m_binding));
QStringList input_options(m_dialog->getVibrationMotors());
if (!current.isEmpty() && input_options.indexOf(current) < 0)
{
input_options.append(current);
}
else if (input_options.isEmpty())
{
QMessageBox::critical(QtUtils::GetRootWidget(this), tr("Error"), tr("No devices with vibration motors were detected."));
return;
}
QInputDialog input_dialog(this);
input_dialog.setWindowTitle(full_key);

View File

@ -73,6 +73,7 @@ protected:
std::vector<InputBindingKey> m_new_bindings;
QTimer* m_input_listen_timer = nullptr;
u32 m_input_listen_remaining_seconds = 0;
QPoint m_input_listen_start_position{};
};
class InputVibrationBindingWidget : public QPushButton

View File

@ -117,7 +117,6 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsDialog* dialog, QWidget
m_ui.pauseOnStart->setDisabled(true);
m_ui.pauseOnFocusLoss->setDisabled(true);
m_ui.disableWindowResizing->setDisabled(true);
m_ui.hideMouseCursor->setDisabled(true);
m_ui.language->setDisabled(true);
}

View File

@ -88,7 +88,7 @@
<item row="1" column="1">
<widget class="QCheckBox" name="hideMouseCursor">
<property name="text">
<string>Hide Mouse Cursor</string>
<string>Hide Cursor In Fullscreen</string>
</property>
</widget>
</item>

View File

@ -16,6 +16,7 @@
#include "PrecompiledHeader.h"
#include <QtGui/QDrag>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QMenu>
#include <QtWidgets/QMessageBox>
@ -48,12 +49,14 @@ MemoryCardSettingsWidget::MemoryCardSettingsWidget(SettingsDialog* dialog, QWidg
// this is a bit lame, but resizeEvent() isn't good enough to autosize our columns,
// since the group box hasn't been resized at that point.
m_ui.cardGroupBox->installEventFilter(this);
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.directory, m_ui.browse, m_ui.open, m_ui.reset, "Folders", "MemoryCards", "memcards");
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.autoEject, "EmuCore", "McdEnableEjection", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.automaticManagement, "EmuCore", "McdFolderAutoManage", true);
setupAdditionalUi();
connect(m_ui.directory, &QLineEdit::textChanged, this, &MemoryCardSettingsWidget::refresh);
m_ui.cardList->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_ui.cardList, &MemoryCardListWidget::itemSelectionChanged, this, &MemoryCardSettingsWidget::updateCardActions);
connect(m_ui.cardList, &MemoryCardListWidget::customContextMenuRequested, this, &MemoryCardSettingsWidget::listContextMenuRequested);

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>796</width>
<width>639</width>
<height>443</height>
</rect>
</property>
@ -58,6 +58,41 @@
<string>Memory Cards</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Folder:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="directory"/>
</item>
<item>
<widget class="QPushButton" name="browse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="open">
<property name="text">
<string>Open...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="reset">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="MemoryCardListWidget" name="cardList">
<property name="rootIsDecorated">

View File

@ -37,6 +37,7 @@
#include "GameListSettingsWidget.h"
#include "GraphicsSettingsWidget.h"
#include "DEV9SettingsWidget.h"
#include "FolderSettingsWidget.h"
#include "HotkeySettingsWidget.h"
#include "InterfaceSettingsWidget.h"
#include "MemoryCardSettingsWidget.h"
@ -133,6 +134,12 @@ void SettingsDialog::setupUi(const GameList::Entry* game)
tr("<strong>Network & HDD Settings</strong><hr>These options control the network connectivity and internal HDD storage of the console.<br><br>"
"Mouse over an option for additional information."));
if (!isPerGameSettings())
{
addWidget(m_folder_settings = new FolderSettingsWidget(this, m_ui.settingsContainer), tr("Folders"), QStringLiteral("folder-open-line"),
tr("<strong>Folder Settings</strong><hr>These options control where PCSX2 will save runtime data files."));
}
m_ui.settingsCategory->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
m_ui.settingsCategory->setCurrentRow(0);
m_ui.settingsContainer->setCurrentIndex(0);

View File

@ -39,6 +39,7 @@ class GameFixSettingsWidget;
class GraphicsSettingsWidget;
class AudioSettingsWidget;
class MemoryCardSettingsWidget;
class FolderSettingsWidget;
class DEV9SettingsWidget;
class SettingsDialog final : public QDialog
@ -65,6 +66,7 @@ public:
__fi GraphicsSettingsWidget* getGraphicsSettingsWidget() const { return m_graphics_settings; }
__fi AudioSettingsWidget* getAudioSettingsWidget() const { return m_audio_settings; }
__fi MemoryCardSettingsWidget* getMemoryCardSettingsWidget() const { return m_memory_card_settings; }
__fi FolderSettingsWidget* getFolderSettingsWidget() const { return m_folder_settings; }
__fi DEV9SettingsWidget* getDEV9SettingsWidget() const { return m_dev9_settings; }
void registerWidgetHelp(QObject* object, QString title, QString recommended_value, QString text);
@ -122,6 +124,7 @@ private:
GraphicsSettingsWidget* m_graphics_settings = nullptr;
AudioSettingsWidget* m_audio_settings = nullptr;
MemoryCardSettingsWidget* m_memory_card_settings = nullptr;
FolderSettingsWidget* m_folder_settings = nullptr;
DEV9SettingsWidget* m_dev9_settings = nullptr;
std::array<QString, MAX_SETTINGS_WIDGETS> m_category_help_text;

View File

@ -135,6 +135,7 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="EarlyHardwareCheck.cpp" />
<ClCompile Include="Settings\FolderSettingsWidget.cpp" />
<ClCompile Include="Tools\InputRecording\NewInputRecordingDlg.cpp" />
<ClCompile Include="Settings\BIOSSettingsWidget.cpp" />
<ClCompile Include="Settings\ControllerBindingWidgets.cpp" />
@ -195,6 +196,7 @@
<QtMoc Include="Settings\DEV9SettingsWidget.h" />
<QtMoc Include="Settings\DEV9UiCommon.h" />
<ClInclude Include="Settings\ControllerSettingWidgetBinder.h" />
<QtMoc Include="Settings\FolderSettingsWidget.h" />
<ClInclude Include="Settings\HddCreateQt.h" />
<QtMoc Include="Settings\GameSummaryWidget.h" />
<QtMoc Include="Settings\CreateMemoryCardDialog.h" />
@ -238,6 +240,7 @@
<ClCompile Include="$(IntDir)Settings\moc_InputBindingWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_AudioSettingsWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_MemoryCardSettingsWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_FolderSettingsWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_DEV9DnsHostDialog.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_DEV9SettingsWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_DEV9UiCommon.cpp" />
@ -329,6 +332,9 @@
<FileType>Document</FileType>
</QtUi>
</ItemGroup>
<ItemGroup>
<None Include="Settings\FolderSettingsWidget.ui" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="$(SolutionDir)common\vsprops\QtCompile.targets" />
<ImportGroup Label="ExtensionTargets" />

View File

@ -217,6 +217,12 @@
<Filter>Tools\Input Recording</Filter>
</ClCompile>
<ClCompile Include="EarlyHardwareCheck.cpp" />
<ClCompile Include="Settings\FolderSettingsWidget.cpp">
<Filter>Settings</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)Settings\moc_FolderSettingsWidget.cpp">
<Filter>moc</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Manifest Include="..\pcsx2\windows\PCSX2.manifest">
@ -319,6 +325,7 @@
<QtMoc Include="Tools\InputRecording\NewInputRecordingDlg.h">
<Filter>Tools\Input Recording</Filter>
</QtMoc>
<QtMoc Include="Settings\FolderSettingsWidget.h" />
</ItemGroup>
<ItemGroup>
<QtResource Include="resources\resources.qrc">
@ -395,4 +402,9 @@
<Filter>Tools\Input Recording</Filter>
</QtUi>
</ItemGroup>
<ItemGroup>
<None Include="Settings\FolderSettingsWidget.ui">
<Filter>Settings</Filter>
</None>
</ItemGroup>
</Project>

View File

@ -1094,6 +1094,7 @@ if(PCSX2_CORE)
Frontend/InputManager.cpp
Frontend/InputSource.cpp
Frontend/LayeredSettingsInterface.cpp
Frontend/LogSink.cpp
GSDumpReplayer.cpp
HostSettings.cpp
VMManager.cpp
@ -1104,6 +1105,7 @@ if(PCSX2_CORE)
Frontend/InputManager.h
Frontend/InputSource.h
Frontend/LayeredSettingsInterface.h
Frontend/LogSink.h
GSDumpReplayer.h
HostSettings.h
VMManager.h)

View File

@ -434,6 +434,7 @@ struct Pcsx2Config
struct
{
bool
PCRTCAntiBlur : 1,
DisableInterlaceOffset : 1,
PCRTCOffsets : 1,
PCRTCOverscan : 1,

View File

@ -0,0 +1,49 @@
# Documentation Artifacts
- Source Directory: `/pcsx2/pcsx2/Docs`
- Release Directory: `/pcsx2/bin/Docs`
Docs should be written in the source directory first in an easily editable format. Currently, Markdown is the preferred format due to its simple markup and easy portability. GitHub's built-in preview functions are a huge benefit as well.
Visual Studio Code is a cross platform text editor/development platform that can handle Markdown syntax, plus extensions are available to enable in-editor previewing and PDF generation. However, this is not a requirement since other editors will support Markdown, and worst case GitHub supports editing Markdown files in-browser.
## How to Generate
### Setup
To generate the documentation artifacts into the release directory, you will require the following:
- `pandoc`
- Converts from Markdown to PDF
- `miktex` or something similar that provides `pdflatex`
- this is what generates the PDF file
#### Linux / MacOS
Consult pandoc's official documentation - https://pandoc.org/installing.html
#### Windows
In a PowerShell shell, run the following:
```ps1
> iwr -useb get.scoop.sh | iex
> scoop install pandoc latex
```
If you prefer Chocolatey or using the installer, consult pandoc's official documentation - https://pandoc.org/installing.html
### Usage
Run the following, this assumes you have access to bash, either by virtue of running on linux or through something like `git-bash` on Windows.
```bash
> ./gen-docs.sh
```
> You can override the default output directory like so - `OUT_DIR=<PATH> ./gen-docs.sh`
### Customizing Output
For generating the PDF's this popular template is used and has many features that could be useful https://github.com/Wandmalfarbe/pandoc-latex-template#usage reference it's documentation to take advantage of those if desired.

View File

@ -1,49 +1,25 @@
# Documentation Artifacts
# PCSX2
- Source Directory: `/pcsx2/pcsx2/Docs`
- Release Directory: `/pcsx2/bin/Docs`
PCSX2 is a Playstation 2 emulator for Windows, Linux and Mac. The project has been going since 2001 and still going strong, with contributions from many very clever developers over the years! PCSX2 has compatibility upwards of 95% of the Playstation 2 library being playable.
Docs should be written in the source directory first in an easily editable format. Currently, Markdown is the preferred format due to its simple markup and easy portability. GitHub's built-in preview functions are a huge benefit as well.
## Basic usage
Visual Studio Code is a cross platform text editor/development platform that can handle Markdown syntax, plus extensions are available to enable in-editor previewing and PDF generation. However, this is not a requirement since other editors will support Markdown, and worst case GitHub supports editing Markdown files in-browser.
For information on how to set up the emulator, please see our [basic setup](https://pcsx2.net/guides/basic-setup/) guide.
## How to Generate
## Contributing
Pull requests are welcome and can be submitted at our [GitHub page](https://github.com/PCSX2/pcsx2/)
### Setup
Please make sure to follow our coding style guide and fill out the pull request forms with the appropriate information.
To generate the documentation artifacts into the release directory, you will require the following:
## License
[GPL 3.0](https://www.gnu.org/licenses/gpl-3.0.en.html)
- `pandoc`
- Converts from Markdown to PDF
## Donating to the project
If you wish to donate monetarily to the project you can do so via the [GitHub Sponsors](https://github.com/sponsors/PCSX2). All donations are gratefully received!
- `miktex` or something similar that provides `pdflatex`
- this is what generates the PDF file
## Thanks to the following monthly donators
We thank everybody for their contributions to the project, but we would like to give a special shout out to:
#### Linux / MacOS
Consult pandoc's official documentation - https://pandoc.org/installing.html
#### Windows
In a PowerShell shell, run the following:
```ps1
> iwr -useb get.scoop.sh | iex
> scoop install pandoc latex
```
If you prefer Chocolatey or using the installer, consult pandoc's official documentation - https://pandoc.org/installing.html
### Usage
Run the following, this assumes you have access to bash, either by virtue of running on linux or through something like `git-bash` on Windows.
```bash
> ./gen-docs.sh
```
> You can override the default output directory like so - `OUT_DIR=<PATH> ./gen-docs.sh`
### Customizing Output
For generating the PDF's this popular template is used and has many features that could be useful https://github.com/Wandmalfarbe/pandoc-latex-template#usage reference it's documentation to take advantage of those if desired.
- noigeaR
- xTVaser
- And 1 anonymous donator!

View File

@ -95,8 +95,9 @@ D3D11HostDisplay::D3D11HostDisplay() = default;
D3D11HostDisplay::~D3D11HostDisplay()
{
pxAssertMsg(!m_context, "Context should have been destroyed by now");
pxAssertMsg(!m_swap_chain, "Swap chain should have been destroyed by now");
D3D11HostDisplay::DestroyRenderSurface();
m_context.Reset();
m_device.Reset();
}
HostDisplay::RenderAPI D3D11HostDisplay::GetRenderAPI() const
@ -332,13 +333,6 @@ bool D3D11HostDisplay::InitializeRenderDevice(std::string_view shader_cache_dire
return true;
}
void D3D11HostDisplay::DestroyRenderDevice()
{
DestroyRenderSurface();
m_context.Reset();
m_device.Reset();
}
bool D3D11HostDisplay::MakeRenderContextCurrent()
{
return true;
@ -757,7 +751,7 @@ void D3D11HostDisplay::EndPresent()
KickTimestampQuery();
}
void D3D11HostDisplay::CreateTimestampQueries()
bool D3D11HostDisplay::CreateTimestampQueries()
{
for (u32 i = 0; i < NUM_TIMESTAMP_QUERIES; i++)
{
@ -768,12 +762,13 @@ void D3D11HostDisplay::CreateTimestampQueries()
if (FAILED(hr))
{
m_timestamp_queries = {};
return;
return false;
}
}
}
KickTimestampQuery();
return true;
}
void D3D11HostDisplay::DestroyTimestampQueries()
@ -835,7 +830,7 @@ void D3D11HostDisplay::PopTimestampQuery()
void D3D11HostDisplay::KickTimestampQuery()
{
if (m_timestamp_query_started)
if (m_timestamp_query_started || !m_timestamp_queries[0][0])
return;
m_context->Begin(m_timestamp_queries[m_write_timestamp_query][0].Get());
@ -843,16 +838,21 @@ void D3D11HostDisplay::KickTimestampQuery()
m_timestamp_query_started = true;
}
void D3D11HostDisplay::SetGPUTimingEnabled(bool enabled)
bool D3D11HostDisplay::SetGPUTimingEnabled(bool enabled)
{
if (m_gpu_timing_enabled == enabled)
return;
return true;
m_gpu_timing_enabled = enabled;
if (m_gpu_timing_enabled)
CreateTimestampQueries();
{
return CreateTimestampQueries();
}
else
{
DestroyTimestampQueries();
return true;
}
}
float D3D11HostDisplay::GetAndResetAccumulatedGPUTime()

View File

@ -45,7 +45,6 @@ public:
bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, VsyncMode vsync, bool threaded_presentation, bool debug_device) override;
bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) override;
void DestroyRenderDevice() override;
bool MakeRenderContextCurrent() override;
bool DoneRenderContextCurrent() override;
@ -69,7 +68,7 @@ public:
bool BeginPresent(bool frame_skip) override;
void EndPresent() override;
void SetGPUTimingEnabled(bool enabled) override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
static AdapterAndModeList StaticGetAdapterAndModeList();
@ -87,7 +86,7 @@ protected:
bool CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode);
bool CreateSwapChainRTV();
void CreateTimestampQueries();
bool CreateTimestampQueries();
void DestroyTimestampQueries();
void PopTimestampQuery();
void KickTimestampQuery();

View File

@ -51,8 +51,12 @@ D3D12HostDisplay::D3D12HostDisplay() = default;
D3D12HostDisplay::~D3D12HostDisplay()
{
pxAssertMsg(!g_d3d12_context, "Context should have been destroyed by now");
pxAssertMsg(!m_swap_chain, "Swap chain should have been destroyed by now");
if (g_d3d12_context)
{
g_d3d12_context->WaitForGPUIdle();
D3D12HostDisplay::DestroyRenderSurface();
g_d3d12_context->Destroy();
}
}
HostDisplay::RenderAPI D3D12HostDisplay::GetRenderAPI() const
@ -85,7 +89,8 @@ bool D3D12HostDisplay::HasRenderSurface() const
return static_cast<bool>(m_swap_chain);
}
std::unique_ptr<HostDisplayTexture> D3D12HostDisplay::CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic /* = false */)
std::unique_ptr<HostDisplayTexture> D3D12HostDisplay::CreateTexture(
u32 width, u32 height, const void* data, u32 data_stride, bool dynamic /* = false */)
{
D3D12::Texture tex;
if (!tex.Create(width, height, 1, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN,
@ -94,17 +99,17 @@ std::unique_ptr<HostDisplayTexture> D3D12HostDisplay::CreateTexture(u32 width, u
return {};
}
if (data && !tex.LoadData(0, 0, 0, width, height, data, data_stride))
if (data && !tex.LoadData(g_d3d12_context->GetInitCommandList(), 0, 0, 0, width, height, data, data_stride))
return {};
return std::make_unique<D3D12HostDisplayTexture>(std::move(tex));
}
void D3D12HostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height,
const void* texture_data, u32 texture_data_stride)
void D3D12HostDisplay::UpdateTexture(
HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data, u32 texture_data_stride)
{
static_cast<D3D12HostDisplayTexture*>(texture)->GetTexture().LoadData(0, x, y, width, height, texture_data,
texture_data_stride);
static_cast<D3D12HostDisplayTexture*>(texture)->GetTexture().LoadData(
g_d3d12_context->GetCommandList(), 0, x, y, width, height, texture_data, texture_data_stride);
}
bool D3D12HostDisplay::GetHostRefreshRate(float* refresh_rate)
@ -203,15 +208,6 @@ bool D3D12HostDisplay::InitializeRenderDevice(std::string_view shader_cache_dire
return true;
}
void D3D12HostDisplay::DestroyRenderDevice()
{
g_d3d12_context->ExecuteCommandList(true);
DestroyRenderSurface();
if (g_d3d12_context)
g_d3d12_context->Destroy();
}
bool D3D12HostDisplay::MakeRenderContextCurrent()
{
return true;
@ -644,9 +640,10 @@ void D3D12HostDisplay::EndPresent()
m_swap_chain->Present(static_cast<UINT>(vsync), 0);
}
void D3D12HostDisplay::SetGPUTimingEnabled(bool enabled)
bool D3D12HostDisplay::SetGPUTimingEnabled(bool enabled)
{
g_d3d12_context->SetEnableGPUTiming(enabled);
return true;
}
float D3D12HostDisplay::GetAndResetAccumulatedGPUTime()

View File

@ -51,7 +51,6 @@ public:
bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, VsyncMode vsync, bool threaded_presentation, bool debug_device) override;
bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) override;
void DestroyRenderDevice() override;
bool MakeRenderContextCurrent() override;
bool DoneRenderContextCurrent() override;
@ -75,7 +74,7 @@ public:
bool BeginPresent(bool frame_skip) override;
void EndPresent() override;
void SetGPUTimingEnabled(bool enabled) override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
static AdapterAndModeList StaticGetAdapterAndModeList();

View File

@ -19,15 +19,18 @@
#include <cmath>
#include <deque>
#include <mutex>
#include <unordered_map>
#include "fmt/core.h"
#include "common/StringUtil.h"
#include "common/Timer.h"
#include "imgui.h"
#include "Config.h"
#include "Counters.h"
#include "Frontend/ImGuiManager.h"
#include "Frontend/InputManager.h"
#include "GS.h"
#include "GS/GS.h"
#include "Host.h"
@ -39,10 +42,21 @@
#include "VMManager.h"
#endif
static void SetImGuiStyle();
static bool LoadFontData();
static void UnloadFontData();
static bool AddImGuiFonts();
namespace ImGuiManager
{
static void SetStyle();
static void SetKeyMap();
static bool LoadFontData();
static void UnloadFontData();
static bool AddImGuiFonts();
static ImFont* AddTextFont(float size);
static ImFont* AddFixedFont(float size);
static bool AddIconFonts(float size);
static void AcquirePendingOSDMessages();
static void DrawOSDMessages();
static void FormatProcessorStat(std::string& text, double usage, double time);
static void DrawPerformanceOverlay();
} // namespace ImGuiManager
static float s_global_scale = 1.0f;
@ -53,6 +67,17 @@ static std::vector<u8> s_standard_font_data;
static std::vector<u8> s_fixed_font_data;
static std::vector<u8> s_icon_font_data;
static Common::Timer s_last_render_time;
#ifdef PCSX2_CORE
// cached copies of WantCaptureKeyboard/Mouse, used to know when to dispatch events
static std::atomic_bool s_imgui_wants_keyboard{false};
static std::atomic_bool s_imgui_wants_mouse{false};
// mapping of host key -> imgui key
static std::unordered_map<u32, ImGuiKey> s_imgui_key_map;
#endif
bool ImGuiManager::Initialize()
{
if (!LoadFontData())
@ -62,23 +87,30 @@ bool ImGuiManager::Initialize()
}
HostDisplay* display = Host::GetHostDisplay();
ImGui::CreateContext();
ImGui::GetIO().IniFilename = nullptr;
s_global_scale = std::max(1.0f, display->GetWindowScale() * static_cast<float>(EmuConfig.GS.OsdScale / 100.0));
ImGui::GetIO().DisplayFramebufferScale = ImVec2(1, 1); // We already scale things ourselves, this would double-apply scaling
ImGui::GetIO().DisplaySize.x = static_cast<float>(display->GetWindowWidth());
ImGui::GetIO().DisplaySize.y = static_cast<float>(display->GetWindowHeight());
ImGui::GetStyle() = ImGuiStyle();
ImGui::GetStyle().WindowMinSize = ImVec2(1.0f, 1.0f);
SetImGuiStyle();
ImGui::GetStyle().ScaleAllSizes(s_global_scale);
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.IniFilename = nullptr;
#ifdef PCSX2_CORE
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
io.BackendUsingLegacyKeyArrays = 0;
io.BackendUsingLegacyNavInputArray = 0;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad;
#endif
io.DisplayFramebufferScale = ImVec2(1, 1); // We already scale things ourselves, this would double-apply scaling
io.DisplaySize.x = static_cast<float>(display->GetWindowWidth());
io.DisplaySize.y = static_cast<float>(display->GetWindowHeight());
SetKeyMap();
SetStyle();
if (!display->CreateImGuiContext())
{
pxFailRel("Failed to create ImGui device context");
display->DestroyImGuiContext();
ImGui::DestroyContext();
UnloadFontData();
return false;
@ -93,6 +125,9 @@ bool ImGuiManager::Initialize()
return false;
}
// don't need the font data anymore, save some memory
ImGui::GetIO().Fonts->ClearTexData();
NewFrame();
return true;
}
@ -104,6 +139,10 @@ void ImGuiManager::Shutdown()
display->DestroyImGuiContext();
if (ImGui::GetCurrentContext())
ImGui::DestroyContext();
s_standard_font = nullptr;
s_fixed_font = nullptr;
UnloadFontData();
}
@ -139,7 +178,7 @@ void ImGuiManager::UpdateScale()
ImGui::GetStyle() = ImGuiStyle();
ImGui::GetStyle().WindowMinSize = ImVec2(1.0f, 1.0f);
SetImGuiStyle();
SetStyle();
ImGui::GetStyle().ScaleAllSizes(scale);
if (!AddImGuiFonts())
@ -153,14 +192,24 @@ void ImGuiManager::UpdateScale()
void ImGuiManager::NewFrame()
{
ImGuiIO& io = ImGui::GetIO();
io.DeltaTime = s_last_render_time.GetTimeSecondsAndReset();
ImGui::NewFrame();
#ifdef PCSX2_CORE
s_imgui_wants_keyboard.store(io.WantCaptureKeyboard, std::memory_order_relaxed);
s_imgui_wants_mouse.store(io.WantCaptureMouse, std::memory_order_release);
#endif
}
void SetImGuiStyle()
void ImGuiManager::SetStyle()
{
ImGuiStyle* style = &ImGui::GetStyle();
ImVec4* colors = style->Colors;
ImGuiStyle& style = ImGui::GetStyle();
style = ImGuiStyle();
style.WindowMinSize = ImVec2(1.0f, 1.0f);
ImVec4* colors = style.Colors;
colors[ImGuiCol_Text] = ImVec4(0.95f, 0.96f, 0.98f, 1.00f);
colors[ImGuiCol_TextDisabled] = ImVec4(0.36f, 0.42f, 0.47f, 1.00f);
colors[ImGuiCol_WindowBg] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
@ -209,9 +258,58 @@ void SetImGuiStyle()
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
style.ScaleAllSizes(s_global_scale);
}
bool LoadFontData()
void ImGuiManager::SetKeyMap()
{
#ifdef PCSX2_CORE
struct KeyMapping
{
int index;
const char* name;
const char* alt_name;
};
static constexpr KeyMapping mapping[] = {{ImGuiKey_LeftArrow, "Left"}, {ImGuiKey_RightArrow, "Right"}, {ImGuiKey_UpArrow, "Up"},
{ImGuiKey_DownArrow, "Down"}, {ImGuiKey_PageUp, "PageUp"}, {ImGuiKey_PageDown, "PageDown"}, {ImGuiKey_Home, "Home"},
{ImGuiKey_End, "End"}, {ImGuiKey_Insert, "Insert"}, {ImGuiKey_Delete, "Delete"}, {ImGuiKey_Backspace, "Backspace"},
{ImGuiKey_Space, "Space"}, {ImGuiKey_Enter, "Return"}, {ImGuiKey_Escape, "Escape"}, {ImGuiKey_LeftCtrl, "LeftCtrl", "Ctrl"},
{ImGuiKey_LeftShift, "LeftShift", "Shift"}, {ImGuiKey_LeftAlt, "LeftAlt", "Alt"}, {ImGuiKey_LeftSuper, "LeftSuper", "Super"},
{ImGuiKey_RightCtrl, "RightCtrl"}, {ImGuiKey_RightShift, "RightShift"}, {ImGuiKey_RightAlt, "RightAlt"},
{ImGuiKey_RightSuper, "RightSuper"}, {ImGuiKey_Menu, "Menu"}, {ImGuiKey_0, "0"}, {ImGuiKey_1, "1"}, {ImGuiKey_2, "2"},
{ImGuiKey_3, "3"}, {ImGuiKey_4, "4"}, {ImGuiKey_5, "5"}, {ImGuiKey_6, "6"}, {ImGuiKey_7, "7"}, {ImGuiKey_8, "8"}, {ImGuiKey_9, "9"},
{ImGuiKey_A, "A"}, {ImGuiKey_B, "B"}, {ImGuiKey_C, "C"}, {ImGuiKey_D, "D"}, {ImGuiKey_E, "E"}, {ImGuiKey_F, "F"}, {ImGuiKey_G, "G"},
{ImGuiKey_H, "H"}, {ImGuiKey_I, "I"}, {ImGuiKey_J, "J"}, {ImGuiKey_K, "K"}, {ImGuiKey_L, "L"}, {ImGuiKey_M, "M"}, {ImGuiKey_N, "N"},
{ImGuiKey_O, "O"}, {ImGuiKey_P, "P"}, {ImGuiKey_Q, "Q"}, {ImGuiKey_R, "R"}, {ImGuiKey_S, "S"}, {ImGuiKey_T, "T"}, {ImGuiKey_U, "U"},
{ImGuiKey_V, "V"}, {ImGuiKey_W, "W"}, {ImGuiKey_X, "X"}, {ImGuiKey_Y, "Y"}, {ImGuiKey_Z, "Z"}, {ImGuiKey_F1, "F1"},
{ImGuiKey_F2, "F2"}, {ImGuiKey_F3, "F3"}, {ImGuiKey_F4, "F4"}, {ImGuiKey_F5, "F5"}, {ImGuiKey_F6, "F6"}, {ImGuiKey_F7, "F7"},
{ImGuiKey_F8, "F8"}, {ImGuiKey_F9, "F9"}, {ImGuiKey_F10, "F10"}, {ImGuiKey_F11, "F11"}, {ImGuiKey_F12, "F12"},
{ImGuiKey_Apostrophe, "Apostrophe"}, {ImGuiKey_Comma, "Comma"}, {ImGuiKey_Minus, "Minus"}, {ImGuiKey_Period, "Period"},
{ImGuiKey_Slash, "Slash"}, {ImGuiKey_Semicolon, "Semicolon"}, {ImGuiKey_Equal, "Equal"}, {ImGuiKey_LeftBracket, "BracketLeft"},
{ImGuiKey_Backslash, "Backslash"}, {ImGuiKey_RightBracket, "BracketRight"}, {ImGuiKey_GraveAccent, "QuoteLeft"},
{ImGuiKey_CapsLock, "CapsLock"}, {ImGuiKey_ScrollLock, "ScrollLock"}, {ImGuiKey_NumLock, "NumLock"},
{ImGuiKey_PrintScreen, "PrintScreen"}, {ImGuiKey_Pause, "Pause"}, {ImGuiKey_Keypad0, "Keypad0"}, {ImGuiKey_Keypad1, "Keypad1"},
{ImGuiKey_Keypad2, "Keypad2"}, {ImGuiKey_Keypad3, "Keypad3"}, {ImGuiKey_Keypad4, "Keypad4"}, {ImGuiKey_Keypad5, "Keypad5"},
{ImGuiKey_Keypad6, "Keypad6"}, {ImGuiKey_Keypad7, "Keypad7"}, {ImGuiKey_Keypad8, "Keypad8"}, {ImGuiKey_Keypad9, "Keypad9"},
{ImGuiKey_KeypadDecimal, "KeypadPeriod"}, {ImGuiKey_KeypadDivide, "KeypadDivide"}, {ImGuiKey_KeypadMultiply, "KeypadMultiply"},
{ImGuiKey_KeypadSubtract, "KeypadMinus"}, {ImGuiKey_KeypadAdd, "KeypadPlus"}, {ImGuiKey_KeypadEnter, "KeypadReturn"},
{ImGuiKey_KeypadEqual, "KeypadEqual"}};
s_imgui_key_map.clear();
for (const KeyMapping& km : mapping)
{
std::optional<u32> map(InputManager::ConvertHostKeyboardStringToCode(km.name));
if (!map.has_value() && km.alt_name)
map = InputManager::ConvertHostKeyboardStringToCode(km.alt_name);
if (map.has_value())
s_imgui_key_map[map.value()] = km.index;
}
#endif
}
bool ImGuiManager::LoadFontData()
{
if (s_standard_font_data.empty())
{
@ -243,14 +341,14 @@ bool LoadFontData()
return true;
}
void UnloadFontData()
void ImGuiManager::UnloadFontData()
{
std::vector<u8>().swap(s_standard_font_data);
std::vector<u8>().swap(s_fixed_font_data);
std::vector<u8>().swap(s_icon_font_data);
}
static ImFont* AddTextFont(float size /*= 15.0f*/)
ImFont* ImGuiManager::AddTextFont(float size)
{
static const ImWchar default_ranges[] = {
// Basic Latin + Latin Supplement + Central European diacritics
@ -278,15 +376,15 @@ static ImFont* AddTextFont(float size /*= 15.0f*/)
s_standard_font_data.data(), static_cast<int>(s_standard_font_data.size()), size, &cfg, default_ranges);
}
static ImFont* AddFixedFont(float size)
ImFont* ImGuiManager::AddFixedFont(float size)
{
ImFontConfig cfg;
cfg.FontDataOwnedByAtlas = false;
return ImGui::GetIO().Fonts->AddFontFromMemoryTTF(s_fixed_font_data.data(),
static_cast<int>(s_fixed_font_data.size()), size, &cfg, nullptr);
return ImGui::GetIO().Fonts->AddFontFromMemoryTTF(
s_fixed_font_data.data(), static_cast<int>(s_fixed_font_data.size()), size, &cfg, nullptr);
}
static bool AddIconFonts(float size)
bool ImGuiManager::AddIconFonts(float size)
{
static const ImWchar range_fa[] = {ICON_MIN_FA, ICON_MAX_FA, 0};
@ -297,11 +395,11 @@ static bool AddIconFonts(float size)
cfg.GlyphMaxAdvanceX = size * 0.75f;
cfg.FontDataOwnedByAtlas = false;
return (ImGui::GetIO().Fonts->AddFontFromMemoryTTF(s_icon_font_data.data(), static_cast<int>(s_icon_font_data.size()),
size * 0.75f, &cfg, range_fa) != nullptr);
return (ImGui::GetIO().Fonts->AddFontFromMemoryTTF(
s_icon_font_data.data(), static_cast<int>(s_icon_font_data.size()), size * 0.75f, &cfg, range_fa) != nullptr);
}
bool AddImGuiFonts()
bool ImGuiManager::AddImGuiFonts()
{
const float standard_font_size = std::ceil(15.0f * s_global_scale);
@ -387,7 +485,7 @@ void Host::ClearOSDMessages()
s_osd_active_messages.clear();
}
static void AcquirePendingOSDMessages()
void ImGuiManager::AcquirePendingOSDMessages()
{
std::atomic_thread_fence(std::memory_order_consume);
if (s_osd_posted_messages.empty())
@ -403,10 +501,9 @@ static void AcquirePendingOSDMessages()
{
OSDMessage& new_msg = s_osd_posted_messages.front();
std::deque<OSDMessage>::iterator iter;
if (!new_msg.key.empty() && (iter = std::find_if(s_osd_active_messages.begin(), s_osd_active_messages.end(),
[&new_msg](const OSDMessage& other) {
return new_msg.key == other.key;
})) != s_osd_active_messages.end())
if (!new_msg.key.empty() &&
(iter = std::find_if(s_osd_active_messages.begin(), s_osd_active_messages.end(),
[&new_msg](const OSDMessage& other) { return new_msg.key == other.key; })) != s_osd_active_messages.end())
{
iter->text = std::move(new_msg.text);
iter->duration = new_msg.duration;
@ -426,7 +523,7 @@ static void AcquirePendingOSDMessages()
}
}
static void DrawOSDMessages()
void ImGuiManager::DrawOSDMessages()
{
ImFont* const font = ImGui::GetFont();
const float scale = s_global_scale;
@ -469,13 +566,13 @@ static void DrawOSDMessages()
ImDrawList* dl = ImGui::GetBackgroundDrawList();
dl->AddRectFilled(pos, ImVec2(pos.x + size.x, pos.y + size.y), IM_COL32(0x21, 0x21, 0x21, alpha), rounding);
dl->AddRect(pos, ImVec2(pos.x + size.x, pos.y + size.y), IM_COL32(0x48, 0x48, 0x48, alpha), rounding);
dl->AddText(font, font->FontSize, ImVec2(text_rect.x, text_rect.y), IM_COL32(0xff, 0xff, 0xff, alpha),
msg.text.c_str(), msg.text.c_str() + msg.text.length(), max_width, &text_rect);
dl->AddText(font, font->FontSize, ImVec2(text_rect.x, text_rect.y), IM_COL32(0xff, 0xff, 0xff, alpha), msg.text.c_str(),
msg.text.c_str() + msg.text.length(), max_width, &text_rect);
position_y += size.y + spacing;
}
}
static void FormatProcessorStat(std::string& text, double usage, double time)
void ImGuiManager::FormatProcessorStat(std::string& text, double usage, double time)
{
// Some values, such as GPU (and even CPU to some extent) can be out of phase with the wall clock,
// which the processor time is divided by to get a utilization percentage. Let's clamp it at 100%,
@ -486,7 +583,7 @@ static void FormatProcessorStat(std::string& text, double usage, double time)
fmt::format_to(std::back_inserter(text), "{:.1f}% ({:.2f}ms)", usage, time);
}
static void DrawPerformanceOverlay()
void ImGuiManager::DrawPerformanceOverlay()
{
const float scale = s_global_scale;
const float shadow_offset = std::ceil(1.0f * scale);
@ -504,14 +601,11 @@ static void DrawPerformanceOverlay()
#define DRAW_LINE(font, text, color) \
do \
{ \
text_size = \
font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, (text), nullptr, nullptr); \
dl->AddText( \
font, font->FontSize, \
text_size = font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, (text), nullptr, nullptr); \
dl->AddText(font, font->FontSize, \
ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x + shadow_offset, position_y + shadow_offset), \
IM_COL32(0, 0, 0, 100), (text)); \
dl->AddText(font, font->FontSize, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y), color, \
(text)); \
dl->AddText(font, font->FontSize, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y), color, (text)); \
position_y += text_size.y + spacing; \
} while (0)
@ -528,18 +622,20 @@ static void DrawPerformanceOverlay()
{
switch (PerformanceMetrics::GetInternalFPSMethod())
{
case PerformanceMetrics::InternalFPSMethod::GSPrivilegedRegister:
fmt::format_to(std::back_inserter(text), "G: {:.2f} [P] | V: {:.2f}", PerformanceMetrics::GetInternalFPS(), PerformanceMetrics::GetFPS());
break;
case PerformanceMetrics::InternalFPSMethod::GSPrivilegedRegister:
fmt::format_to(std::back_inserter(text), "G: {:.2f} [P] | V: {:.2f}", PerformanceMetrics::GetInternalFPS(),
PerformanceMetrics::GetFPS());
break;
case PerformanceMetrics::InternalFPSMethod::DISPFBBlit:
fmt::format_to(std::back_inserter(text), "G: {:.2f} [B] | V: {:.2f}", PerformanceMetrics::GetInternalFPS(), PerformanceMetrics::GetFPS());
break;
case PerformanceMetrics::InternalFPSMethod::DISPFBBlit:
fmt::format_to(std::back_inserter(text), "G: {:.2f} [B] | V: {:.2f}", PerformanceMetrics::GetInternalFPS(),
PerformanceMetrics::GetFPS());
break;
case PerformanceMetrics::InternalFPSMethod::None:
default:
fmt::format_to(std::back_inserter(text), "V: {:.2f}", PerformanceMetrics::GetFPS());
break;
case PerformanceMetrics::InternalFPSMethod::None:
default:
fmt::format_to(std::back_inserter(text), "V: {:.2f}", PerformanceMetrics::GetFPS());
break;
}
first = false;
}
@ -651,6 +747,9 @@ static void DrawPerformanceOverlay()
void ImGuiManager::RenderOSD()
{
// acquire for IO.MousePos.
std::atomic_thread_fence(std::memory_order_acquire);
DrawPerformanceOverlay();
AcquirePendingOSDMessages();
@ -671,3 +770,96 @@ ImFont* ImGuiManager::GetFixedFont()
{
return s_fixed_font;
}
#ifdef PCSX2_CORE
void ImGuiManager::UpdateMousePosition(float x, float y)
{
if (!ImGui::GetCurrentContext())
return;
ImGui::GetIO().MousePos = ImVec2(x, y);
std::atomic_thread_fence(std::memory_order_release);
}
bool ImGuiManager::ProcessPointerButtonEvent(InputBindingKey key, float value)
{
if (!ImGui::GetCurrentContext() || key.data >= std::size(ImGui::GetIO().MouseDown))
return false;
// still update state anyway
GetMTGS().RunOnGSThread([button = key.data, down = (value != 0.0f)]() { ImGui::GetIO().AddMouseButtonEvent(button, down); });
return s_imgui_wants_mouse.load(std::memory_order_acquire);
}
bool ImGuiManager::ProcessPointerAxisEvent(InputBindingKey key, float value)
{
if (!ImGui::GetCurrentContext() || value == 0.0f || key.data < static_cast<u32>(InputPointerAxis::WheelX))
return false;
// still update state anyway
const bool horizontal = (key.data == static_cast<u32>(InputPointerAxis::WheelX));
GetMTGS().RunOnGSThread([wheel_x = horizontal ? value : 0.0f, wheel_y = horizontal ? 0.0f : value]() {
ImGui::GetIO().AddMouseWheelEvent(wheel_x, wheel_y);
});
return s_imgui_wants_mouse.load(std::memory_order_acquire);
}
bool ImGuiManager::ProcessHostKeyEvent(InputBindingKey key, float value)
{
decltype(s_imgui_key_map)::iterator iter;
if (!ImGui::GetCurrentContext() || (iter = s_imgui_key_map.find(key.data)) == s_imgui_key_map.end())
return false;
// still update state anyway
GetMTGS().RunOnGSThread([imkey = iter->second, down = (value != 0.0f)]() { ImGui::GetIO().AddKeyEvent(imkey, down); });
return s_imgui_wants_keyboard.load(std::memory_order_acquire);
}
bool ImGuiManager::ProcessGenericInputEvent(GenericInputBinding key, float value)
{
static constexpr ImGuiKey key_map[] = {
ImGuiKey_None, // Unknown,
ImGuiKey_GamepadDpadUp, // DPadUp
ImGuiKey_GamepadDpadRight, // DPadRight
ImGuiKey_GamepadDpadLeft, // DPadLeft
ImGuiKey_GamepadDpadDown, // DPadDown
ImGuiKey_None, // LeftStickUp
ImGuiKey_None, // LeftStickRight
ImGuiKey_None, // LeftStickDown
ImGuiKey_None, // LeftStickLeft
ImGuiKey_GamepadL3, // L3
ImGuiKey_None, // RightStickUp
ImGuiKey_None, // RightStickRight
ImGuiKey_None, // RightStickDown
ImGuiKey_None, // RightStickLeft
ImGuiKey_GamepadR3, // R3
ImGuiKey_GamepadFaceUp, // Triangle
ImGuiKey_GamepadFaceRight, // Circle
ImGuiKey_GamepadFaceDown, // Cross
ImGuiKey_GamepadFaceLeft, // Square
ImGuiKey_GamepadBack, // Select
ImGuiKey_GamepadStart, // Start
ImGuiKey_None, // System
ImGuiKey_GamepadL1, // L1
ImGuiKey_GamepadL2, // L2
ImGuiKey_GamepadR1, // R1
ImGuiKey_GamepadL2, // R2
};
if (!ImGui::GetCurrentContext() || !s_imgui_wants_keyboard.load(std::memory_order_acquire))
return false;
if (static_cast<u32>(key) >= std::size(key_map) || key_map[static_cast<u32>(key)] == ImGuiKey_None)
return false;
GetMTGS().RunOnGSThread(
[key = key_map[static_cast<u32>(key)], value]() { ImGui::GetIO().AddKeyAnalogEvent(key, (value > 0.0f), value); });
return true;
}
#endif // PCSX2_CORE

View File

@ -17,6 +17,9 @@
struct ImFont;
union InputBindingKey;
enum class GenericInputBinding : u8;
namespace ImGuiManager
{
/// Initializes ImGui, creates fonts, etc.
@ -45,5 +48,25 @@ namespace ImGuiManager
/// Returns the fixed-width font for external drawing.
ImFont* GetFixedFont();
#ifdef PCSX2_CORE
/// Called on the UI or CPU thread in response to mouse movement.
void UpdateMousePosition(float x, float y);
/// Called on the CPU thread in response to a mouse button press.
/// Returns true if ImGui intercepted the event, and regular handlers should not execute.
bool ProcessPointerButtonEvent(InputBindingKey key, float value);
/// Called on the CPU thread in response to a mouse wheel movement.
/// Returns true if ImGui intercepted the event, and regular handlers should not execute.
bool ProcessPointerAxisEvent(InputBindingKey key, float value);
/// Called on the CPU thread in response to a key press.
/// Returns true if ImGui intercepted the event, and regular handlers should not execute.
bool ProcessHostKeyEvent(InputBindingKey key, float value);
/// Called on the CPU thread when any input event fires. Allows imgui to take over controller navigation.
bool ProcessGenericInputEvent(GenericInputBinding key, float value);
#endif
} // namespace ImGuiManager

View File

@ -16,10 +16,14 @@
#include "PrecompiledHeader.h"
#include "Frontend/InputManager.h"
#include "Frontend/InputSource.h"
#include "Frontend/ImGuiManager.h"
#include "PAD/Host/PAD.h"
#include "common/StringUtil.h"
#include "common/Timer.h"
#include "VMManager.h"
#include "fmt/core.h"
#include <array>
#include <memory>
#include <mutex>
@ -36,7 +40,7 @@ enum : u32
{
MAX_KEYS_PER_BINDING = 4,
MAX_MOTORS_PER_PAD = 2,
FIRST_EXTERNAL_INPUT_SOURCE = static_cast<u32>(InputSourceType::Mouse) + 1u,
FIRST_EXTERNAL_INPUT_SOURCE = static_cast<u32>(InputSourceType::Pointer) + 1u,
LAST_EXTERNAL_INPUT_SOURCE = static_cast<u32>(InputSourceType::Count),
};
@ -90,10 +94,8 @@ struct PadVibrationBinding
// ------------------------------------------------------------------------
namespace InputManager
{
static std::optional<InputBindingKey> ParseHostKeyboardKey(
const std::string_view& source, const std::string_view& sub_binding);
static std::optional<InputBindingKey> ParseHostMouseKey(
const std::string_view& source, const std::string_view& sub_binding);
static std::optional<InputBindingKey> ParseHostKeyboardKey(const std::string_view& source, const std::string_view& sub_binding);
static std::optional<InputBindingKey> ParsePointerKey(const std::string_view& source, const std::string_view& sub_binding);
static std::vector<std::string_view> SplitChord(const std::string_view& binding);
static bool SplitBinding(const std::string_view& binding, std::string_view* source, std::string_view* sub_binding);
@ -105,8 +107,10 @@ namespace InputManager
static void AddHotkeyBindings(SettingsInterface& si);
static void AddPadBindings(SettingsInterface& si, u32 pad, const char* default_type);
static void UpdateContinuedVibration();
static void GenerateRelativeMouseEvents();
static bool DoEventHook(InputBindingKey key, float value);
static bool PreprocessEvent(InputBindingKey key, float value, GenericInputBinding generic_key);
} // namespace InputManager
// ------------------------------------------------------------------------
@ -132,6 +136,24 @@ static std::array<std::unique_ptr<InputSource>, static_cast<u32>(InputSourceType
// ------------------------------------------------------------------------
static const HotkeyInfo* const s_hotkey_list[] = {g_vm_manager_hotkeys, g_gs_hotkeys, g_host_hotkeys};
// ------------------------------------------------------------------------
// Tracking host mouse movement and turning into relative events
// 4 axes: pointer left/right, wheel vertical/horizontal. Last/Next/Normalized.
// ------------------------------------------------------------------------
static constexpr const std::array<const char*, static_cast<u8>(InputPointerAxis::Count)> s_pointer_axis_names = {
{"X", "Y", "WheelX", "WheelY"}};
static constexpr const std::array<const char*, 3> s_pointer_button_names = {{"LeftButton", "RightButton", "MiddleButton"}};
struct PointerAxisState
{
std::atomic<s32> delta;
float last_value;
};
static std::array<std::array<float, static_cast<u8>(InputPointerAxis::Count)>, InputManager::MAX_POINTER_DEVICES> s_host_pointer_positions;
static std::array<std::array<PointerAxisState, static_cast<u8>(InputPointerAxis::Count)>, InputManager::MAX_POINTER_DEVICES>
s_pointer_state;
static std::array<float, static_cast<u8>(InputPointerAxis::Count)> s_pointer_axis_scale;
// ------------------------------------------------------------------------
// Binding Parsing
// ------------------------------------------------------------------------
@ -166,8 +188,7 @@ std::vector<std::string_view> InputManager::SplitChord(const std::string_view& b
return parts;
}
bool InputManager::SplitBinding(
const std::string_view& binding, std::string_view* source, std::string_view* sub_binding)
bool InputManager::SplitBinding(const std::string_view& binding, std::string_view* source, std::string_view* sub_binding)
{
const std::string_view::size_type slash_pos = binding.find('/');
if (slash_pos == std::string_view::npos)
@ -192,9 +213,9 @@ std::optional<InputBindingKey> InputManager::ParseInputBindingKey(const std::str
{
return ParseHostKeyboardKey(source, sub_binding);
}
else if (StringUtil::StartsWith(source, "Mouse"))
else if (StringUtil::StartsWith(source, "Pointer"))
{
return ParseHostMouseKey(source, sub_binding);
return ParsePointerKey(source, sub_binding);
}
else
{
@ -241,17 +262,21 @@ std::string InputManager::ConvertInputBindingKeyToString(InputBindingKey key)
{
const std::optional<std::string> str(ConvertHostKeyboardCodeToString(key.data));
if (str.has_value() && !str->empty())
return StringUtil::StdStringFromFormat("Keyboard/%s", str->c_str());
return fmt::format("Keyboard/{}", str->c_str());
}
else if (key.source_type == InputSourceType::Mouse)
else if (key.source_type == InputSourceType::Pointer)
{
if (key.source_subtype == InputSubclass::MouseButton)
return StringUtil::StdStringFromFormat("Mouse%u/Button%u", key.source_index, key.data);
else if (key.source_subtype == InputSubclass::MousePointer)
return StringUtil::StdStringFromFormat("Mouse%u/Pointer%u", key.source_index, key.data);
else if (key.source_subtype == InputSubclass::MouseWheel)
return StringUtil::StdStringFromFormat(
"Mouse%u/Wheel%u%c", key.source_index, key.data, key.negative ? '-' : '+');
if (key.source_subtype == InputSubclass::PointerButton)
{
if (key.data < s_pointer_button_names.size())
return fmt::format("Pointer-{}/{}", u32{key.source_index}, s_pointer_button_names[key.data]);
else
return fmt::format("Pointer-{}/Button{}", u32{key.source_index}, key.data);
}
else if (key.source_subtype == InputSubclass::PointerAxis)
{
return fmt::format("Pointer-{}/{}{:c}", u32{key.source_index}, s_pointer_axis_names[key.data], key.negative ? '-' : '+');
}
}
else if (key.source_type < InputSourceType::Count && s_input_sources[static_cast<u32>(key.source_type)])
{
@ -335,21 +360,23 @@ InputBindingKey InputManager::MakeHostKeyboardKey(s32 key_code)
return key;
}
InputBindingKey InputManager::MakeHostMouseButtonKey(s32 button_index)
InputBindingKey InputManager::MakePointerButtonKey(u32 index, u32 button_index)
{
InputBindingKey key = {};
key.source_type = InputSourceType::Mouse;
key.source_subtype = InputSubclass::MouseButton;
key.data = static_cast<u32>(button_index);
key.source_index = index;
key.source_type = InputSourceType::Pointer;
key.source_subtype = InputSubclass::PointerButton;
key.data = button_index;
return key;
}
InputBindingKey InputManager::MakeHostMouseWheelKey(s32 axis_index)
InputBindingKey InputManager::MakePointerAxisKey(u32 index, InputPointerAxis axis)
{
InputBindingKey key = {};
key.source_type = InputSourceType::Mouse;
key.source_subtype = InputSubclass::MouseWheel;
key.data = static_cast<u32>(axis_index);
key.data = static_cast<u32>(axis);
key.source_index = index;
key.source_type = InputSourceType::Pointer;
key.source_subtype = InputSubclass::PointerAxis;
return key;
}
@ -389,8 +416,7 @@ std::optional<InputSourceType> InputManager::ParseInputSourceString(const std::s
return std::nullopt;
}
std::optional<InputBindingKey> InputManager::ParseHostKeyboardKey(
const std::string_view& source, const std::string_view& sub_binding)
std::optional<InputBindingKey> InputManager::ParseHostKeyboardKey(const std::string_view& source, const std::string_view& sub_binding)
{
if (source != "Keyboard")
return std::nullopt;
@ -405,14 +431,15 @@ std::optional<InputBindingKey> InputManager::ParseHostKeyboardKey(
return key;
}
std::optional<InputBindingKey> InputManager::ParseHostMouseKey(
const std::string_view& source, const std::string_view& sub_binding)
std::optional<InputBindingKey> InputManager::ParsePointerKey(const std::string_view& source, const std::string_view& sub_binding)
{
if (source != "Mouse")
const std::optional<s32> pointer_index = StringUtil::FromChars<s32>(source.substr(8));
if (!pointer_index.has_value() || pointer_index.value() < 0)
return std::nullopt;
InputBindingKey key = {};
key.source_type = InputSourceType::Mouse;
key.source_type = InputSourceType::Pointer;
key.source_index = static_cast<u32>(pointer_index.value());
if (StringUtil::StartsWith(sub_binding, "Button"))
{
@ -420,15 +447,41 @@ std::optional<InputBindingKey> InputManager::ParseHostMouseKey(
if (!button_number.has_value() || button_number.value() < 0)
return std::nullopt;
key.source_subtype = InputSubclass::MouseButton;
key.source_subtype = InputSubclass::PointerButton;
key.data = static_cast<u32>(button_number.value());
}
else
{
return std::nullopt;
return key;
}
return key;
for (u32 i = 0; i < s_pointer_axis_names.size(); i++)
{
if (StringUtil::StartsWith(sub_binding, s_pointer_axis_names[i]))
{
key.source_subtype = InputSubclass::PointerAxis;
key.data = i;
const std::string_view dir_part(sub_binding.substr(std::strlen(s_pointer_axis_names[i])));
if (dir_part == "+")
key.negative = false;
else if (dir_part == "-")
key.negative = true;
else
return std::nullopt;
return key;
}
}
for (u32 i = 0; i < s_pointer_button_names.size(); i++)
{
if (sub_binding == s_pointer_button_names[i])
{
key.source_subtype = InputSubclass::PointerButton;
key.data = i;
return key;
}
}
return std::nullopt;
}
// ------------------------------------------------------------------------
@ -484,17 +537,16 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const ch
if (!bindings.empty())
{
// we use axes for all pad bindings to simplify things, and because they are pressure sensitive
AddBindings(bindings, InputAxisEventHandler{[pad_index, bind_index, bind_names](float value) {
PAD::SetControllerState(pad_index, bind_index, value);
}});
AddBindings(bindings, InputAxisEventHandler{[pad_index, bind_index, bind_names](
float value) { PAD::SetControllerState(pad_index, bind_index, value); }});
}
}
}
for (u32 macro_button_index = 0; macro_button_index < PAD::NUM_MACRO_BUTTONS_PER_CONTROLLER; macro_button_index++)
{
const std::vector<std::string> bindings(si.GetStringList(section.c_str(),
StringUtil::StdStringFromFormat("Macro%u", macro_button_index + 1).c_str()));
const std::vector<std::string> bindings(
si.GetStringList(section.c_str(), StringUtil::StdStringFromFormat("Macro%u", macro_button_index + 1).c_str()));
if (!bindings.empty())
{
AddBindings(bindings, InputButtonEventHandler{[pad_index, macro_button_index](bool state) {
@ -547,14 +599,30 @@ bool InputManager::HasAnyBindingsForKey(InputBindingKey key)
return (s_binding_map.find(key.MaskDirection()) != s_binding_map.end());
}
bool InputManager::HasAnyBindingsForSource(InputBindingKey key)
{
std::unique_lock lock(s_binding_map_write_lock);
for (const auto& it : s_binding_map)
{
const InputBindingKey& okey = it.first;
if (okey.source_type == key.source_type && okey.source_index == key.source_index &&
okey.source_subtype == key.source_subtype)
{
return true;
}
}
return false;
}
bool InputManager::IsAxisHandler(const InputEventHandler& handler)
{
return std::holds_alternative<InputAxisEventHandler>(handler);
}
bool InputManager::InvokeEvents(InputBindingKey key, float value)
bool InputManager::InvokeEvents(InputBindingKey key, float value, GenericInputBinding generic_key)
{
if (DoEventHook(key, value))
if (PreprocessEvent(key, value, generic_key))
return true;
// find all the bindings associated with this key
@ -628,8 +696,7 @@ bool InputManager::InvokeEvents(InputBindingKey key, float value)
binding->current_mask = new_mask;
// invert if we're negative, since the handler expects 0..1
const float value_to_pass = (negative ? ((value < 0.0f) ? -value : 0.0f) : (value > 0.0f) ? value :
0.0f);
const float value_to_pass = (negative ? ((value < 0.0f) ? -value : 0.0f) : (value > 0.0f) ? value : 0.0f);
// axes are fired regardless of a state change, unless they're zero
// (but going from not-zero to zero will still fire, because of the full state)
@ -654,6 +721,89 @@ bool InputManager::InvokeEvents(InputBindingKey key, float value)
return true;
}
bool InputManager::PreprocessEvent(InputBindingKey key, float value, GenericInputBinding generic_key)
{
if (DoEventHook(key, value))
return true;
// does imgui want the event?
if (key.source_type == InputSourceType::Keyboard)
{
if (ImGuiManager::ProcessHostKeyEvent(key, value))
return true;
}
else if (key.source_type == InputSourceType::Pointer && key.source_subtype == InputSubclass::PointerButton)
{
if (ImGuiManager::ProcessPointerButtonEvent(key, value))
return true;
}
else if (generic_key != GenericInputBinding::Unknown)
{
if (ImGuiManager::ProcessGenericInputEvent(generic_key, value))
return true;
}
return false;
}
void InputManager::GenerateRelativeMouseEvents()
{
for (u32 device = 0; device < MAX_POINTER_DEVICES; device++)
{
for (u32 axis = 0; axis < static_cast<u32>(static_cast<u8>(InputPointerAxis::Count)); axis++)
{
PointerAxisState& state = s_pointer_state[device][axis];
const float delta = static_cast<float>(state.delta.exchange(0, std::memory_order_acquire)) / 65536.0f;
const float unclamped_value = delta * s_pointer_axis_scale[axis];
const InputBindingKey key(MakePointerAxisKey(device, static_cast<InputPointerAxis>(axis)));
if (axis >= static_cast<u32>(InputPointerAxis::WheelX) && ImGuiManager::ProcessPointerAxisEvent(key, unclamped_value))
continue;
const float value = std::clamp(unclamped_value, -1.0f, 1.0f);
if (value != state.last_value)
{
state.last_value = value;
InvokeEvents(key, value, GenericInputBinding::Unknown);
}
}
}
}
void InputManager::UpdatePointerAbsolutePosition(u32 index, float x, float y)
{
const float dx = x - std::exchange(s_host_pointer_positions[index][static_cast<u8>(InputPointerAxis::X)], x);
const float dy = y - std::exchange(s_host_pointer_positions[index][static_cast<u8>(InputPointerAxis::Y)], y);
if (dx != 0.0f)
UpdatePointerRelativeDelta(index, InputPointerAxis::X, dx);
if (dy != 0.0f)
UpdatePointerRelativeDelta(index, InputPointerAxis::Y, dy);
ImGuiManager::UpdateMousePosition(x, y);
}
void InputManager::UpdatePointerRelativeDelta(u32 index, InputPointerAxis axis, float d, bool raw_input)
{
s_pointer_state[index][static_cast<u8>(axis)].delta.fetch_add(static_cast<s32>(d * 65536.0f), std::memory_order_release);
}
bool InputManager::HasPointerAxisBinds()
{
std::unique_lock lock(s_binding_map_write_lock);
for (const auto& it : s_binding_map)
{
const InputBindingKey& key = it.first;
if (key.source_type == InputSourceType::Pointer && key.source_subtype == InputSubclass::PointerAxis &&
key.data >= static_cast<u32>(InputPointerAxis::X) && key.data <= static_cast<u32>(InputPointerAxis::Y))
{
return true;
}
}
return false;
}
// ------------------------------------------------------------------------
// Vibration
// ------------------------------------------------------------------------
@ -684,7 +834,8 @@ void InputManager::SetPadVibrationIntensity(u32 pad_index, float large_or_single
{
// both motors are bound to the same source, do an optimal update
large_motor.last_update_time = Common::Timer::GetCurrentValue();
large_motor.source->UpdateMotorState(large_motor.binding, small_motor.binding, large_or_single_motor_intensity, small_motor_intensity);
large_motor.source->UpdateMotorState(
large_motor.binding, small_motor.binding, large_or_single_motor_intensity, small_motor_intensity);
}
else
{
@ -801,7 +952,11 @@ bool InputManager::DoEventHook(InputBindingKey key, float value)
return false;
const InputInterceptHook::CallbackResult action = m_event_intercept_callback(key, value);
return (action == InputInterceptHook::CallbackResult::StopProcessingEvent);
if (action >= InputInterceptHook::CallbackResult::RemoveHookAndStopProcessingEvent)
m_event_intercept_callback = {};
return (action == InputInterceptHook::CallbackResult::RemoveHookAndStopProcessingEvent ||
action == InputInterceptHook::CallbackResult::StopProcessingEvent);
}
// ------------------------------------------------------------------------
@ -825,6 +980,17 @@ void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& bind
// falling back to the base configuration.
for (u32 pad = 0; pad < PAD::NUM_CONTROLLER_PORTS; pad++)
AddPadBindings(binding_si, pad, PAD::GetDefaultPadType(pad));
for (u32 axis = 0; axis < static_cast<u32>(InputPointerAxis::Count); axis++)
{
// From lilypad: 1 mouse pixel = 1/8th way down.
const float default_scale = (axis <= static_cast<u32>(InputPointerAxis::Y)) ? 8.0f : 1.0f;
const float invert =
si.GetBoolValue("Pad", fmt::format("Pointer{}Invert", s_pointer_axis_names[axis]).c_str(), false) ? -1.0f : 1.0f;
s_pointer_axis_scale[axis] =
invert /
std::max(si.GetFloatValue("Pad", fmt::format("Pointer{}Scale", s_pointer_axis_names[axis]).c_str(), default_scale), 1.0f);
}
}
// ------------------------------------------------------------------------
@ -851,6 +1017,8 @@ void InputManager::PollSources()
s_input_sources[i]->PollEvents();
}
GenerateRelativeMouseEvents();
if (VMManager::GetState() == VMState::Running && !s_pad_vibration_array.empty())
UpdateContinuedVibration();
}
@ -953,7 +1121,8 @@ GenericInputBindingMapping InputManager::GetGenericBindingMapping(const std::str
}
template <typename T>
static void UpdateInputSourceState(SettingsInterface& si, std::unique_lock<std::mutex>& settings_lock, InputSourceType type, bool default_state)
static void UpdateInputSourceState(
SettingsInterface& si, std::unique_lock<std::mutex>& settings_lock, InputSourceType type, bool default_state)
{
const bool enabled = si.GetBoolValue("InputSources", InputManager::InputSourceToString(type), default_state);
if (enabled)

View File

@ -28,7 +28,7 @@
enum class InputSourceType : u32
{
Keyboard,
Mouse,
Pointer,
#ifdef _WIN32
//DInput,
XInput,
@ -44,9 +44,8 @@ enum class InputSubclass : u32
{
None = 0,
MouseButton = 0,
MousePointer = 1,
MouseWheel = 2,
PointerButton = 0,
PointerAxis = 1,
ControllerButton = 0,
ControllerAxis = 1,
@ -102,7 +101,9 @@ struct InputInterceptHook
enum class CallbackResult
{
StopProcessingEvent,
ContinueProcessingEvent
ContinueProcessingEvent,
RemoveHookAndStopProcessingEvent,
RemoveHookAndContinueProcessingEvent,
};
using Callback = std::function<CallbackResult(InputBindingKey key, float value)>;
@ -114,19 +115,24 @@ struct HotkeyInfo
const char* name;
const char* category;
const char* display_name;
void(*handler)(bool pressed);
void (*handler)(bool pressed);
};
#define DECLARE_HOTKEY_LIST(name) extern const HotkeyInfo name[]
#define BEGIN_HOTKEY_LIST(name) const HotkeyInfo name[] = {
#define DEFINE_HOTKEY(name, category, display_name, handler) {(name), (category), (display_name), (handler)},
#define END_HOTKEY_LIST() {nullptr, nullptr, nullptr, nullptr} };
#define END_HOTKEY_LIST() \
{ \
nullptr, nullptr, nullptr, nullptr \
} \
} \
;
DECLARE_HOTKEY_LIST(g_vm_manager_hotkeys);
DECLARE_HOTKEY_LIST(g_gs_hotkeys);
DECLARE_HOTKEY_LIST(g_host_hotkeys);
/// Generic input bindings. These roughly match a DualShock 4 or XBox One controller.
/// They are used for automatic binding to PS2 controller types.
/// They are used for automatic binding to PS2 controller types, and for big picture mode navigation.
enum class GenericInputBinding : u8
{
Unknown,
@ -169,13 +175,26 @@ enum class GenericInputBinding : u8
};
using GenericInputBindingMapping = std::vector<std::pair<GenericInputBinding, std::string>>;
/// Host mouse relative axes are X, Y, wheel horizontal, wheel vertical.
enum class InputPointerAxis : u8
{
X,
Y,
WheelX,
WheelY,
Count
};
/// External input source class.
class InputSource;
namespace InputManager
{
/// Minimum interval between vibration updates when the effect is continuous.
static constexpr double VIBRATION_UPDATE_INTERVAL_SECONDS = 0.5; // 500ms
static constexpr double VIBRATION_UPDATE_INTERVAL_SECONDS = 0.5; // 500ms
/// Maximum number of host mouse devices.
static constexpr u32 MAX_POINTER_DEVICES = 1;
/// Returns a pointer to the external input source class, if present.
InputSource* GetInputSourceInterface(InputSourceType type);
@ -196,10 +215,11 @@ namespace InputManager
InputBindingKey MakeHostKeyboardKey(s32 key_code);
/// Creates a key for a host-specific button.
InputBindingKey MakeHostMouseButtonKey(s32 button_index);
InputBindingKey MakePointerButtonKey(u32 index, u32 button_index);
/// Creates a key for a host-specific mouse wheel axis (0 = vertical, 1 = horizontal).
InputBindingKey MakeHostMouseWheelKey(s32 axis_index);
/// Creates a key for a host-specific mouse relative event
/// (axis 0 = horizontal, 1 = vertical, 2 = wheel horizontal, 3 = wheel vertical).
InputBindingKey MakePointerAxisKey(u32 index, InputPointerAxis axis);
/// Parses an input binding key string.
std::optional<InputBindingKey> ParseInputBindingKey(const std::string_view& binding);
@ -235,12 +255,16 @@ namespace InputManager
void PollSources();
/// Returns true if any bindings exist for the specified key.
/// This is the only function which can be safely called on another thread.
/// Can be safely called on another thread.
bool HasAnyBindingsForKey(InputBindingKey key);
/// Returns true if any bindings exist for the specified source + index.
/// Can be safely called on another thread.
bool HasAnyBindingsForSource(InputBindingKey key);
/// Updates internal state for any binds for this key, and fires callbacks as needed.
/// Returns true if anything was bound to this key, otherwise false.
bool InvokeEvents(InputBindingKey key, float value);
bool InvokeEvents(InputBindingKey key, float value, GenericInputBinding generic_key = GenericInputBinding::Unknown);
/// Sets a hook which can be used to intercept events before they're processed by the normal bindings.
/// This is typically used when binding new controls to detect what gets pressed.
@ -259,6 +283,15 @@ namespace InputManager
/// Zeros all vibration intensities. Call when pausing.
/// The pad vibration state will internally remain, so that when emulation is unpaused, the effect resumes.
void PauseVibration();
/// Updates absolute pointer position. Can call from UI thread, use when the host only reports absolute coordinates.
void UpdatePointerAbsolutePosition(u32 index, float x, float y);
/// Updates relative pointer position. Can call from the UI thread, use when host supports relative coordinate reporting.
void UpdatePointerRelativeDelta(u32 index, InputPointerAxis axis, float d, bool raw_input = false);
/// Returns true if any bindings are present which require relative mouse movement.
bool HasPointerAxisBinds();
} // namespace InputManager
namespace Host
@ -268,4 +301,4 @@ namespace Host
/// Called when an input device is disconnected.
void OnInputDeviceDisconnected(const std::string_view& identifier);
}
} // namespace Host

390
pcsx2/Frontend/LogSink.cpp Normal file
View File

@ -0,0 +1,390 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "DebugTools/Debug.h"
#include "Frontend/LogSink.h"
#include "HostSettings.h"
#include "common/Assertions.h"
#include "common/Console.h"
#include "common/FileSystem.h"
#include "common/Path.h"
#include "common/StringUtil.h"
#include "common/Timer.h"
#ifdef _WIN32
#include "common/RedtapeWindows.h"
#endif
#include "fmt/core.h"
#include <csignal>
// Used on both Windows and Linux.
#ifdef _WIN32
static const wchar_t s_console_colors[][ConsoleColors_Count] = {
#define CC(x) L ## x
#else
static const char s_console_colors[][ConsoleColors_Count] = {
#define CC(x) x
#endif
CC("\033[0m"), // default
CC("\033[30m\033[1m"), // black
CC("\033[32m"), // green
CC("\033[31m"), // red
CC("\033[34m"), // blue
CC("\033[35m"), // magenta
CC("\033[35m"), // orange (FIXME)
CC("\033[37m"), // gray
CC("\033[36m"), // cyan
CC("\033[33m"), // yellow
CC("\033[37m"), // white
CC("\033[30m\033[1m"), // strong black
CC("\033[31m\033[1m"), // strong red
CC("\033[32m\033[1m"), // strong green
CC("\033[34m\033[1m"), // strong blue
CC("\033[35m\033[1m"), // strong magenta
CC("\033[35m\033[1m"), // strong orange (FIXME)
CC("\033[37m\033[1m"), // strong gray
CC("\033[36m\033[1m"), // strong cyan
CC("\033[33m\033[1m"), // strong yellow
CC("\033[37m\033[1m") // strong white
};
#undef CC
static Common::Timer::Value s_log_start_timestamp = Common::Timer::GetCurrentValue();
static bool s_log_timestamps = false;
static std::mutex s_log_mutex;
// Replacement for Console so we actually get output to our console window on Windows.
#ifdef _WIN32
static bool s_debugger_attached = false;
static bool s_console_handle_set = false;
static bool s_console_allocated = false;
static HANDLE s_console_handle = INVALID_HANDLE_VALUE;
static HANDLE s_old_console_stdin = NULL;
static HANDLE s_old_console_stdout = NULL;
static HANDLE s_old_console_stderr = NULL;
static BOOL WINAPI ConsoleCtrlHandler(DWORD dwCtrlType)
{
Console.WriteLn("Handler %u", dwCtrlType);
if (dwCtrlType != CTRL_C_EVENT)
return FALSE;
::raise(SIGTERM);
return TRUE;
}
static bool EnableVirtualTerminalProcessing(HANDLE hConsole)
{
if (hConsole == INVALID_HANDLE_VALUE)
return false;
DWORD old_mode;
if (!GetConsoleMode(hConsole, &old_mode))
return false;
// already enabled?
if (old_mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)
return true;
return SetConsoleMode(hConsole, old_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
}
#endif
static void ConsoleQt_SetTitle(const char* title)
{
#ifdef _WIN32
SetConsoleTitleW(StringUtil::UTF8StringToWideString(title).c_str());
#else
std::fprintf(stdout, "\033]0;%s\007", title);
#endif
}
static void ConsoleQt_DoSetColor(ConsoleColors color)
{
#ifdef _WIN32
if (s_console_handle == INVALID_HANDLE_VALUE)
return;
const wchar_t* colortext = s_console_colors[static_cast<u32>(color)];
DWORD written;
WriteConsoleW(s_console_handle, colortext, std::wcslen(colortext), &written, nullptr);
#else
const char* colortext = s_console_colors[static_cast<u32>(color)];
std::fputs(colortext, stdout);
#endif
}
static void ConsoleQt_DoWrite(const char* fmt)
{
std::unique_lock lock(s_log_mutex);
#ifdef _WIN32
if (s_console_handle != INVALID_HANDLE_VALUE || s_debugger_attached)
{
// TODO: Put this on the stack.
std::wstring wfmt(StringUtil::UTF8StringToWideString(fmt));
if (s_debugger_attached)
OutputDebugStringW(wfmt.c_str());
if (s_console_handle != INVALID_HANDLE_VALUE)
{
DWORD written;
WriteConsoleW(s_console_handle, wfmt.c_str(), static_cast<DWORD>(wfmt.length()), &written, nullptr);
}
}
#else
std::fputs(fmt, stdout);
std::fputc('\n', stdout);
#endif
if (emuLog)
{
std::fputs(fmt, emuLog);
}
}
static void ConsoleQt_DoWriteLn(const char* fmt)
{
std::unique_lock lock(s_log_mutex);
// find time since start of process, but save a syscall if we're not writing timestamps
float message_time = s_log_timestamps ?
static_cast<float>(
Common::Timer::ConvertValueToSeconds(Common::Timer::GetCurrentValue() - s_log_start_timestamp)) :
0.0f;
// split newlines up
const char* start = fmt;
do
{
const char* end = std::strchr(start, '\n');
std::string_view line;
if (end)
{
line = std::string_view(start, end - start);
start = end + 1;
}
else
{
line = std::string_view(start);
start = nullptr;
}
#ifdef _WIN32
if (s_console_handle != INVALID_HANDLE_VALUE || s_debugger_attached)
{
// TODO: Put this on the stack.
std::wstring wfmt(StringUtil::UTF8StringToWideString(line));
if (s_debugger_attached)
{
// VS already timestamps logs (at least with the productivity power tools).
if (!wfmt.empty())
OutputDebugStringW(wfmt.c_str());
OutputDebugStringW(L"\n");
}
if (s_console_handle != INVALID_HANDLE_VALUE)
{
DWORD written;
if (s_log_timestamps)
{
wchar_t timestamp_text[128];
const int timestamp_len = _swprintf(timestamp_text, L"[%10.4f] ", message_time);
WriteConsoleW(s_console_handle, timestamp_text, static_cast<DWORD>(timestamp_len), &written, nullptr);
}
if (!wfmt.empty())
WriteConsoleW(s_console_handle, wfmt.c_str(), static_cast<DWORD>(wfmt.length()), &written, nullptr);
WriteConsoleW(s_console_handle, L"\n", 1, &written, nullptr);
}
}
#else
if (s_log_timestamps)
{
std::fprintf(stdout, "[%10.4f] %.*s\n", message_time, static_cast<int>(line.length()), line.data());
}
else
{
if (!line.empty())
std::fwrite(line.data(), line.length(), 1, stdout);
std::fputc('\n', stdout);
}
#endif
if (emuLog)
{
if (s_log_timestamps)
{
std::fprintf(emuLog, "[%10.4f] %.*s\n", message_time, static_cast<int>(line.length()), line.data());
}
else
{
std::fwrite(line.data(), line.length(), 1, emuLog);
std::fputc('\n', emuLog);
}
}
} while (start);
}
static void ConsoleQt_Newline()
{
ConsoleQt_DoWriteLn("");
}
static const IConsoleWriter ConsoleWriter_WinQt =
{
ConsoleQt_DoWrite,
ConsoleQt_DoWriteLn,
ConsoleQt_DoSetColor,
ConsoleQt_DoWrite,
ConsoleQt_Newline,
ConsoleQt_SetTitle,
};
static void UpdateLoggingSinks(bool system_console, bool file_log)
{
#ifdef _WIN32
const bool debugger_attached = IsDebuggerPresent();
s_debugger_attached = debugger_attached;
if (system_console)
{
if (!s_console_handle_set)
{
s_old_console_stdin = GetStdHandle(STD_INPUT_HANDLE);
s_old_console_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
s_old_console_stderr = GetStdHandle(STD_ERROR_HANDLE);
bool handle_valid = (GetConsoleWindow() != NULL);
if (!handle_valid)
{
s_console_allocated = AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole();
handle_valid = (GetConsoleWindow() != NULL);
}
if (handle_valid)
{
s_console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
if (s_console_handle != INVALID_HANDLE_VALUE)
{
s_console_handle_set = true;
SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
// This gets us unix-style coloured output.
EnableVirtualTerminalProcessing(GetStdHandle(STD_OUTPUT_HANDLE));
EnableVirtualTerminalProcessing(GetStdHandle(STD_ERROR_HANDLE));
// Redirect stdout/stderr.
std::FILE* fp;
freopen_s(&fp, "CONIN$", "r", stdin);
freopen_s(&fp, "CONOUT$", "w", stdout);
freopen_s(&fp, "CONOUT$", "w", stderr);
}
}
}
// just in case it fails
system_console = s_console_handle_set;
}
else
{
if (s_console_handle_set)
{
s_console_handle_set = false;
SetConsoleCtrlHandler(ConsoleCtrlHandler, FALSE);
// redirect stdout/stderr back to null.
std::FILE* fp;
freopen_s(&fp, "NUL:", "w", stderr);
freopen_s(&fp, "NUL:", "w", stdout);
freopen_s(&fp, "NUL:", "w", stdin);
// release console and restore state
SetStdHandle(STD_INPUT_HANDLE, s_old_console_stdin);
SetStdHandle(STD_OUTPUT_HANDLE, s_old_console_stdout);
SetStdHandle(STD_ERROR_HANDLE, s_old_console_stderr);
s_old_console_stdin = NULL;
s_old_console_stdout = NULL;
s_old_console_stderr = NULL;
if (s_console_allocated)
{
s_console_allocated = false;
FreeConsole();
}
}
}
#else
const bool debugger_attached = false;
#endif
if (file_log)
{
if (!emuLog)
{
emuLogName = Path::Combine(EmuFolders::Logs, "emulog.txt");
emuLog = FileSystem::OpenCFile(emuLogName.c_str(), "wb");
file_log = (emuLog != nullptr);
}
}
else
{
if (emuLog)
{
std::fclose(emuLog);
emuLog = nullptr;
emuLogName = {};
}
}
// Discard logs completely if there's no sinks.
if (debugger_attached || system_console || file_log)
Console_SetActiveHandler(ConsoleWriter_WinQt);
else
Console_SetActiveHandler(ConsoleWriter_Null);
}
void Host::InitializeEarlyConsole()
{
UpdateLoggingSinks(true, false);
}
void Host::UpdateLogging()
{
const bool system_console_enabled = Host::GetBaseBoolSettingValue("Logging", "EnableSystemConsole", false);
const bool file_logging_enabled = Host::GetBaseBoolSettingValue("Logging", "EnableFileLogging", false);
s_log_timestamps = Host::GetBaseBoolSettingValue("Logging", "EnableTimestamps", true);
const bool any_logging_sinks = system_console_enabled || file_logging_enabled;
DevConWriterEnabled = any_logging_sinks && (IsDevBuild || Host::GetBaseBoolSettingValue("Logging", "EnableVerbose", false));
SysConsole.eeConsole.Enabled = any_logging_sinks && Host::GetBaseBoolSettingValue("Logging", "EnableEEConsole", false);
SysConsole.iopConsole.Enabled = any_logging_sinks && Host::GetBaseBoolSettingValue("Logging", "EnableIOPConsole", false);
// Input Recording Logs
SysConsole.recordingConsole.Enabled = any_logging_sinks && Host::GetBaseBoolSettingValue("Logging", "EnableInputRecordingLogs", true);
SysConsole.controlInfo.Enabled = any_logging_sinks && Host::GetBaseBoolSettingValue("Logging", "EnableControllerLogs", false);
UpdateLoggingSinks(system_console_enabled, file_logging_enabled);
}

25
pcsx2/Frontend/LogSink.h Normal file
View File

@ -0,0 +1,25 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
namespace Host
{
/// Updates the Console handler based on the current configuration.
void UpdateLogging();
/// Initializes early console logging (for printing command line arguments).
void InitializeEarlyConsole();
}

View File

@ -56,7 +56,6 @@ public:
bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) override;
bool MakeRenderContextCurrent() override;
bool DoneRenderContextCurrent() override;
void DestroyRenderDevice() override;
void DestroyRenderSurface() override;
bool ChangeRenderWindow(const WindowInfo& wi) override;
bool SupportsFullscreen() const override;

View File

@ -49,6 +49,9 @@ MetalHostDisplay::MetalHostDisplay()
MetalHostDisplay::~MetalHostDisplay()
{
MetalHostDisplay::DestroyRenderSurface();
m_queue = nullptr;
m_dev.Reset();
}
HostDisplay::AdapterAndModeList GetMetalAdapterAndModeList()
@ -158,13 +161,6 @@ bool MetalHostDisplay::InitializeRenderDevice(std::string_view shader_cache_dire
bool MetalHostDisplay::MakeRenderContextCurrent() { return true; }
bool MetalHostDisplay::DoneRenderContextCurrent() { return true; }
void MetalHostDisplay::DestroyRenderDevice()
{
DestroyRenderSurface();
m_queue = nullptr;
m_dev.Reset();
}
void MetalHostDisplay::DestroyRenderSurface()
{
if (!m_layer)

View File

@ -53,7 +53,11 @@ OpenGLHostDisplay::OpenGLHostDisplay() = default;
OpenGLHostDisplay::~OpenGLHostDisplay()
{
pxAssertMsg(!m_gl_context, "Context should have been destroyed by now");
if (m_gl_context)
{
m_gl_context->DoneCurrent();
m_gl_context.reset();
}
}
HostDisplay::RenderAPI OpenGLHostDisplay::GetRenderAPI() const
@ -239,15 +243,6 @@ bool OpenGLHostDisplay::DoneRenderContextCurrent()
return m_gl_context->DoneCurrent();
}
void OpenGLHostDisplay::DestroyRenderDevice()
{
if (!m_gl_context)
return;
m_gl_context->DoneCurrent();
m_gl_context.reset();
}
bool OpenGLHostDisplay::ChangeRenderWindow(const WindowInfo& new_wi)
{
pxAssert(m_gl_context);
@ -481,18 +476,21 @@ void OpenGLHostDisplay::KickTimestampQuery()
m_timestamp_query_started = true;
}
void OpenGLHostDisplay::SetGPUTimingEnabled(bool enabled)
bool OpenGLHostDisplay::SetGPUTimingEnabled(bool enabled)
{
enabled &= (!m_gl_context->IsGLES() || GLAD_GL_EXT_disjoint_timer_query);
if (m_gpu_timing_enabled == enabled)
return;
return true;
if (enabled && m_gl_context->IsGLES() && !GLAD_GL_EXT_disjoint_timer_query)
return false;
m_gpu_timing_enabled = enabled;
if (m_gpu_timing_enabled)
CreateTimestampQueries();
else
DestroyTimestampQueries();
return true;
}
float OpenGLHostDisplay::GetAndResetAccumulatedGPUTime()

View File

@ -40,7 +40,6 @@ public:
bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, VsyncMode vsync, bool threaded_presentation, bool debug_device) override;
bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) override;
void DestroyRenderDevice() override;
bool MakeRenderContextCurrent() override;
bool DoneRenderContextCurrent() override;
@ -62,7 +61,7 @@ public:
bool BeginPresent(bool frame_skip) override;
void EndPresent() override;
void SetGPUTimingEnabled(bool enabled) override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
protected:

View File

@ -498,7 +498,10 @@ bool SDLInputSource::HandleControllerButtonEvent(const SDL_ControllerButtonEvent
return false;
const InputBindingKey key(MakeGenericControllerButtonKey(InputSourceType::SDL, it->player_id, ev->button));
return InputManager::InvokeEvents(key, (ev->state == SDL_PRESSED) ? 1.0f : 0.0f);
const GenericInputBinding generic_key = (ev->button < std::size(s_sdl_generic_binding_button_mapping)) ?
s_sdl_generic_binding_button_mapping[ev->button] :
GenericInputBinding::Unknown;
return InputManager::InvokeEvents(key, (ev->state == SDL_PRESSED) ? 1.0f : 0.0f, generic_key);
}
std::vector<InputBindingKey> SDLInputSource::EnumerateMotors()

View File

@ -51,8 +51,14 @@ VulkanHostDisplay::VulkanHostDisplay() = default;
VulkanHostDisplay::~VulkanHostDisplay()
{
pxAssertRel(!g_vulkan_context, "Context should have been destroyed by now");
pxAssertRel(!m_swap_chain, "Swap chain should have been destroyed by now");
if (g_vulkan_context)
{
g_vulkan_context->WaitForGPUIdle();
m_swap_chain.reset();
Vulkan::ShaderCache::Destroy();
Vulkan::Context::Destroy();
}
}
HostDisplay::RenderAPI VulkanHostDisplay::GetRenderAPI() const
@ -163,7 +169,6 @@ HostDisplay::AdapterAndModeList VulkanHostDisplay::GetAdapterAndModeList()
void VulkanHostDisplay::DestroyRenderSurface()
{
m_window_info = {};
g_vulkan_context->WaitForGPUIdle();
m_swap_chain.reset();
}
@ -195,8 +200,8 @@ std::string VulkanHostDisplay::GetDriverInfo() const
static bool UploadBufferToTexture(
Vulkan::Texture* texture, VkCommandBuffer cmdbuf, u32 width, u32 height, const void* data, u32 data_stride)
{
const u32 upload_stride = Common::AlignUpPow2(Vulkan::Util::GetTexelSize(texture->GetFormat()) * width,
g_vulkan_context->GetBufferCopyRowPitchAlignment());
const u32 texel_size = Vulkan::Util::GetTexelSize(texture->GetFormat());
const u32 upload_stride = Common::AlignUpPow2(texel_size * width, g_vulkan_context->GetBufferCopyRowPitchAlignment());
const u32 upload_size = upload_stride * height;
Vulkan::StreamBuffer& buf = g_vulkan_context->GetTextureUploadBuffer();
@ -216,7 +221,7 @@ static bool UploadBufferToTexture(
StringUtil::StrideMemCpy(buf.GetCurrentHostPointer(), upload_stride, data, data_stride, upload_stride, height);
buf.CommitMemory(upload_size);
texture->UpdateFromBuffer(cmdbuf, 0, 0, 0, 0, width, height, width, buf.GetBuffer(), buf_offset);
texture->UpdateFromBuffer(cmdbuf, 0, 0, 0, 0, width, height, upload_stride / texel_size, buf.GetBuffer(), buf_offset);
return true;
}
@ -320,18 +325,6 @@ bool VulkanHostDisplay::UpdateImGuiFontTexture()
return ImGui_ImplVulkan_CreateFontsTexture();
}
void VulkanHostDisplay::DestroyRenderDevice()
{
if (!g_vulkan_context)
return;
g_vulkan_context->WaitForGPUIdle();
Vulkan::ShaderCache::Destroy();
DestroyRenderSurface();
Vulkan::Context::Destroy();
}
bool VulkanHostDisplay::MakeRenderContextCurrent()
{
return true;
@ -419,9 +412,9 @@ void VulkanHostDisplay::EndPresent()
g_vulkan_context->MoveToNextCommandBuffer();
}
void VulkanHostDisplay::SetGPUTimingEnabled(bool enabled)
bool VulkanHostDisplay::SetGPUTimingEnabled(bool enabled)
{
g_vulkan_context->SetEnableGPUTiming(enabled);
return g_vulkan_context->SetEnableGPUTiming(enabled);
}
float VulkanHostDisplay::GetAndResetAccumulatedGPUTime()

View File

@ -29,7 +29,6 @@ public:
bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, VsyncMode vsync, bool threaded_presentation, bool debug_device) override;
bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) override;
void DestroyRenderDevice() override;
bool MakeRenderContextCurrent() override;
bool DoneRenderContextCurrent() override;
@ -51,7 +50,7 @@ public:
bool BeginPresent(bool frame_skip) override;
void EndPresent() override;
void SetGPUTimingEnabled(bool enabled) override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
static AdapterAndModeList StaticGetAdapterAndModeList(const WindowInfo* wi);

View File

@ -29,7 +29,7 @@ const char* XInputSource::s_axis_names[XInputSource::NUM_AXES] = {
"LeftTrigger", // AXIS_TRIGGERLEFT
"RightTrigger", // AXIS_TRIGGERRIGHT
};
static const GenericInputBinding s_sdl_generic_binding_axis_mapping[][2] = {
static const GenericInputBinding s_xinput_generic_binding_axis_mapping[][2] = {
{GenericInputBinding::LeftStickLeft, GenericInputBinding::LeftStickRight}, // AXIS_LEFTX
{GenericInputBinding::LeftStickUp, GenericInputBinding::LeftStickDown}, // AXIS_LEFTY
{GenericInputBinding::RightStickLeft, GenericInputBinding::RightStickRight}, // AXIS_RIGHTX
@ -72,7 +72,7 @@ const u16 XInputSource::s_button_masks[XInputSource::NUM_BUTTONS] = {
XINPUT_GAMEPAD_Y,
0x400, // XINPUT_GAMEPAD_GUIDE
};
static const GenericInputBinding s_sdl_generic_binding_button_mapping[] = {
static const GenericInputBinding s_xinput_generic_binding_button_mapping[] = {
GenericInputBinding::DPadUp, // XINPUT_GAMEPAD_DPAD_UP
GenericInputBinding::DPadDown, // XINPUT_GAMEPAD_DPAD_DOWN
GenericInputBinding::DPadLeft, // XINPUT_GAMEPAD_DPAD_LEFT
@ -347,19 +347,19 @@ bool XInputSource::GetGenericBindingMapping(const std::string_view& device, Gene
// assume all buttons are present.
const s32 pid = player_id.value();
for (u32 i = 0; i < std::size(s_sdl_generic_binding_axis_mapping); i++)
for (u32 i = 0; i < std::size(s_xinput_generic_binding_axis_mapping); i++)
{
const GenericInputBinding negative = s_sdl_generic_binding_axis_mapping[i][0];
const GenericInputBinding positive = s_sdl_generic_binding_axis_mapping[i][1];
const GenericInputBinding negative = s_xinput_generic_binding_axis_mapping[i][0];
const GenericInputBinding positive = s_xinput_generic_binding_axis_mapping[i][1];
if (negative != GenericInputBinding::Unknown)
mapping->emplace_back(negative, StringUtil::StdStringFromFormat("XInput-%d/-%s", pid, s_axis_names[i]));
if (positive != GenericInputBinding::Unknown)
mapping->emplace_back(positive, StringUtil::StdStringFromFormat("XInput-%d/+%s", pid, s_axis_names[i]));
}
for (u32 i = 0; i < std::size(s_sdl_generic_binding_button_mapping); i++)
for (u32 i = 0; i < std::size(s_xinput_generic_binding_button_mapping); i++)
{
const GenericInputBinding binding = s_sdl_generic_binding_button_mapping[i];
const GenericInputBinding binding = s_xinput_generic_binding_button_mapping[i];
if (binding != GenericInputBinding::Unknown)
mapping->emplace_back(binding, StringUtil::StdStringFromFormat("XInput-%d/%s", pid, s_button_names[i]));
}
@ -435,9 +435,12 @@ void XInputSource::CheckForStateChanges(u32 index, const XINPUT_STATE& new_state
const u16 button_mask = s_button_masks[button];
if ((old_button_bits & button_mask) != (new_button_bits & button_mask))
{
const GenericInputBinding generic_key = (button < std::size(s_xinput_generic_binding_button_mapping)) ?
s_xinput_generic_binding_button_mapping[button] : GenericInputBinding::Unknown;
const float value = ((new_button_bits & button_mask) != 0) ? 1.0f : 0.0f;
InputManager::InvokeEvents(
MakeGenericControllerButtonKey(InputSourceType::XInput, index, button),
(new_button_bits & button_mask) != 0);
value, generic_key);
}
}
}

View File

@ -271,7 +271,7 @@ bool ImGui_ImplDX12_CreateFontsTexture()
}
}
if (!bd->FontTexture.LoadData(0, 0, 0, width, height, pixels, width * sizeof(u32)))
if (!bd->FontTexture.LoadData(g_d3d12_context->GetInitCommandList(), 0, 0, 0, width, height, pixels, width * sizeof(u32)))
return false;
io.Fonts->SetTexID((ImTextureID)&bd->FontTexture);

View File

@ -281,10 +281,10 @@ static bool DoGSOpen(GSRendererType renderer, u8* basemem)
return false;
}
g_gs_renderer->SetRegsMem(basemem);
display->SetVSync(EmuConfig.GetEffectiveVsyncMode());
display->SetGPUTimingEnabled(GSConfig.OsdShowGPU);
GSConfig.OsdShowGPU = EmuConfig.GS.OsdShowGPU && display->SetGPUTimingEnabled(true);
g_gs_renderer->SetRegsMem(basemem);
return true;
}
@ -861,7 +861,10 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
if (GSConfig.OsdShowGPU != old_config.OsdShowGPU)
{
if (HostDisplay* display = Host::GetHostDisplay(); display)
display->SetGPUTimingEnabled(GSConfig.OsdShowGPU);
{
if (!display->SetGPUTimingEnabled(GSConfig.OsdShowGPU))
GSConfig.OsdShowGPU = false;
}
}
}
@ -1370,6 +1373,7 @@ void GSApp::Init()
m_default_configuration["fxaa"] = "0";
m_default_configuration["GSDumpCompression"] = "0";
m_default_configuration["HWDisableReadbacks"] = "0";
m_default_configuration["pcrtc_antiblur"] = "1";
m_default_configuration["disable_interlace_offset"] = "0";
m_default_configuration["pcrtc_offsets"] = "0";
m_default_configuration["pcrtc_overscan"] = "0";

View File

@ -42,7 +42,7 @@ GSState::GSState()
, m_skip(0)
, m_skip_offset(0)
, m_q(1.0f)
, m_scanmask_used(false)
, m_scanmask_used(0)
, tex_flushed(true)
, m_vt(this, IsFirstProvokingVertex())
, m_regs(NULL)
@ -182,7 +182,7 @@ void GSState::Reset(bool hardware_reset)
m_vertex.tail = 0;
m_vertex.next = 0;
m_index.tail = 0;
m_scanmask_used = false;
m_scanmask_used = 0;
m_dirty_gs_regs = 0;
m_backed_up_ctx = -1;
@ -1271,7 +1271,7 @@ void GSState::GIFRegHandlerSCANMSK(const GIFReg* RESTRICT r)
m_env.SCANMSK = (GSVector4i)r->SCANMSK;
if (m_env.SCANMSK.MSK & 2)
m_scanmask_used = true;
m_scanmask_used = 2;
if (m_prev_env.SCANMSK.MSK != m_env.SCANMSK.MSK)
m_dirty_gs_regs |= (1 << DIRTY_REG_SCANMSK);

View File

@ -161,7 +161,7 @@ protected:
GSVector4i m_scissor;
GSVector4i m_ofxy;
bool m_scanmask_used;
u8 m_scanmask_used;
bool tex_flushed;
struct

View File

@ -377,7 +377,7 @@ void GSDevice::Interlace(const GSVector2i& ds, int field, int mode, float yoffse
if (mode == 0 || mode == 2) // weave or blend
{
// weave first
const int offset = static_cast<int>(yoffset) * (1 - field);
const int offset = static_cast<int>(yoffset) * field;
DoInterlace(m_merge, m_weavebob, field, false, GSConfig.DisableInterlaceOffset ? 0 : offset);
@ -398,7 +398,8 @@ void GSDevice::Interlace(const GSVector2i& ds, int field, int mode, float yoffse
}
else if (mode == 1) // bob
{
DoInterlace(m_merge, m_weavebob, 3, true, yoffset * field);
// Field is reversed here as we are countering the bounce.
DoInterlace(m_merge, m_weavebob, 3, true, yoffset * (1-field));
m_current = m_weavebob;
}

View File

@ -95,6 +95,7 @@ bool GSRenderer::Merge(int field)
GSVector2i frame_baseline = {INT_MAX, INT_MAX};
GSVector2i display_combined = {0, 0};
bool feedback_merge = m_regs->EXTWRITE.WRITE == 1;
bool display_offset = false;
for (int i = 0; i < 2; i++)
{
@ -114,6 +115,7 @@ bool GSRenderer::Merge(int field)
frame_baseline.x = std::min(fr[i].left, frame_baseline.x);
frame_baseline.y = std::min(fr[i].top, frame_baseline.y);
display_offset |= std::abs(display_baseline.y - display_offsets[i].y) == 1;
/*DevCon.Warning("Read offset was X %d(left %d) Y %d(top %d)", display_baseline.x, dr[i].left, display_baseline.y, dr[i].top);
DevCon.Warning("[%d]: %d %d %d %d, %d %d %d %d\n", i, fr[i].x,fr[i].y,fr[i].z,fr[i].w , dr[i].x,dr[i].y,dr[i].z,dr[i].w);
DevCon.Warning("Offset X %d Offset Y %d", display_offsets[i].x, display_offsets[i].y);*/
@ -168,7 +170,7 @@ bool GSRenderer::Merge(int field)
const bool slbg = m_regs->PMODE.SLBG;
GSVector2i resolution(GetResolution());
bool scanmask_frame = true;
bool scanmask_frame = m_scanmask_used && !display_offset;
const bool ignore_offset = !GSConfig.PCRTCOffsets;
for (int i = 0; i < 2; i++)
@ -186,55 +188,64 @@ bool GSRenderer::Merge(int field)
// If using scanmsk we have to keep the single line offset, regardless of upscale
// so we handle this separately after the rect calculations.
const int interlace_offset = display_diff.y & 1;
if (m_scanmask_used && interlace_offset)
float interlace_offset = 0.0f;
if ((!GSConfig.PCRTCAntiBlur || m_scanmask_used) && display_offset)
{
display_diff.y &= ~1;
scanmask_frame = false;
interlace_offset = static_cast<float>(display_diff.y & 1);
// When the displays are offset by 1 we need to adjust for upscale to handle it (reduces bounce in MGS2 when upscaling)
interlace_offset += (tex[i]->GetScale().y - 1.0f) / 2;
if (!ignore_offset)
off.y &= ~1;
display_diff.y &= ~1;
}
// Start of Anti-Blur code.
if (!ignore_offset)
{
if (samesrc)
if (GSConfig.PCRTCAntiBlur)
{
// Offset by DISPLAY setting
if (display_diff.x < 4)
off.x -= display_diff.x;
if (display_diff.y < 4)
off.y -= display_diff.y;
if (samesrc)
{
// Offset by DISPLAY setting
if (display_diff.x < 4)
off.x -= display_diff.x;
if (display_diff.y < 4)
off.y -= display_diff.y;
// Offset by DISPFB setting
if (frame_diff.x == 1)
off.x += 1;
if (frame_diff.y == 1)
off.y += 1;
// Offset by DISPFB setting
if (frame_diff.x == 1)
off.x += 1;
if (frame_diff.y == 1)
off.y += 1;
}
}
}
else
{
if (!slbg || !feedback_merge)
{
const int videomode = static_cast<int>(GetVideoMode()) - 1;
const GSVector4i offsets = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode] : VideoModeOffsetsOverscan[videomode];
GSVector2i base_resolution(offsets.x, offsets.y);
if (isinterlaced() && !m_regs->SMODE2.FFMD)
base_resolution.y *= 2;
// If the offsets between the two displays are quite large, it's probably intended for an effect.
if (display_diff.x >= 4)
if (display_diff.x >= 4 || !GSConfig.PCRTCAntiBlur)
off.x = display_diff.x;
if (display_diff.y >= 4)
if (display_diff.y >= 4 || !GSConfig.PCRTCAntiBlur)
off.y = display_diff.y;
// Anti blur hax.
if (samesrc)
{
// Adjusting the screen offset when using a negative offset.
const int videomode = static_cast<int>(GetVideoMode()) - 1;
const GSVector4i offsets = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode] : VideoModeOffsetsOverscan[videomode];
GSVector2i base_resolution(offsets.x, offsets.y);
if (isinterlaced() && !m_regs->SMODE2.FFMD)
base_resolution.y *= 2;
// Offset by DISPLAY setting
if (display_diff.x < 0)
{
@ -249,17 +260,21 @@ bool GSRenderer::Merge(int field)
resolution.y -= display_diff.y;
}
// Offset by DISPFB setting
if (frame_diff.x == 1)
off.x += 1;
if (frame_diff.y == 1)
off.y += 1;
// Don't do X, we only care about height, this would need to be tailored for games using X (Black does -5).
// Mainly for Hokuto no Ken which does -14 Y offset.
if (display_baseline.y < -4)
off.y += display_baseline.y;
// Anti-Blur stuff
if (GSConfig.PCRTCAntiBlur)
{
// Offset by DISPFB setting
if (frame_diff.x == 1)
off.x += 1;
if (frame_diff.y == 1)
off.y += 1;
}
}
}
}
@ -277,9 +292,8 @@ bool GSRenderer::Merge(int field)
// src_out_rect is the resized rect for output. (Not really used)
src_out_rect[i] = (GSVector4(r) * scale) / GSVector4(tex[i]->GetSize()).xyxy();
// Restore the single line offset for scanmsk.
if (m_scanmask_used && interlace_offset)
dst[i] += GSVector4(0.0f, 1.0f, 0.0f, 1.0f);
// Restore manually offset "interlace" lines
dst[i] += GSVector4(0.0f, interlace_offset, 0.0f, interlace_offset);
}
if (feedback_merge && tex[2])
@ -335,24 +349,24 @@ bool GSRenderer::Merge(int field)
g_gs_device->Merge(tex, src_gs_read, dst, fs, m_regs->PMODE, m_regs->EXTBUF, c);
// Offset is not compatible with scanmsk, as scanmsk renders every other line, but at x7 the interlace offset will be 7 lines
const int offset = (m_scanmask_used || !m_regs->SMODE2.FFMD) ? 0 : (int)(tex[1] ? tex[1]->GetScale().y : tex[0]->GetScale().y);
// Use offset for bob deinterlacing, for normal deinterlacing we add an extra 1 since it's clearer, with bob that causes extra shake.
const bool is_bob = GSConfig.InterlaceMode == GSInterlaceMode::BobTFF || GSConfig.InterlaceMode == GSInterlaceMode::BobBFF;
const float offset = (!m_regs->SMODE2.FFMD && !is_bob) ? 0 : ((tex[1] ? tex[1]->GetScale().y : tex[0]->GetScale().y));
if (isReallyInterlaced() && GSConfig.InterlaceMode != GSInterlaceMode::Off)
{
const bool scanmask = m_scanmask_used && scanmask_frame && GSConfig.InterlaceMode == GSInterlaceMode::Automatic;
if (GSConfig.InterlaceMode == GSInterlaceMode::Automatic && (m_regs->SMODE2.FFMD)) // Auto interlace enabled / Odd frame interlace setting
// FFMD (half frames) requires blend deinterlacing, so automatically use that. Same when SCANMSK is used but not blended in the merge circuit (Alpine Racer 3)
if (GSConfig.InterlaceMode == GSInterlaceMode::Automatic && (m_regs->SMODE2.FFMD || scanmask_frame))
{
constexpr int field2 = 1;
constexpr int field2 = 0;
constexpr int mode = 2;
g_gs_device->Interlace(ds, field ^ field2, mode, offset);
}
else
{
const int field2 = scanmask ? 0 : 1 - ((static_cast<int>(GSConfig.InterlaceMode) - 1) & 1);
const int mode = scanmask ? 2 : ((static_cast<int>(GSConfig.InterlaceMode) - 1) >> 1);
const int field2 = ((static_cast<int>(GSConfig.InterlaceMode) - 1) & 1);
const int mode = ((static_cast<int>(GSConfig.InterlaceMode) - 1) >> 1);
g_gs_device->Interlace(ds, field ^ field2, mode, offset);
}
@ -374,7 +388,8 @@ bool GSRenderer::Merge(int field)
}
}
m_scanmask_used = false;
if (m_scanmask_used)
m_scanmask_used--;
return true;
}

View File

@ -1753,7 +1753,7 @@ void GSDeviceMTL::RenderImGui(ImDrawData* data)
[enc setVertexBytes:&transform length:sizeof(transform) atIndex:GSMTLBufferIndexUniforms];
simd::uint4 last_scissor = simd::make_uint4(0, 0, m_display->GetWindowWidth(), m_display->GetWindowHeight());
simd::float2 fb_size = simd::float2(last_scissor.zw);
simd::float2 fb_size = simd_float(last_scissor.zw);
simd::float2 clip_off = ToSimd(data->DisplayPos); // (0,0) unless using multi-viewports
simd::float2 clip_scale = ToSimd(data->FramebufferScale); // (1,1) unless using retina display which are often (2,2)
ImTextureID last_tex = nullptr;
@ -1779,7 +1779,7 @@ void GSDeviceMTL::RenderImGui(ImDrawData* data)
clip_max = simd::min(clip_max, fb_size);
if (simd::any(clip_min >= clip_max))
continue;
simd::uint4 scissor = simd::make_uint4(simd::uint2(clip_min), simd::uint2(clip_max - clip_min));
simd::uint4 scissor = simd::make_uint4(simd_uint(clip_min), simd_uint(clip_max - clip_min));
ImTextureID tex = cmd.GetTexID();
if (simd::any(scissor != last_scissor))
{

View File

@ -97,6 +97,9 @@ const char* dialog_message(int ID, bool* updateText)
return cvtString("Shows more overscan area for some games which expect there to be some.\n"
"Used for games like Need for Speed Underground (PAL),Crash Nitro Kart (PAL) \n"
"and Espgaluda (When using certain Tate modes)");
case IDC_PCRTC_ANTIBLUR:
return cvtString("Enable: Adds internal anti-blur hacks on the merge ciruit.\n"
"This will likely make a lot of games look crisper but is less accurate to the PS2.");
case IDC_DISABLE_INTERLACE_OFFSETS:
return cvtString("Enable: Removes the offset for interlacing when upscaling.\n"
"Can reduce blurring in some games, where the opposite is true most of the time.\n"

View File

@ -44,6 +44,7 @@ enum
IDC_FILTER,
IDC_PCRTC_OFFSETS,
IDC_PCRTC_OVERSCAN,
IDC_PCRTC_ANTIBLUR,
IDC_DISABLE_INTERLACE_OFFSETS,
// Hardware Renderer
IDC_PRELOAD_TEXTURES,

View File

@ -324,7 +324,7 @@ RendererTab::RendererTab(wxWindow* parent)
m_ui.addCheckBox(pcrtc_checks_box, "Screen Offsets", "pcrtc_offsets", IDC_PCRTC_OFFSETS);
m_ui.addCheckBox(pcrtc_checks_box, "Show Overscan", "pcrtc_overscan", IDC_PCRTC_OVERSCAN);
m_ui.addCheckBox(pcrtc_checks_box, "Disable Interlace Offset", "disable_interlace_offset", IDC_DISABLE_INTERLACE_OFFSETS);
m_ui.addCheckBox(pcrtc_checks_box, "Anti-Blur", "pcrtc_antiblur", IDC_PCRTC_ANTIBLUR);
general_box->Add(pcrtc_checks_box, wxSizerFlags().Center());
tab_box->Add(hardware_box.outer, wxSizerFlags().Expand());

View File

@ -65,8 +65,9 @@ bool HostDisplay::GetHostRefreshRate(float* refresh_rate)
return WindowInfo::QueryRefreshRateForWindow(m_window_info, refresh_rate);
}
void HostDisplay::SetGPUTimingEnabled(bool enabled)
bool HostDisplay::SetGPUTimingEnabled(bool enabled)
{
return false;
}
float HostDisplay::GetAndResetAccumulatedGPUTime()

View File

@ -101,7 +101,6 @@ public:
virtual bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) = 0;
virtual bool MakeRenderContextCurrent() = 0;
virtual bool DoneRenderContextCurrent() = 0;
virtual void DestroyRenderDevice() = 0;
virtual void DestroyRenderSurface() = 0;
virtual bool ChangeRenderWindow(const WindowInfo& wi) = 0;
virtual bool SupportsFullscreen() const = 0;
@ -136,7 +135,7 @@ public:
virtual bool GetHostRefreshRate(float* refresh_rate);
/// Enables/disables GPU frame timing.
virtual void SetGPUTimingEnabled(bool enabled);
virtual bool SetGPUTimingEnabled(bool enabled);
/// Returns the amount of GPU time utilized since the last time this method was called.
virtual float GetAndResetAccumulatedGPUTime();

View File

@ -15,9 +15,14 @@
#include "PrecompiledHeader.h"
#include "common/Assertions.h"
#include "Frontend/LayeredSettingsInterface.h"
#include "GS.h"
#include "GS/Renderers/HW/GSTextureReplacements.h"
#include "Host.h"
#include "HostSettings.h"
#include "Frontend/LayeredSettingsInterface.h"
#include "MemoryCardFile.h"
#include "Sio.h"
#include "VMManager.h"
static std::mutex s_settings_mutex;
static LayeredSettingsInterface s_layered_settings_interface;
@ -202,3 +207,45 @@ void Host::Internal::SetInputSettingsLayer(SettingsInterface* sif)
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_INPUT, sif);
}
void Host::Internal::UpdateEmuFolders()
{
const std::string old_cheats_directory(EmuFolders::Cheats);
const std::string old_cheats_ws_directory(EmuFolders::CheatsWS);
const std::string old_cheats_ni_directory(EmuFolders::CheatsNI);
const std::string old_memcards_directory(EmuFolders::MemoryCards);
const std::string old_textures_directory(EmuFolders::Textures);
EmuFolders::LoadConfig(*GetBaseSettingsLayer());
EmuFolders::EnsureFoldersExist();
if (VMManager::HasValidVM())
{
if (EmuFolders::Cheats != old_cheats_directory ||
EmuFolders::CheatsWS != old_cheats_ws_directory ||
EmuFolders::CheatsNI != old_cheats_ni_directory)
{
VMManager::ReloadPatches(true, true);
}
if (EmuFolders::MemoryCards != old_memcards_directory)
{
FileMcd_EmuClose();
FileMcd_EmuOpen();
for (u32 port = 0; port < 2; port++)
{
for (u32 slot = 0; slot < 4; slot++)
SetForceMcdEjectTimeoutNow(port, slot);
}
}
if (EmuFolders::Textures != old_textures_directory)
{
GetMTGS().RunOnGSThread([]() {
if (VMManager::HasValidVM())
GSTextureReplacements::ReloadReplacementMap();
});
}
}
}

View File

@ -79,5 +79,8 @@ namespace Host
/// Sets the input profile settings layer. Called by VMManager when the game changes.
void SetInputSettingsLayer(SettingsInterface* sif);
/// Updates the variables in the EmuFolders namespace, reloading subsystems if needed. Must call with the lock held.
void UpdateEmuFolders();
} // namespace Internal
} // namespace Host

View File

@ -242,7 +242,7 @@ void SysMtgsThread::PostVsyncStart(bool registers_written)
void SysMtgsThread::InitAndReadFIFO(u8* mem, u32 qwc)
{
if (GSConfig.HWDisableReadbacks && GSConfig.UseHardwareRenderer())
if (EmuConfig.GS.HWDisableReadbacks && GSConfig.UseHardwareRenderer())
{
GSReadLocalMemoryUnsync(mem, qwc, vif1.BITBLTBUF._u64, vif1.TRXPOS._u64, vif1.TRXREG._u64);
return;
@ -916,6 +916,13 @@ void SysMtgsThread::ApplySettings()
RunOnGSThread([opts = EmuConfig.GS]() {
GSUpdateConfig(opts);
});
// We need to synchronize the thread when changing any settings when the download mode
// is unsynchronized, because otherwise we might potentially read in the middle of
// the GS renderer being reopened.
if (EmuConfig.GS.HWDisableReadbacks)
WaitGS(false, false, false);
}
void SysMtgsThread::ResizeDisplayWindow(int width, int height, float scale)
@ -960,6 +967,10 @@ void SysMtgsThread::SwitchRenderer(GSRendererType renderer, bool display_message
RunOnGSThread([renderer]() {
GSSwitchRenderer(renderer);
});
// See note in ApplySettings() for reasoning here.
if (EmuConfig.GS.HWDisableReadbacks)
WaitGS(false, false, false);
}
void SysMtgsThread::SetSoftwareRendering(bool software, bool display_message /* = true */)

View File

@ -28,7 +28,8 @@ KeyStatus::KeyStatus()
for (u32 pad = 0; pad < NUM_CONTROLLER_PORTS; pad++)
{
m_axis_scale[pad] = 1.0f;
m_axis_scale[pad][0] = 0.0f;
m_axis_scale[pad][1] = 1.0f;
}
}
@ -50,10 +51,10 @@ void KeyStatus::Init()
void KeyStatus::Set(u32 pad, u32 index, float value)
{
m_button_pressure[pad][index] = static_cast<u8>(std::clamp(value * m_axis_scale[pad] * 255.0f, 0.0f, 255.0f));
if (IsAnalogKey(index))
{
m_button_pressure[pad][index] = static_cast<u8>(std::clamp(((value < m_axis_scale[pad][0]) ? 0.0f : value) * m_axis_scale[pad][1] * 255.0f, 0.0f, 255.0f));
// Left -> -- -> Right
// Value range : FFFF8002 -> 0 -> 7FFE
// Force range : 80 -> 0 -> 7F
@ -94,8 +95,10 @@ void KeyStatus::Set(u32 pad, u32 index, float value)
}
else
{
m_button_pressure[pad][index] = static_cast<u8>(std::clamp(value * 255.0f, 0.0f, 255.0f));
// Since we reordered the buttons for better UI, we need to remap them here.
static constexpr std::array<u8, MAX_KEYS> bitmask_mapping = { {
static constexpr std::array<u8, MAX_KEYS> bitmask_mapping = {{
12, // PAD_UP
13, // PAD_RIGHT
14, // PAD_DOWN
@ -114,7 +117,7 @@ void KeyStatus::Set(u32 pad, u32 index, float value)
10, // PAD_R3
16, // Analog
// remainder are analogs and not used here
} };
}};
// TODO: Deadzone here?
if (value > 0.0f)

View File

@ -36,7 +36,7 @@ namespace PAD
u32 m_button[NUM_CONTROLLER_PORTS];
u8 m_button_pressure[NUM_CONTROLLER_PORTS][MAX_KEYS];
PADAnalog m_analog[NUM_CONTROLLER_PORTS];
float m_axis_scale[NUM_CONTROLLER_PORTS];
float m_axis_scale[NUM_CONTROLLER_PORTS][2];
float m_vibration_scale[NUM_CONTROLLER_PORTS][2];
public:
@ -48,7 +48,11 @@ namespace PAD
__fi PAD::ControllerType GetType(u32 pad) { return m_type[pad]; }
__fi void SetType(u32 pad, PAD::ControllerType type) { m_type[pad] = type; }
__fi void SetAxisScale(u32 pad, float scale) { m_axis_scale[pad] = scale; }
__fi void SetAxisScale(u32 pad, float deadzone, float scale)
{
m_axis_scale[pad][0] = deadzone;
m_axis_scale[pad][1] = scale;
}
__fi float GetVibrationScale(u32 pad, u32 motor) const { return m_vibration_scale[pad][motor]; }
__fi void SetVibrationScale(u32 pad, u32 motor, float scale) { m_vibration_scale[pad][motor] = scale; }

View File

@ -47,6 +47,7 @@ namespace PAD
bool trigger_state; ///< Whether the macro button is active.
};
static std::string GetConfigSection(u32 pad_index);
static void LoadMacroButtonConfig(const SettingsInterface& si, u32 pad, const std::string_view& type, const std::string& section);
static void ApplyMacroButton(u32 pad, const MacroButton& mb);
static void UpdateMacroButtons();
@ -182,6 +183,11 @@ u8 PADpoll(u8 value)
return pad_poll(value);
}
std::string PAD::GetConfigSection(u32 pad_index)
{
return fmt::format("Pad{}", pad_index + 1);
}
void PAD::LoadConfig(const SettingsInterface& si)
{
PAD::s_macro_buttons = {};
@ -192,7 +198,7 @@ void PAD::LoadConfig(const SettingsInterface& si)
// This is where we would load controller types, if onepad supported them.
for (u32 i = 0; i < NUM_CONTROLLER_PORTS; i++)
{
const std::string section(StringUtil::StdStringFromFormat("Pad%u", i + 1u));
const std::string section(GetConfigSection(i));
const std::string type(si.GetStringValue(section.c_str(), "Type", GetDefaultPadType(i)));
const ControllerInfo* ci = GetControllerInfo(type);
@ -204,13 +210,14 @@ void PAD::LoadConfig(const SettingsInterface& si)
g_key_status.SetType(i, ci->type);
const float axis_scale = si.GetFloatValue(section.c_str(), "AxisScale", 1.0f);
g_key_status.SetAxisScale(i, axis_scale);
const float axis_deadzone = si.GetFloatValue(section.c_str(), "Deadzone", DEFAULT_STICK_DEADZONE);
const float axis_scale = si.GetFloatValue(section.c_str(), "AxisScale", DEFAULT_STICK_SCALE);
g_key_status.SetAxisScale(i, axis_deadzone, axis_scale);
if (ci->vibration_caps != VibrationCapabilities::NoVibration)
{
const float large_motor_scale = si.GetFloatValue(section.c_str(), "LargeMotorScale", 1.0f);
const float small_motor_scale = si.GetFloatValue(section.c_str(), "SmallMotorScale", 1.0f);
const float large_motor_scale = si.GetFloatValue(section.c_str(), "LargeMotorScale", DEFAULT_MOTOR_SCALE);
const float small_motor_scale = si.GetFloatValue(section.c_str(), "SmallMotorScale", DEFAULT_MOTOR_SCALE);
g_key_status.SetVibrationScale(i, 0, large_motor_scale);
g_key_status.SetVibrationScale(i, 1, small_motor_scale);
}
@ -227,22 +234,35 @@ const char* PAD::GetDefaultPadType(u32 pad)
void PAD::SetDefaultConfig(SettingsInterface& si)
{
si.ClearSection("InputSources");
for (u32 i = 0; i < NUM_CONTROLLER_PORTS; i++)
si.ClearSection(StringUtil::StdStringFromFormat("Pad%u", i + 1).c_str());
si.ClearSection("Hotkeys");
si.ClearSection("Pad");
// PCSX2 Controller Settings - Global Settings
si.SetBoolValue("InputSources", "SDL", true);
si.SetBoolValue("InputSources", "SDLControllerEnhancedMode", false);
si.SetBoolValue("InputSources", "XInput", false);
si.SetBoolValue("InputSources", "RawInput", false);
si.SetBoolValue("Pad", "MultitapPort1", false);
si.SetBoolValue("Pad", "MultitapPort2", false);
si.SetFloatValue("Pad", "PointerXScale", 8.0f);
si.SetFloatValue("Pad", "PointerYScale", 8.0f);
si.SetBoolValue("Pad", "PointerXInvert", false);
si.SetBoolValue("Pad", "PointerYInvert", false);
// PCSX2 Controller Settings - Default pad types and parameters.
for (u32 i = 0; i < NUM_CONTROLLER_PORTS; i++)
{
const std::string section(GetConfigSection(i));
si.ClearSection(section.c_str());
si.SetStringValue(section.c_str(), "Type", GetDefaultPadType(i));
si.SetFloatValue(section.c_str(), "Deadzone", DEFAULT_STICK_DEADZONE);
si.SetFloatValue(section.c_str(), "AxisScale", DEFAULT_STICK_SCALE);
si.SetFloatValue(section.c_str(), "LargeMotorScale", DEFAULT_MOTOR_SCALE);
si.SetFloatValue(section.c_str(), "SmallMotorScale", DEFAULT_MOTOR_SCALE);
}
// PCSX2 Controller Settings - Controller 1 / Controller 2 / ...
// Use the automapper to set this up.
si.SetStringValue("Pad1", "Type", GetDefaultPadType(0));
MapController(si, 0, InputManager::GetGenericBindingMapping("Keyboard"));
// PCSX2 Controller Settings - Hotkeys
@ -286,6 +306,7 @@ void PAD::SetDefaultConfig(SettingsInterface& si)
si.SetStringValue("Hotkeys", "TogglePause", "Keyboard/Space");
si.SetStringValue("Hotkeys", "ToggleSlowMotion", "Keyboard/Shift & Keyboard/Backtab");
si.SetStringValue("Hotkeys", "ToggleTurbo", "Keyboard/Tab");
si.SetStringValue("Hotkeys", "HoldTurbo", "Keyboard/Period");
}
void PAD::Update()

View File

@ -87,6 +87,11 @@ namespace PAD
/// Number of macro buttons per controller.
static constexpr u32 NUM_MACRO_BUTTONS_PER_CONTROLLER = 4;
/// Default stick deadzone/sensitivity.
static constexpr float DEFAULT_STICK_DEADZONE = 0.0f;
static constexpr float DEFAULT_STICK_SCALE = 1.33f;
static constexpr float DEFAULT_MOTOR_SCALE = 1.0f;
/// Returns the default type for the specified port.
const char* GetDefaultPadType(u32 pad);

View File

@ -120,6 +120,9 @@ struct PadFullFreezeData
u8 slot[2];
PadFreezeData padData[2][4];
QueryInfo query;
// unused padding data, present only so that data size
// matches Windows version for cross-platform save/load
char padding[8];
};
extern QueryInfo query;

View File

@ -300,6 +300,7 @@ Pcsx2Config::GSOptions::GSOptions()
{
bitset = 0;
PCRTCAntiBlur = true;
DisableInterlaceOffset = false;
PCRTCOffsets = false;
PCRTCOverscan = false;
@ -510,6 +511,7 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
// Unfortunately, because code in the GS still reads the setting by key instead of
// using these variables, we need to use the old names. Maybe post 2.0 we can change this.
GSSettingBoolEx(PCRTCAntiBlur, "pcrtc_antiblur");
GSSettingBoolEx(DisableInterlaceOffset, "disable_interlace_offset");
GSSettingBoolEx(PCRTCOffsets, "pcrtc_offsets");
GSSettingBoolEx(PCRTCOverscan, "pcrtc_overscan");

View File

@ -129,10 +129,12 @@ static std::vector<u8> s_widescreen_cheats_data;
static bool s_widescreen_cheats_loaded = false;
static std::vector<u8> s_no_interlacing_cheats_data;
static bool s_no_interlacing_cheats_loaded = false;
static s32 s_active_widescreen_patches = 0;
static u32 s_active_no_interlacing_patches = 0;
static s32 s_current_save_slot = 1;
static u32 s_frame_advance_count = 0;
static u32 s_mxcsr_saved;
static std::optional<LimiterModeType> s_limiter_mode_prior_to_hold_interaction;
bool VMManager::PerformEarlyHardwareChecks(const char** error)
{
@ -315,6 +317,16 @@ void VMManager::LoadSettings()
if (s_active_no_interlacing_patches > 0 && EmuConfig.GS.InterlaceMode == GSInterlaceMode::Automatic)
EmuConfig.GS.InterlaceMode = GSInterlaceMode::Off;
// Switch to 16:9 if widescreen patches are enabled, and AR is auto.
if (s_active_widescreen_patches > 0 && EmuConfig.GS.AspectRatio == AspectRatioType::RAuto4_3_3_2)
{
// Don't change when reloading settings in the middle of a FMV with switch.
if (EmuConfig.CurrentAspectRatio == EmuConfig.GS.AspectRatio)
EmuConfig.CurrentAspectRatio = AspectRatioType::R16_9;
EmuConfig.GS.AspectRatio = AspectRatioType::R16_9;
}
// Force MTVU off when playing back GS dumps, it doesn't get used.
if (GSDumpReplayer::IsReplayingDump())
EmuConfig.Speedhacks.vuThread = false;
@ -468,6 +480,8 @@ void VMManager::LoadPatches(const std::string& serial, u32 crc, bool show_messag
{
const std::string crc_string(fmt::format("{:08X}", crc));
s_patches_crc = crc;
s_active_widescreen_patches = 0;
s_active_no_interlacing_patches = 0;
ForgetLoadedPatches();
std::string message;
@ -497,10 +511,9 @@ void VMManager::LoadPatches(const std::string& serial, u32 crc, bool show_messag
}
// wide screen patches
int ws_patch_count = 0;
if (EmuConfig.EnableWideScreenPatches && crc != 0)
{
if (ws_patch_count = LoadPatchesFromDir(crc_string, EmuFolders::CheatsWS, "Widescreen hacks", false))
if (s_active_widescreen_patches = LoadPatchesFromDir(crc_string, EmuFolders::CheatsWS, "Widescreen hacks", false))
{
Console.WriteLn(Color_Gray, "Found widescreen patches in the cheats_ws folder --> skipping cheats_ws.zip");
}
@ -518,13 +531,25 @@ void VMManager::LoadPatches(const std::string& serial, u32 crc, bool show_messag
if (!s_widescreen_cheats_data.empty())
{
ws_patch_count = LoadPatchesFromZip(crc_string, s_widescreen_cheats_data.data(), s_widescreen_cheats_data.size());
PatchesCon->WriteLn(Color_Green, "(Wide Screen Cheats DB) Patches Loaded: %d", ws_patch_count);
s_active_widescreen_patches = LoadPatchesFromZip(crc_string, s_widescreen_cheats_data.data(), s_widescreen_cheats_data.size());
PatchesCon->WriteLn(Color_Green, "(Wide Screen Cheats DB) Patches Loaded: %d", s_active_widescreen_patches);
}
}
if (ws_patch_count > 0)
fmt::format_to(std::back_inserter(message), "{}{} widescreen patches", (patch_count > 0 || cheat_count > 0) ? " and " : "", ws_patch_count);
if (s_active_widescreen_patches > 0)
{
fmt::format_to(std::back_inserter(message), "{}{} widescreen patches", (patch_count > 0 || cheat_count > 0) ? " and " : "", s_active_widescreen_patches);
// Switch to 16:9 if widescreen patches are enabled, and AR is auto.
if (EmuConfig.GS.AspectRatio == AspectRatioType::RAuto4_3_3_2)
{
// Don't change when reloading settings in the middle of a FMV with switch.
if (EmuConfig.CurrentAspectRatio == EmuConfig.GS.AspectRatio)
EmuConfig.CurrentAspectRatio = AspectRatioType::R16_9;
EmuConfig.GS.AspectRatio = AspectRatioType::R16_9;
}
}
}
// no-interlacing patches
@ -555,7 +580,7 @@ void VMManager::LoadPatches(const std::string& serial, u32 crc, bool show_messag
if (s_active_no_interlacing_patches > 0)
{
fmt::format_to(std::back_inserter(message), "{}{} no-interlacing patches", (patch_count > 0 || cheat_count > 0 || ws_patch_count > 0) ? " and " : "", s_active_no_interlacing_patches);
fmt::format_to(std::back_inserter(message), "{}{} no-interlacing patches", (patch_count > 0 || cheat_count > 0 || s_active_widescreen_patches > 0) ? " and " : "", s_active_no_interlacing_patches);
// Disable interlacing in GS if active.
if (EmuConfig.GS.InterlaceMode == GSInterlaceMode::Automatic)
@ -572,7 +597,7 @@ void VMManager::LoadPatches(const std::string& serial, u32 crc, bool show_messag
if (show_messages)
{
if (cheat_count > 0 || ws_patch_count > 0 || s_active_no_interlacing_patches > 0)
if (cheat_count > 0 || s_active_widescreen_patches > 0 || s_active_no_interlacing_patches > 0)
{
message += " are active.";
Host::AddKeyedOSDMessage("LoadPatches", std::move(message), 5.0f);
@ -979,7 +1004,10 @@ void VMManager::Shutdown(bool save_resume_state)
Host::OnGameChanged(s_disc_path, s_game_serial, s_game_name, 0);
}
s_active_game_fixes = 0;
s_active_widescreen_patches = 0;
s_active_no_interlacing_patches = 0;
s_limiter_mode_prior_to_hold_interaction.reset();
UpdateGameSettingsLayer();
std::string().swap(s_elf_override);
@ -1017,7 +1045,9 @@ void VMManager::Reset()
const bool game_was_started = g_GameStarted;
s_active_game_fixes = 0;
s_active_widescreen_patches = 0;
s_active_no_interlacing_patches = 0;
s_limiter_mode_prior_to_hold_interaction.reset();
SysClearExecutionCache();
memBindConditionalHandlers();
@ -1654,6 +1684,23 @@ static void HotkeyCycleSaveSlot(s32 delta)
}
}
static void HotkeyLoadStateSlot(s32 slot)
{
if (s_game_crc == 0)
{
Host::AddKeyedOSDMessage("LoadStateFromSlot", "Cannot load state without a game running.", 10.0f);
return;
}
if (!VMManager::HasSaveStateInSlot(s_game_serial.c_str(), s_game_crc, slot))
{
Host::AddKeyedOSDMessage("LoadStateFromSlot", fmt::format("No save state found in slot {}.", slot));
return;
}
VMManager::LoadStateFromSlot(slot);
}
BEGIN_HOTKEY_LIST(g_vm_manager_hotkeys)
DEFINE_HOTKEY("ToggleFrameLimit", "System", "Toggle Frame Limit", [](bool pressed) {
if (!pressed)
@ -1664,12 +1711,18 @@ DEFINE_HOTKEY("ToggleFrameLimit", "System", "Toggle Frame Limit", [](bool presse
}
})
DEFINE_HOTKEY("ToggleTurbo", "System", "Toggle Turbo", [](bool pressed) {
if (!pressed)
if (pressed && !s_limiter_mode_prior_to_hold_interaction.has_value())
{
VMManager::SetLimiterMode((EmuConfig.LimiterMode != LimiterModeType::Turbo) ?
s_limiter_mode_prior_to_hold_interaction = VMManager::GetLimiterMode();
VMManager::SetLimiterMode((s_limiter_mode_prior_to_hold_interaction.value() != LimiterModeType::Turbo) ?
LimiterModeType::Turbo :
LimiterModeType::Nominal);
}
else if (!pressed && s_limiter_mode_prior_to_hold_interaction.has_value())
{
VMManager::SetLimiterMode(s_limiter_mode_prior_to_hold_interaction.value());
s_limiter_mode_prior_to_hold_interaction.reset();
}
})
DEFINE_HOTKEY("ToggleSlowMotion", "System", "Toggle Slow Motion", [](bool pressed) {
if (!pressed)
@ -1710,7 +1763,7 @@ DEFINE_HOTKEY("SaveStateToSlot", "Save States", "Save State To Selected Slot", [
})
DEFINE_HOTKEY("LoadStateFromSlot", "Save States", "Load State From Selected Slot", [](bool pressed) {
if (!pressed)
VMManager::LoadStateFromSlot(s_current_save_slot);
HotkeyLoadStateSlot(s_current_save_slot);
})
#define DEFINE_HOTKEY_SAVESTATE_X(slotnum,slotnumstr) DEFINE_HOTKEY("SaveStateToSlot" #slotnum, \
@ -1728,7 +1781,7 @@ DEFINE_HOTKEY_SAVESTATE_X(10, 10)
#define DEFINE_HOTKEY_LOADSTATE_X(slotnum, slotnumstr) DEFINE_HOTKEY("LoadStateFromSlot" #slotnum, \
"Save States", "Load State From Slot " #slotnumstr , [](bool pressed) { \
if (!pressed) \
VMManager::LoadStateFromSlot(slotnum); \
HotkeyLoadStateSlot(slotnum); \
})
DEFINE_HOTKEY_LOADSTATE_X(1, 01)
DEFINE_HOTKEY_LOADSTATE_X(2, 02)

View File

@ -121,7 +121,6 @@ HostDisplay* Host::AcquireHostDisplay(HostDisplay::RenderAPI api)
!s_host_display->InitializeRenderDevice(EmuFolders::Cache, GSConfig.UseDebugDevice) ||
!ImGuiManager::Initialize())
{
s_host_display->DestroyRenderDevice();
s_host_display.reset();
return nullptr;
}
@ -137,10 +136,7 @@ void Host::ReleaseHostDisplay()
ImGuiManager::Shutdown();
if (s_host_display)
{
s_host_display->DestroyRenderDevice();
s_host_display.reset();
}
sApp.CloseGsPanel();
}

View File

@ -265,7 +265,6 @@ namespace Panels
pxCheckBox* m_check_HideMouse;
pxCheckBox* m_check_DclickFullscreen;
pxCheckBox* m_check_SyncToHostRefreshRate;
wxTextCtrl* m_text_WindowWidth;
wxTextCtrl* m_text_WindowHeight;

View File

@ -74,7 +74,6 @@ Panels::GSWindowSettingsPanel::GSWindowSettingsPanel(wxWindow* parent)
// Implement custom hotkeys (Alt + Enter) with translatable string intact + not blank in GUI.
m_check_Fullscreen = new pxCheckBox(this, _("Start in fullscreen mode by default") + wxString(" (") + wxGetApp().GlobalAccels->findKeycodeWithCommandId("FullscreenToggle").toTitleizedString() + wxString(")"));
m_check_DclickFullscreen = new pxCheckBox(this, _("Double-click toggles fullscreen mode"));
m_check_SyncToHostRefreshRate = new pxCheckBox(this, _("Sync To Host Refresh Rate"));
m_combo_FMVAspectRatioSwitch->SetToolTip(pxEt(L"Off: Disables temporary aspect ratio switch. (It will use the above setting from Aspect Ratio instead of FMV Aspect Ratio Override.)\n\n"
L"Auto 4:3/3:2: Temporarily switch to a 4:3 aspect ratio while an FMV plays to correctly display a 4:3 FMV. Will use 3:2 is the resolution is 480P\n\n"
@ -137,7 +136,6 @@ Panels::GSWindowSettingsPanel::GSWindowSettingsPanel(wxWindow* parent)
*this += m_check_Fullscreen;
*this += m_check_DclickFullscreen;
*this += m_check_SyncToHostRefreshRate;
*this += new wxStaticLine(this) | StdExpand();
*this += s_vsync | StdExpand();
@ -171,7 +169,6 @@ void Panels::GSWindowSettingsPanel::ApplyConfigToGui(AppConfig& configToApply, i
m_text_Zoom->ChangeValue(wxString::FromDouble(gsconf.Zoom, 2));
m_check_DclickFullscreen->SetValue(conf.IsToggleFullscreenOnDoubleClick);
m_check_SyncToHostRefreshRate->SetValue(gsconf.SyncToHostRefreshRate);
m_text_WindowWidth->ChangeValue(wxsFormat(L"%d", conf.WindowSize.GetWidth()));
m_text_WindowHeight->ChangeValue(wxsFormat(L"%d", conf.WindowSize.GetHeight()));
@ -202,7 +199,6 @@ void Panels::GSWindowSettingsPanel::Apply()
gsconf.VsyncEnable = static_cast<VsyncMode>(m_combo_vsync->GetSelection());
appconf.IsToggleFullscreenOnDoubleClick = m_check_DclickFullscreen->GetValue();
gsconf.SyncToHostRefreshRate = m_check_SyncToHostRefreshRate->GetValue();
long xr, yr = 1;

View File

@ -198,6 +198,7 @@
<ClCompile Include="Frontend\InputManager.cpp" />
<ClCompile Include="Frontend\InputSource.cpp" />
<ClCompile Include="Frontend\LayeredSettingsInterface.cpp" />
<ClCompile Include="Frontend\LogSink.cpp" />
<ClCompile Include="Frontend\OpenGLHostDisplay.cpp" />
<ClCompile Include="Frontend\SDLInputSource.cpp" />
<ClCompile Include="Frontend\VulkanHostDisplay.cpp" />
@ -517,6 +518,7 @@
<ClInclude Include="Frontend\InputManager.h" />
<ClInclude Include="Frontend\InputSource.h" />
<ClInclude Include="Frontend\LayeredSettingsInterface.h" />
<ClInclude Include="Frontend\LogSink.h" />
<ClInclude Include="Frontend\OpenGLHostDisplay.h" />
<ClInclude Include="Frontend\SDLInputSource.h" />
<ClInclude Include="Frontend\VulkanHostDisplay.h" />
@ -812,4 +814,4 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
</Project>

View File

@ -1272,6 +1272,9 @@
<ClCompile Include="Recording\PadData.cpp">
<Filter>Tools\Input Recording</Filter>
</ClCompile>
<ClCompile Include="Frontend\LogSink.cpp">
<Filter>Host</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Patch.h">
@ -2113,6 +2116,9 @@
<ClInclude Include="Recording\PadData.h">
<Filter>Tools\Input Recording</Filter>
</ClInclude>
<ClInclude Include="Frontend\LogSink.h">
<Filter>Host</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuildStep Include="rdebug\deci2.h">
@ -2137,4 +2143,4 @@
<Filter>System\Ps2\Debug\rdebug</Filter>
</CustomBuildStep>
</ItemGroup>
</Project>
</Project>

View File

@ -238,7 +238,9 @@ bool Updater::ParseZip()
if (!entry.destination_filename.empty() && entry.destination_filename.back() != FS_OSPATH_SEPARATOR_CHARACTER)
{
// skip updater itself, since it was already pre-extracted.
if (StringUtil::Strcasecmp(entry.destination_filename.c_str(), "updater.exe") != 0)
// also skips portable.ini to not mess with future non-portable installs.
if (StringUtil::Strcasecmp(entry.destination_filename.c_str(), "updater.exe") != 0 &&
StringUtil::Strcasecmp(entry.destination_filename.c_str(), "portable.ini") != 0)
{
m_progress->DisplayFormattedInformation("Found file in zip: '%s'", entry.destination_filename.c_str());
m_update_paths.push_back(std::move(entry));