Compare commits

..

4 Commits

Author SHA1 Message Date
AsperTheDog
c0078088dd Add shader non-uniform indexing support (#91)
This PR marks ALL texture indexes as nonuniform to fix an issue with the paths in Tomodachi Life: Living the Dream on AMD cards. It should have a negligible impact on performance (and it should not have an impact at all on NVIDIA cards!)

It's caused by what is called 'implicit non-uniform sampler array indexing'. The idea is basically that some GPUs optimize texture lookups from indexed texture arrays, by assuming that you are never going to index different textures within a single workgroup. What this causes is that visual glitch where a subgroup is tasked with rendering a block of the screen, and in the boundaries some cores are indexing the wrong texture.

Co-authored-by: AsperTheDog <guillerman0000@gmail.com>
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/91
2026-05-14 08:59:10 +00:00
Max
dcac29680a Updated PlayReports for more titles (#93)
- Echoes of Wisdom (Warps)
- Super Mario Odyssey (Kingdoms)
- Super Mario Bros. Wonder (World & Course)
- Pokemon Scarlet/Violet (DLC & Accademy Rooms)
- Super Mario 3D All Stars (Game Selection) (Berry is working on track showcase)

Co-authored-by: berrydiaboli <anthonyhoffman54444@gmail.com>
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/93
2026-05-14 04:06:28 +00:00
Max
ec07e51807 Update compatibility.csv (#94)
- Emio – The Smiling Man: Famicom Detective Club (DEMO)
- GROOVE COASTER WAI WAI PARTY!!!!
- Metroid Prime 4: Beyond
- Mute Crimson DX
- Pokémon Champions
- Pokémon FireRed Version
- Pokémon LeafGreen Version
- Tomodachi Life: Living the Dream
- Tomodachi Life: Living the Dream – Welcome Edtion

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/94
2026-05-14 04:05:58 +00:00
cookieso
e7aa6775af Input: Implement HD Rumble for compatible Nin devices (#40)
This PR addresses [this issue](https://github.com/Ryubing/Issues/issues/231) and implements HD Rumble for compatible Nin devices.
## New Features
- Add the option for Gamepads to implement HD Rumble
- Add the HD rumble capability to SDL3Gamepad and SDL3JoyCon when they meet certain requirements

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/40
2026-05-13 23:57:30 +00:00
20 changed files with 800 additions and 42 deletions

View File

@@ -1061,6 +1061,7 @@
0100BCA016636000,"eBaseball Powerful Pro Yakyuu 2022",gpu;services-horizon;crash,nothing,2024-05-26 23:07:19
01001F20100B8000,"Eclipse: Edge of Light",,playable,2020-08-11 23:06:29
0100E0A0110F4000,"eCrossminton",,playable,2020-07-11 18:24:27
010054601D54C000,"Emio The Smiling Man: Famicom Detective Club (DEMO)",demo,playable,2026-05-13 18:32:12
0100ABE00DB4E000,"Edna & Harvey: Harvey's New Eyes",nvdec,playable,2021-01-26 14:36:08
01004F000B716000,"Edna & Harvey: The Breakout Anniversary Edition",crash;nvdec,ingame,2022-08-01 16:59:56
01002550129F0000,"Effie",,playable,2022-10-27 14:36:39
@@ -1204,7 +1205,7 @@
01003B200E440000,"Five Nights at Freddy's: Sister Location",,playable,2023-10-06 09:00:58
010038200E088000,"Flan",crash;regression,ingame,2021-11-17 07:39:28
01000A0004C50000,"FLASHBACK™",nvdec,playable,2020-05-14 13:57:29
0100C53004C52000,"Flat Heroes",gpu,ingame,2022-07-26 19:37:37
0100C53004C52000,"Flat Heroes",,playable,2026-02-27 17:00:00
0100B54012798000,"Flatland: Prologue",,playable,2020-12-11 20:41:12
0100307004B4C000,"Flinthook",online,playable,2021-03-25 20:42:29
010095A004040000,"Flip Wars",services;ldn-untested,ingame,2022-05-02 15:39:18
@@ -1394,6 +1395,7 @@
0100c3c012718000,"Grand Theft Auto: III The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
0100182014022000,"Grand Theft Auto: Vice City The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
010065a014024000,"Grand Theft Auto: San Andreas The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
0100EB500D92E000,"GROOVE COASTER WAI WAI PARTY!!!!",gpu,ingame,2026-05-13 18:32:12
0100822012D76000,"HAAK",gpu,ingame,2023-02-19 14:31:05
01007E100EFA8000,"Habroxia",,playable,2020-06-16 23:04:42
0100535012974000,"Hades",vulkan,playable,2022-10-05 10:45:21
@@ -1832,6 +1834,7 @@
010055200E87E000,"Metamorphosis",UE4;audout;gpu;nvdec,ingame,2021-06-16 16:18:11
0100D4900E82C000,"Metro 2033 Redux",gpu,ingame,2022-11-09 10:53:13
0100F0400E850000,"Metro: Last Light Redux",slow;nvdec;vulkan-backend-bug,ingame,2023-11-01 11:53:52
010019A01E2F2000,"Metroid Prime 4: Beyond",,ingame,2026-05-13 18:32:12
010012101468C000,"Metroid Prime™ Remastered",gpu;Needs Update;vulkan-backend-bug;opengl-backend-bug,ingame,2024-05-07 22:48:15
010093801237C000,"Metroid™ Dread",,playable,2023-11-13 04:02:36
0100A1200F20C000,"Midnight Evil",,playable,2022-10-18 22:55:19
@@ -1945,6 +1948,7 @@
0100C3E00ACAA000,"Mutant Football League: Dynasty Edition",online-broken,playable,2022-08-05 17:01:51
01004BE004A86000,"Mutant Mudds Collection",,playable,2022-08-05 17:11:38
0100E6B00DEA4000,"Mutant Year Zero: Road to Eden - Deluxe Edition",nvdec;UE4,playable,2022-09-10 13:31:10
010037501F864000,"Mute Crimson DX",,ingame,2026-05-13 18:32:12
0100161009E5C000,"MX Nitro: Unleashed",,playable,2022-09-27 22:34:33
0100218011E7E000,"MX vs ATV All Out",nvdec;UE4;vulkan-backend-bug,playable,2022-10-25 19:51:46
0100D940063A0000,"MXGP3 - The Official Motocross Videogame",UE4;gpu;nvdec,ingame,2020-12-16 14:00:20
@@ -2268,6 +2272,7 @@
010086F0064CE000,"Poi: Explorer Edition",nvdec,playable,2021-01-21 19:32:00
0100EB6012FD2000,"Poison Control",,playable,2021-05-16 14:01:54
010072400E04A000,"Pokémon Café ReMix",,playable,2021-08-17 20:00:04
01005B7008C52800,"Pokémon Champions",Needs Update;services;online-broke,menus,2026-05-13 18:32:12
010008c01e742000,"Pokémon Friends",crash;services,menus,2025-07-24 13:32:00
01003D200BAA2000,"Pokémon Mystery Dungeon™: Rescue Team DX",mac-bug,playable,2024-01-21 00:16:32
01008DB008C2C000,"Pokémon Shield + Pokémon Shield Expansion Pass",deadlock;crash;online-broken;ldn-works;LAN,ingame,2024-08-12 07:20:22
@@ -2275,6 +2280,8 @@
01009AD008C4C000,"Pokémon: Let's Go, Pikachu! demo",slow;demo,playable,2023-11-26 11:23:20
0100000011D90000,"Pokémon™ Brilliant Diamond",gpu;ldn-works,ingame,2024-08-28 13:26:35
010018E011D92000,"Pokémon™ Shining Pearl",gpu;ldn-works,ingame,2024-08-28 13:26:35
100554023408000,"Pokémon FireRed Version",crashes,nothing,2026-05-13 18:32:12
010034D02340E000,"Pokémon LeafGreen Version",crashes,nothing,2026-05-13 18:32:12
010015F008C54000,"Pokémon™ HOME",Needs Update;crash;services,menus,2020-12-06 06:01:51
01001F5010DFA000,"Pokémon™ Legends: Arceus",gpu;Needs Update;ldn-works,ingame,2024-09-19 10:02:02
0100F43008C44000,"Pokémon™ Legends: Z-A",gpu;crash;ldn-works,ingame,2025-11-16 00:30:00
@@ -2866,7 +2873,7 @@
0100277011F1A000,"Super Mario Bros.™ 35",online-broken,menus,2022-08-07 16:27:25
010015100B514000,"Super Mario Bros.™ Wonder",amd-vendor-bug,playable,2024-09-06 13:21:21
01009B90006DC000,"Super Mario Maker™ 2",online-broken;ldn-broken,playable,2024-08-25 11:05:19
0100000000010000,"Super Mario Odyssey™",nvdec;intel-vendor-bug;mac-bug,playable,2024-08-25 01:32:34
0100000000010000,"Super Mario Odyssey™",nvdec;intel-vendor-bug;mac-bug;amd-vendor-bug,playable,2026-05-13 18:32:12
010036B0034E4000,"Super Mario Party™",gpu;Needs Update;ldn-works,ingame,2024-06-21 05:10:16
0100965017338000,"Super Mario Party Jamboree",mac-bug;gpu,ingame,2025-02-17 02:09:20
0100BC0018138000,"Super Mario RPG™",gpu;audio;nvdec,ingame,2024-06-19 17:43:42
@@ -3163,6 +3170,8 @@
0100E2E00CB14000,"Tokyo School Life",,playable,2022-09-16 20:25:54
010024601BB16000,"Tomb Raider I-III Remastered Starring Lara Croft",gpu;opengl,ingame,2024-09-27 12:32:04
0100D7F01E49C000,"Tomba! Special Edition",services-horizon,nothing,2024-09-15 21:59:54
010051F0207B2000,"Tomodachi Life: Living the Dream",amd-vendor-bug;gpu;intel-vendor-bug;ldn-broken,ingame,2026-05-13 18:32:12
0100CA502552A000,"Tomodachi Life: Living the Dream Welcome Edtion",amd-vendor-bug;demo,playable,2026-05-13 18:32:12
0100D400100F8000,"Tonight We Riot",,playable,2021-02-26 15:55:09
0100CC00102B4000,"Tony Hawk's™ Pro Skater™ 1 + 2",gpu;Needs Update,ingame,2024-09-24 08:18:14
010093F00E818000,"Tools Up!",crash,ingame,2020-07-21 12:58:17
1 title_id game_name labels status last_updated
1061 0100BCA016636000 eBaseball Powerful Pro Yakyuu 2022 gpu;services-horizon;crash nothing 2024-05-26 23:07:19
1062 01001F20100B8000 Eclipse: Edge of Light playable 2020-08-11 23:06:29
1063 0100E0A0110F4000 eCrossminton playable 2020-07-11 18:24:27
1064 010054601D54C000 Emio – The Smiling Man: Famicom Detective Club (DEMO) demo playable 2026-05-13 18:32:12
1065 0100ABE00DB4E000 Edna & Harvey: Harvey's New Eyes nvdec playable 2021-01-26 14:36:08
1066 01004F000B716000 Edna & Harvey: The Breakout – Anniversary Edition crash;nvdec ingame 2022-08-01 16:59:56
1067 01002550129F0000 Effie playable 2022-10-27 14:36:39
1205 01003B200E440000 Five Nights at Freddy's: Sister Location playable 2023-10-06 09:00:58
1206 010038200E088000 Flan crash;regression ingame 2021-11-17 07:39:28
1207 01000A0004C50000 FLASHBACK™ nvdec playable 2020-05-14 13:57:29
1208 0100C53004C52000 Flat Heroes gpu ingame playable 2022-07-26 19:37:37 2026-02-27 17:00:00
1209 0100B54012798000 Flatland: Prologue playable 2020-12-11 20:41:12
1210 0100307004B4C000 Flinthook online playable 2021-03-25 20:42:29
1211 010095A004040000 Flip Wars services;ldn-untested ingame 2022-05-02 15:39:18
1395 0100c3c012718000 Grand Theft Auto: III – The Definitive Edition gpu;UE4 ingame 2022-10-31 20:13:52
1396 0100182014022000 Grand Theft Auto: Vice City – The Definitive Edition gpu;UE4 ingame 2022-10-31 20:13:52
1397 010065a014024000 Grand Theft Auto: San Andreas – The Definitive Edition gpu;UE4 ingame 2022-10-31 20:13:52
1398 0100EB500D92E000 GROOVE COASTER WAI WAI PARTY!!!! gpu ingame 2026-05-13 18:32:12
1399 0100822012D76000 HAAK gpu ingame 2023-02-19 14:31:05
1400 01007E100EFA8000 Habroxia playable 2020-06-16 23:04:42
1401 0100535012974000 Hades vulkan playable 2022-10-05 10:45:21
1834 010055200E87E000 Metamorphosis UE4;audout;gpu;nvdec ingame 2021-06-16 16:18:11
1835 0100D4900E82C000 Metro 2033 Redux gpu ingame 2022-11-09 10:53:13
1836 0100F0400E850000 Metro: Last Light Redux slow;nvdec;vulkan-backend-bug ingame 2023-11-01 11:53:52
1837 010019A01E2F2000 Metroid Prime 4: Beyond ingame 2026-05-13 18:32:12
1838 010012101468C000 Metroid Prime™ Remastered gpu;Needs Update;vulkan-backend-bug;opengl-backend-bug ingame 2024-05-07 22:48:15
1839 010093801237C000 Metroid™ Dread playable 2023-11-13 04:02:36
1840 0100A1200F20C000 Midnight Evil playable 2022-10-18 22:55:19
1948 0100C3E00ACAA000 Mutant Football League: Dynasty Edition online-broken playable 2022-08-05 17:01:51
1949 01004BE004A86000 Mutant Mudds Collection playable 2022-08-05 17:11:38
1950 0100E6B00DEA4000 Mutant Year Zero: Road to Eden - Deluxe Edition nvdec;UE4 playable 2022-09-10 13:31:10
1951 010037501F864000 Mute Crimson DX ingame 2026-05-13 18:32:12
1952 0100161009E5C000 MX Nitro: Unleashed playable 2022-09-27 22:34:33
1953 0100218011E7E000 MX vs ATV All Out nvdec;UE4;vulkan-backend-bug playable 2022-10-25 19:51:46
1954 0100D940063A0000 MXGP3 - The Official Motocross Videogame UE4;gpu;nvdec ingame 2020-12-16 14:00:20
2272 010086F0064CE000 Poi: Explorer Edition nvdec playable 2021-01-21 19:32:00
2273 0100EB6012FD2000 Poison Control playable 2021-05-16 14:01:54
2274 010072400E04A000 Pokémon Café ReMix playable 2021-08-17 20:00:04
2275 01005B7008C52800 Pokémon Champions Needs Update;services;online-broke menus 2026-05-13 18:32:12
2276 010008c01e742000 Pokémon Friends crash;services menus 2025-07-24 13:32:00
2277 01003D200BAA2000 Pokémon Mystery Dungeon™: Rescue Team DX mac-bug playable 2024-01-21 00:16:32
2278 01008DB008C2C000 Pokémon Shield + Pokémon Shield Expansion Pass deadlock;crash;online-broken;ldn-works;LAN ingame 2024-08-12 07:20:22
2280 01009AD008C4C000 Pokémon: Let's Go, Pikachu! demo slow;demo playable 2023-11-26 11:23:20
2281 0100000011D90000 Pokémon™ Brilliant Diamond gpu;ldn-works ingame 2024-08-28 13:26:35
2282 010018E011D92000 Pokémon™ Shining Pearl gpu;ldn-works ingame 2024-08-28 13:26:35
2283 100554023408000 Pokémon FireRed Version crashes nothing 2026-05-13 18:32:12
2284 010034D02340E000 Pokémon LeafGreen Version crashes nothing 2026-05-13 18:32:12
2285 010015F008C54000 Pokémon™ HOME Needs Update;crash;services menus 2020-12-06 06:01:51
2286 01001F5010DFA000 Pokémon™ Legends: Arceus gpu;Needs Update;ldn-works ingame 2024-09-19 10:02:02
2287 0100F43008C44000 Pokémon™ Legends: Z-A gpu;crash;ldn-works ingame 2025-11-16 00:30:00
2873 0100277011F1A000 Super Mario Bros.™ 35 online-broken menus 2022-08-07 16:27:25
2874 010015100B514000 Super Mario Bros.™ Wonder amd-vendor-bug playable 2024-09-06 13:21:21
2875 01009B90006DC000 Super Mario Maker™ 2 online-broken;ldn-broken playable 2024-08-25 11:05:19
2876 0100000000010000 Super Mario Odyssey™ nvdec;intel-vendor-bug;mac-bug nvdec;intel-vendor-bug;mac-bug;amd-vendor-bug playable 2024-08-25 01:32:34 2026-05-13 18:32:12
2877 010036B0034E4000 Super Mario Party™ gpu;Needs Update;ldn-works ingame 2024-06-21 05:10:16
2878 0100965017338000 Super Mario Party Jamboree mac-bug;gpu ingame 2025-02-17 02:09:20
2879 0100BC0018138000 Super Mario RPG™ gpu;audio;nvdec ingame 2024-06-19 17:43:42
3170 0100E2E00CB14000 Tokyo School Life playable 2022-09-16 20:25:54
3171 010024601BB16000 Tomb Raider I-III Remastered Starring Lara Croft gpu;opengl ingame 2024-09-27 12:32:04
3172 0100D7F01E49C000 Tomba! Special Edition services-horizon nothing 2024-09-15 21:59:54
3173 010051F0207B2000 Tomodachi Life: Living the Dream amd-vendor-bug;gpu;intel-vendor-bug;ldn-broken ingame 2026-05-13 18:32:12
3174 0100CA502552A000 Tomodachi Life: Living the Dream – Welcome Edtion amd-vendor-bug;demo playable 2026-05-13 18:32:12
3175 0100D400100F8000 Tonight We Riot playable 2021-02-26 15:55:09
3176 0100CC00102B4000 Tony Hawk's™ Pro Skater™ 1 + 2 gpu;Needs Update ingame 2024-09-24 08:18:14
3177 010093F00E818000 Tools Up! crash ingame 2020-07-21 12:58:17

View File

@@ -59,6 +59,7 @@ namespace Ryujinx.Common
//Mario Franchise
"010021d00812a000", // Arcade Archives VS. SUPER MARIO BROS.
"01007fe0221d8000", // Hello, Mario!
"01006d0017f7a000", // Mario & Luigi: Brothership
"010003000e146000", // Mario & Sonic at the Olympic Games Tokyo 2020
"010067300059a000", // Mario + Rabbids: Kingdom Battle
@@ -70,6 +71,9 @@ namespace Ryujinx.Common
"0100bde00862a000", // Mario Tennis Aces
"0100b99019412000", // Mario vs. Donkey Kong
"010049900f546000", // Super Mario 3D All-Stars
"010049900f546001", // Super Mario 3D All-Stars | Super Mario 64
"010049900f546002", // Super Mario 3D All-Stars | Super Mario Sunshine
"010049900f546003", // Super Mario 3D All-Stars | Super Mario Galaxy
"010028600ebda000", // Super Mario 3D World + Bowser's Fury
"010049900F546001", // Super Mario 64
"0100ea80032ea000", // Super Mario Bros. U Deluxe
@@ -107,6 +111,11 @@ namespace Ryujinx.Common
"0100187003a36000", // Pokémon: Let's Go Eevee!
"010003f003a34000", // Pokémon: Let's Go Pikachu!
"0100f43008c44000", // Pokémon Legends: Z-A
"0100554023408000", // Pokémon FireRed Version (EN)
"01006fa0233f8000", // Pokémon FireRed Version (JP)
"0100fd6023430000", // Pokémon LeafGreen Version (DE)
"0100f1e0233fa000", // Pokémon LeafGreen Version (JP)
"01005b7008c52000", // Pokémon Champions
//Splatoon Franchise
"0100f8f0000a2000", // Splatoon 2 (EU)
@@ -116,13 +125,14 @@ namespace Ryujinx.Common
"0100ba0018500000", // Splatoon 3: Splatfest World Premiere
//NSO Membership games
"0100d870045b6000", // NES - Nintendo Switch Online
"01008d300c50c000", // SNES - Nintendo Switch Online
"0100c62011050000", // GB - Nintendo Switch Online
"010012f017576000", // GBA - Nintendo Switch Online
"0100c9a00ece6000", // N64 - Nintendo Switch Online
"0100e0601c632000", // N64 - Nintendo Switch Online 18+
"0100d870045b6000", // NES - Nintendo Switch Online
"0100b3c014bda000", // SEGA Genesis - Nintendo Switch Online
"01008d300c50c000", // SNES - Nintendo Switch Online
"0100bfc01d976000", // Virtual Boy - Nintendo Switch Online
"0100ccf019c8c000", // F-ZERO 99
"0100ad9012510000", // PAC-MAN 99
"010040600c5ce000", // Tetris 99
@@ -141,12 +151,17 @@ namespace Ryujinx.Common
"0100704000B3A000", // Snipperclips
"01006a800016e000", // Super Smash Bros. Ultimate
"0100a9400c9c2000", // Tokyo Mirage Sessions #FE Encore
"0100ca502552a000", // Tomodachi Life: Living the Dream - Welcome Edition
"010051f0207b2000", // Tomodachi Life: Living the Dream
//Bayonetta Franchise
"010076f0049a2000", // Bayonetta
"01007960049a0000", // Bayonetta 2
"01004a4010fea000", // Bayonetta 3
"0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon
// Famicom Detective Club Franchise
"010054601d54c000", // Emio - The Smiling Man: Famicom Detective Series (DEMO)
//Persona Franchise
"0100dcd01525a000", // Persona 3 Portable
@@ -171,7 +186,9 @@ namespace Ryujinx.Common
"0100453019aa8000", // Xenoblade Chronicles: X Definitive Edition
//Misc Games
"01003670066de000", // 36 Fragments of Midnight
"010056e00853a000", // A Hat in Time
"0100c9f00aaee000", // Ascendence
"0100fd1014726000", // Baldurs Gate: Dark Alliance
"01008c2019598000", // Bluey: The Video Game
"010096f00ff22000", // Borderlands 2: Game of the Year Edition
@@ -185,8 +202,10 @@ namespace Ryujinx.Common
"010027400cdc6000", // Divinity Original 2 - Definitive Edition
"01008c8012920000", // Dying Light Platinum Edition
"0100d11013e6a000", // Eschatos
"01000490067ae000", // Frederic 2: Evil Strikes Back
"01001cc01b2d4000", // Goat Simulator 3
"01003620068ea000", // Hand of Fate 2
"01007ac00e012000", // HEXAGRAVITY
"0100f7e00c70e000", // Hogwarts Legacy
"010013c00e930000", // Hollow Knight: Silksong
"010085500130a000", // Lego City: Undercover
@@ -196,6 +215,7 @@ namespace Ryujinx.Common
"0100853015e86000", // No Man's Sky
"0100f85014ed0000", // No More Heroes
"0100463014ed4000", // No More Heroes 2
"0100f7d00a1bc000", // NO THING
"0100e570094e8000", // Owlboy
"01007bb017812000", // Portal
"0100abd01785c000", // Portal 2
@@ -204,11 +224,14 @@ namespace Ryujinx.Common
"01008e200c5c2000", // Muse Dash
"01005ff002e2a000", // Rayman Legends
"01007820196a6000", // Red Dead Redemption
"01007a800d520000", // REFUNCT
"0100e8300a67a000", // Risk
"01002f7013224000", // Rune Factory 5
"01008d100d43e000", // Saints Row IV
"0100de600beee000", // Saints Row: The Third - The Full Package
"01001180021fa000", // Shovel Knight: Specter of Torment
"010079f00671c000", // Sparkle 2: Evo
"010077b00e046000", // Spyro: Reignited Trilogy
"0100e1D01eb2e000", // Squeakross: Home Squeak Home
"0100e65002bb8000", // Stardew Valley
"0100d7a01b7a2000", // Star Wars: Bounty Hunter

View File

@@ -42,6 +42,7 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsShaderBallot;
public readonly bool SupportsShaderBarrierDivergence;
public readonly bool SupportsShaderFloat64;
public readonly bool SupportsShaderNonUniformIndexing;
public readonly bool SupportsTextureGatherOffsets;
public readonly bool SupportsTextureShadowLod;
public readonly bool SupportsVertexStoreAndAtomics;
@@ -110,6 +111,7 @@ namespace Ryujinx.Graphics.GAL
bool supportsShaderBallot,
bool supportsShaderBarrierDivergence,
bool supportsShaderFloat64,
bool supportsShaderNonUniformIndexing,
bool supportsTextureGatherOffsets,
bool supportsTextureShadowLod,
bool supportsVertexStoreAndAtomics,
@@ -172,6 +174,7 @@ namespace Ryujinx.Graphics.GAL
SupportsShaderBallot = supportsShaderBallot;
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
SupportsShaderFloat64 = supportsShaderFloat64;
SupportsShaderNonUniformIndexing = supportsShaderNonUniformIndexing;
SupportsTextureGatherOffsets = supportsTextureGatherOffsets;
SupportsTextureShadowLod = supportsTextureShadowLod;
SupportsVertexStoreAndAtomics = supportsVertexStoreAndAtomics;

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 7353;
private const uint CodeGenVersion = 7354;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";

View File

@@ -231,6 +231,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
public bool QueryHostSupportsShaderFloat64() => _context.Capabilities.SupportsShaderFloat64;
public bool QueryHostSupportsShaderNonUniformIndexing() => _context.Capabilities.SupportsShaderNonUniformIndexing;
public bool QueryHostSupportsSnormBufferTextureFormat() => _context.Capabilities.SupportsSnormBufferTextureFormat;
public bool QueryHostSupportsTextureGatherOffsets() => _context.Capabilities.SupportsTextureGatherOffsets;

View File

@@ -184,6 +184,7 @@ namespace Ryujinx.Graphics.OpenGL
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
supportsShaderBarrierDivergence: !(intelWindows || intelUnix),
supportsShaderFloat64: true,
supportsShaderNonUniformIndexing: false,
supportsTextureGatherOffsets: true,
supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod,
supportsVertexStoreAndAtomics: true,

View File

@@ -587,6 +587,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return OperationResult.Invalid;
}
private static void MarkNonUniform(CodeGenContext context, SpvInstruction inst)
{
if (context.HostCapabilities.SupportsShaderNonUniformIndexing)
{
context.Decorate(inst, Decoration.NonUniform);
}
}
private static OperationResult GenerateImageAtomic(CodeGenContext context, AstOperation operation)
{
AstTextureOperation texOp = (AstTextureOperation)operation;
@@ -613,6 +621,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
SpvInstruction textureIndex = Src(AggregateType.S32);
image = context.AccessChain(imagePointerType, image, textureIndex);
MarkNonUniform(context, image);
}
int coordsCount = texOp.Type.Dimensions;
@@ -683,15 +692,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
ImageDeclaration declaration = context.Images[texOp.GetTextureSetAndBinding()];
SpvInstruction image = declaration.Image;
bool isIndexed = declaration.IsIndexed;
if (declaration.IsIndexed)
if (isIndexed)
{
SpvInstruction textureIndex = Src(AggregateType.S32);
image = context.AccessChain(declaration.ImagePointerType, image, textureIndex);
MarkNonUniform(context, image);
}
image = context.Load(declaration.ImageType, image);
if (isIndexed)
{
MarkNonUniform(context, image);
}
int coordsCount = texOp.Type.Dimensions;
@@ -740,15 +755,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
ImageDeclaration declaration = context.Images[texOp.GetTextureSetAndBinding()];
SpvInstruction image = declaration.Image;
bool isIndexed = declaration.IsIndexed;
if (declaration.IsIndexed)
if (isIndexed)
{
SpvInstruction textureIndex = Src(AggregateType.S32);
image = context.AccessChain(declaration.ImagePointerType, image, textureIndex);
MarkNonUniform(context, image);
}
image = context.Load(declaration.ImageType, image);
if (isIndexed)
{
MarkNonUniform(context, image);
}
int coordsCount = texOp.Type.Dimensions;
@@ -1878,35 +1899,56 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
private static SpvInstruction GenerateSampledImageLoad(CodeGenContext context, AstTextureOperation texOp, SamplerDeclaration declaration, ref int srcIndex)
{
SpvInstruction image = declaration.Image;
bool imageIndexed = declaration.IsIndexed;
if (declaration.IsIndexed)
if (imageIndexed)
{
SpvInstruction textureIndex = context.Get(AggregateType.S32, texOp.GetSource(srcIndex++));
image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex);
MarkNonUniform(context, image);
}
if (texOp.IsSeparate)
{
image = context.Load(declaration.ImageType, image);
if (imageIndexed)
{
MarkNonUniform(context, image);
}
SamplerDeclaration samplerDeclaration = context.Samplers[texOp.GetSamplerSetAndBinding()];
SpvInstruction sampler = samplerDeclaration.Image;
bool samplerIndexed = samplerDeclaration.IsIndexed;
if (samplerDeclaration.IsIndexed)
if (samplerIndexed)
{
SpvInstruction samplerIndex = context.Get(AggregateType.S32, texOp.GetSource(srcIndex++));
sampler = context.AccessChain(samplerDeclaration.SampledImagePointerType, sampler, samplerIndex);
MarkNonUniform(context, sampler);
}
sampler = context.Load(samplerDeclaration.ImageType, sampler);
if (samplerIndexed)
{
MarkNonUniform(context, sampler);
}
image = context.SampledImage(declaration.SampledImageType, image, sampler);
if (imageIndexed || samplerIndexed)
{
MarkNonUniform(context, image);
}
}
else
{
image = context.Load(declaration.SampledImageType, image);
if (imageIndexed)
{
MarkNonUniform(context, image);
}
}
return image;

View File

@@ -60,6 +60,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.AddCapability(Capability.Float64);
}
if (parameters.HostCapabilities.SupportsShaderNonUniformIndexing)
{
context.AddExtension("SPV_EXT_descriptor_indexing");
context.AddCapability(Capability.ShaderNonUniform);
context.AddCapability(Capability.SampledImageArrayNonUniformIndexing);
context.AddCapability(Capability.StorageImageArrayNonUniformIndexing);
}
if (parameters.Definitions.TransformFeedbackEnabled && parameters.Definitions.LastInVertexPipeline)
{
context.AddCapability(Capability.TransformFeedback);

View File

@@ -336,6 +336,10 @@ namespace Ryujinx.Graphics.Shader
{
return true;
}
bool QueryHostSupportsShaderNonUniformIndexing()
{
return false;
}
/// <summary>
/// Queries host GPU support for signed normalized buffer texture formats.

View File

@@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Shader.Translation
public readonly bool SupportsShaderBallot;
public readonly bool SupportsShaderBarrierDivergence;
public readonly bool SupportsShaderFloat64;
public readonly bool SupportsShaderNonUniformIndexing;
public readonly bool SupportsTextureShadowLod;
public readonly bool SupportsViewportMask;
@@ -20,6 +21,7 @@ namespace Ryujinx.Graphics.Shader.Translation
bool supportsShaderBallot,
bool supportsShaderBarrierDivergence,
bool supportsShaderFloat64,
bool supportsShaderNonUniformIndexing,
bool supportsTextureShadowLod,
bool supportsViewportMask)
{
@@ -30,6 +32,7 @@ namespace Ryujinx.Graphics.Shader.Translation
SupportsShaderBallot = supportsShaderBallot;
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
SupportsShaderFloat64 = supportsShaderFloat64;
SupportsShaderNonUniformIndexing = supportsShaderNonUniformIndexing;
SupportsTextureShadowLod = supportsTextureShadowLod;
SupportsViewportMask = supportsViewportMask;
}

View File

@@ -364,6 +364,7 @@ namespace Ryujinx.Graphics.Shader.Translation
GpuAccessor.QueryHostSupportsShaderBallot(),
GpuAccessor.QueryHostSupportsShaderBarrierDivergence(),
GpuAccessor.QueryHostSupportsShaderFloat64(),
GpuAccessor.QueryHostSupportsShaderNonUniformIndexing(),
GpuAccessor.QueryHostSupportsTextureShadowLod(),
GpuAccessor.QueryHostSupportsViewportMask());

View File

@@ -494,6 +494,8 @@ namespace Ryujinx.Graphics.Vulkan
UniformBufferStandardLayout = supportedPhysicalDeviceVulkan12Features.UniformBufferStandardLayout,
UniformAndStorageBuffer8BitAccess = supportedPhysicalDeviceVulkan12Features.UniformAndStorageBuffer8BitAccess,
StorageBuffer8BitAccess = supportedPhysicalDeviceVulkan12Features.StorageBuffer8BitAccess,
ShaderSampledImageArrayNonUniformIndexing = supportedPhysicalDeviceVulkan12Features.ShaderSampledImageArrayNonUniformIndexing,
ShaderStorageImageArrayNonUniformIndexing = supportedPhysicalDeviceVulkan12Features.ShaderStorageImageArrayNonUniformIndexing,
};
pExtendedFeatures = &featuresVk12;

View File

@@ -775,6 +775,9 @@ namespace Ryujinx.Graphics.Vulkan
supportsShaderBallot: false,
supportsShaderBarrierDivergence: Vendor != Vendor.Intel,
supportsShaderFloat64: Capabilities.SupportsShaderFloat64,
supportsShaderNonUniformIndexing:
featuresVk12.ShaderSampledImageArrayNonUniformIndexing &&
featuresVk12.ShaderStorageImageArrayNonUniformIndexing,
supportsTextureGatherOffsets: features2.Features.ShaderImageGatherExtended && !IsMoltenVk,
supportsTextureShadowLod: false,
supportsVertexStoreAndAtomics: features2.Features.VertexPipelineStoresAndAtomics,

View File

@@ -0,0 +1,151 @@
using Ryujinx.HLE.HOS.Services.Hid;
using SDL;
using static SDL.SDL3;
using System;
namespace Ryujinx.Input.SDL3
{
/// <summary>
/// Manages a HID handle of a gamepad to encode and write HD rumble commands for Nin controllers.
/// </summary>
public unsafe class NpadHdRumble : IDisposable
{
private readonly SDL_hid_device* _hidHandle;
private int _globalCount;
private NpadHdRumble(SDL_hid_device* hidHandle)
{
_hidHandle = hidHandle;
}
public static NpadHdRumble Create(SDL_Gamepad* gamepadHandle)
{
ushort vendor = SDL_GetGamepadVendor(gamepadHandle);
if (vendor != 0x057e)
{
return null;
}
ushort product = SDL_GetGamepadProduct(gamepadHandle);
if (product != 0x2006 && product != 0x2007 && product != 0x2009 && product != 0x200e)
{
return null;
}
return new NpadHdRumble(SDL_hid_open(vendor, product, 0));
}
// Some of the code was translated from https://github.com/MIZUSHIKI/JoyShockLibrary-plus-HDRumble
private void WriteHdRumble(
int encLeftLowFreq, int encLeftLowAmp,
int encLeftHighFreq, int encLeftHighAmp,
int encRightLowFreq, int encRightLowAmp,
int encRightHighFreq, int encRightHighAmp)
{
byte[] buf = new byte[10];
buf[0] = 0x10;
buf[1] = (byte)((++_globalCount) & 0xF);
buf[2] = (byte)(encLeftHighFreq & 0xFF);
buf[3] = (byte)(encLeftHighAmp + ((encLeftHighFreq >> 8) & 0xFF));
buf[4] = (byte)(encLeftLowFreq + ((encLeftLowAmp >> 8) & 0xFF));
buf[5] = (byte)(encLeftLowAmp & 0xFF);
buf[6] = (byte)(encRightHighFreq & 0xFF);
buf[7] = (byte)(encRightHighAmp + ((encRightHighFreq >> 8) & 0xFF));
buf[8] = (byte)(encRightLowFreq + ((encRightLowAmp >> 8) & 0xFF));
buf[9] = (byte)(encRightLowAmp & 0xFF);
if (_globalCount > 0xF)
{
_globalCount = 0x0;
}
fixed (byte* ptr = buf)
{
SDL_hid_write(_hidHandle, ptr, (nuint)buf.Length);
}
}
private static int EncodeLowFreq(float lowFreq)
{
float lf = Math.Clamp(lowFreq, 40.875885f, 626.286133f);
return (int)Math.Round(32 * Math.Log2(lf * 0.1f)) - 0x40;
}
private static int EncodeHighFreq(float highFreq)
{
float hf = Math.Clamp(highFreq, 81.75177f, 1252.572266f);
return ((int)Math.Round(32 * Math.Log2(hf * 0.1f)) - 0x60) * 4;
}
private static int EncodeLowAmp(float rawAmp)
{
int encodedAmp = 0;
if (rawAmp is > 0 and < 0.012f)
{
encodedAmp = 1;
}
else if (rawAmp is >= 0.012f and < 0.112f)
{
encodedAmp = (int)Math.Round(4 * Math.Log2(rawAmp * 110f));
}
else if (rawAmp is >= 0.112f and < 0.225f)
{
encodedAmp = (int)Math.Round(16 * Math.Log2(rawAmp * 17f));
}
else if (rawAmp is >= 0.225f and <= 1f)
{
encodedAmp = (int)Math.Round(32 * Math.Log2(rawAmp * 8.7f));
}
return (int)Math.Floor(encodedAmp / 2.0) + 64;
}
private static int EncodeHighAmp(float rawAmp)
{
int encodedAmp = 0;
if (rawAmp is > 0 and < 0.012f)
{
encodedAmp = 1;
}
else if (rawAmp is >= 0.012f and < 0.112f)
{
encodedAmp = (int)Math.Round(4 * Math.Log2(rawAmp * 110f));
}
else if (rawAmp is >= 0.112f and < 0.225f)
{
encodedAmp = (int)Math.Round(16 * Math.Log2(rawAmp * 17f));
}
else if (rawAmp is >= 0.225f and <= 1f)
{
encodedAmp = (int)Math.Round(32 * Math.Log2(rawAmp * 8.7f));
}
return encodedAmp * 2;
}
public bool HdRumble(VibrationValue left, VibrationValue right)
{
WriteHdRumble(EncodeLowFreq(left.FrequencyLow),
EncodeLowAmp(left.AmplitudeLow),
EncodeHighFreq(left.FrequencyHigh),
EncodeHighAmp(left.AmplitudeHigh),
EncodeLowFreq(right.FrequencyLow),
EncodeLowAmp(right.AmplitudeLow),
EncodeHighFreq(right.FrequencyHigh),
EncodeHighAmp(right.AmplitudeHigh));
return true;
}
public void Dispose()
{
SDL_hid_close(_hidHandle);
}
}
}

View File

@@ -2,6 +2,7 @@ using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.HOS.Services.Hid;
using System;
using System.Collections.Generic;
using System.Numerics;
@@ -76,11 +77,14 @@ namespace Ryujinx.Input.SDL3
private SDL_Gamepad* _gamepadHandle;
private NpadHdRumble _hdRumble;
private float _triggerThreshold;
public SDL3Gamepad(SDL_Gamepad* gamepadHandle, string driverId)
{
_gamepadHandle = gamepadHandle;
_hdRumble = NpadHdRumble.Create(gamepadHandle);
_buttonsUserMapping = new List<ButtonMappingEntry>(20);
Name = SDL_GetGamepadName(_gamepadHandle);
@@ -165,6 +169,10 @@ namespace Ryujinx.Input.SDL3
protected virtual void Dispose(bool disposing)
{
if (disposing && _hdRumble != null)
{
_hdRumble.Dispose();
}
if (disposing && _gamepadHandle != null)
{
SDL_CloseGamepad(_gamepadHandle);
@@ -184,6 +192,11 @@ namespace Ryujinx.Input.SDL3
_triggerThreshold = triggerThreshold;
}
public bool HDRumble(VibrationValue left, VibrationValue right)
{
return _hdRumble?.HdRumble(left, right) ?? false;
}
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
{
if ((Features & GamepadFeaturesFlag.Rumble) == 0)

View File

@@ -1,6 +1,7 @@
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Hid;
using System;
using System.Collections.Generic;
using System.Numerics;
@@ -61,6 +62,8 @@ namespace Ryujinx.Input.SDL3
public GamepadFeaturesFlag Features { get; }
private SDL_Gamepad* _gamepadHandle;
private NpadHdRumble _hdRumble;
private enum JoyConType
{
@@ -76,6 +79,7 @@ namespace Ryujinx.Input.SDL3
public SDL3JoyCon(SDL_Gamepad* gamepadHandle, string driverId)
{
_gamepadHandle = gamepadHandle;
_hdRumble = NpadHdRumble.Create(gamepadHandle);
_buttonsUserMapping = new List<ButtonMappingEntry>(10);
Name = SDL_GetGamepadName(_gamepadHandle);
@@ -139,6 +143,10 @@ namespace Ryujinx.Input.SDL3
protected virtual void Dispose(bool disposing)
{
if (disposing && _hdRumble != null)
{
_hdRumble.Dispose();
}
if (disposing && _gamepadHandle != null)
{
SDL_CloseGamepad(_gamepadHandle);
@@ -156,6 +164,11 @@ namespace Ryujinx.Input.SDL3
{
}
public bool HDRumble(VibrationValue left, VibrationValue right)
{
return _hdRumble?.HdRumble(left, right) ?? false;
}
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
{

View File

@@ -559,18 +559,29 @@ namespace Ryujinx.Input.HLE
{
VibrationValue leftVibrationValue = dualVibrationValue.Item1;
VibrationValue rightVibrationValue = dualVibrationValue.Item2;
float low = Math.Min(1f, (float)((rightVibrationValue.AmplitudeLow * 0.85 + rightVibrationValue.AmplitudeHigh * 0.15) * controllerConfig.Rumble.StrongRumble));
float high = Math.Min(1f, (float)((leftVibrationValue.AmplitudeLow * 0.15 + leftVibrationValue.AmplitudeHigh * 0.85) * controllerConfig.Rumble.WeakRumble));
leftVibrationValue.AmplitudeLow *= controllerConfig.Rumble.WeakRumble;
leftVibrationValue.AmplitudeHigh *= controllerConfig.Rumble.StrongRumble;
rightVibrationValue.AmplitudeLow *= controllerConfig.Rumble.WeakRumble;
rightVibrationValue.AmplitudeHigh *= controllerConfig.Rumble.StrongRumble;
_gamepad?.Rumble(low, high, uint.MaxValue);
if (_gamepad?.HDRumble(leftVibrationValue, rightVibrationValue) == false)
{
_gamepad?.Rumble(low, high, uint.MaxValue);
}
Logger.Debug?.Print(LogClass.Hid, $"Effect for {controllerConfig.PlayerIndex} " +
$"L.low.amp={leftVibrationValue.AmplitudeLow}, " +
$"L.high.amp={leftVibrationValue.AmplitudeHigh}, " +
$"L.low.freq={leftVibrationValue.FrequencyLow}, " +
$"L.high.freq={leftVibrationValue.FrequencyHigh}, " +
$"R.low.amp={rightVibrationValue.AmplitudeLow}, " +
$"R.high.amp={rightVibrationValue.AmplitudeHigh} " +
$"--> ({low}, {high})");
$"R.low.freq={rightVibrationValue.FrequencyLow}, " +
$"R.high.freq={rightVibrationValue.FrequencyHigh}");
}
}
}

View File

@@ -1,5 +1,6 @@
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Services.Hid;
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
@@ -74,6 +75,16 @@ namespace Ryujinx.Input
public void ClearLed() => SetLed(0);
/// <summary>
/// Starts an HD vibration effect on the gamepad if available.
/// </summary>
/// <param name="left">The vibration data for the left side</param>
/// <param name="right">The vibration data for the right side</param>
bool HDRumble(VibrationValue left, VibrationValue right)
{
return false;
}
/// <summary>
/// Starts a rumble effect on the gamepad.
/// </summary>

View File

@@ -1,5 +1,6 @@
using Gommon;
using Humanizer;
using MsgPack;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
@@ -23,24 +24,382 @@ namespace Ryujinx.Ava.Systems.PlayReport
private static FormattedValue SkywardSwordHD_Rupees(SingleValue value)
=> "rupee".ToQuantity(value.Matched.IntValue);
private static FormattedValue SuperMarioOdyssey_AssistMode(SingleValue value)
=> value.Matched.BoxedValue is 1 ? "Playing in Assist Mode" : "Playing in Regular Mode";
private static FormattedValue EchoesOfWisdom_Warp(SingleValue value)
{
FormattedValue locations = value.Matched.IntValue switch
{
// Hyrule Field
23 => "Hyrule Field: Kakariko Village",
43 => "Hyrule Field: West of Hyrule Ranch",
45 => "Hyrule Field: North of Hyrule Ranch",
25 => "Hyrule Field: Hyrule Ranch",
26 => "Hyrule Field: West of Hyrule Castle",
48 => "Hyrule Field: Haunted Grove",
24 => "Hyrule Field: Hyrule Castle",
27 => "Hyrule Field: Northern Sanctuary",
28 => "Eastern Hyrule Field: Eastern Temple",
41 => "Eastern Hyrule Field: Dampé Studio",
22 => "Lake Hylia: Great Fairy Shrine",
// Eternal Forest
47 => "Eternal Forest: Entrance",
46 => "Eternal Forest: Great Deku Tree",
752 => "Eternal Forest: Stilled Ancient Ruins (Halfway Point)",
753 => "Eternal Forest: Stilled Ancient Ruins (Null)",
// Suthorn
33 => "Suthorn Prairie: Lueburry's House",
20 => "Suthorn Prairie: Suthorn Village",
21 => "Suthorn Forest: Suthorn Ruins",
// Faron Wetlands
13 => "Faron Wetlands: Entrance",
15 => "Faron Wetlands: Scrubton",
18 => "Faron Wetlands: Blossu's House",
17 => "Faron Wetlands: Heart Lake",
852 => "Faron Wetlands: Stilled Faron Wetlands",
601 => "Faron Wetlands: Faron Temple 3F",
602 => "Faron Wetlands: Faron Temple 2F (Underwater Entrance)",
603 => "Faron Wetlands: Faron Temple 2F (West Entrance)",
604 => "Faron Wetlands: Faron Temple 2F (Cliff Entrance)",
605 => "Faron Wetlands: Faron Temple 1F (Diababa)",
606 => "Faron Wetlands: Faron Temple 1F (Gohma)",
// Jabul Waters
11 => "Jabul Waters: River Zora Village",
9 => "Jabul Waters: Crossflows Plaza",
8 => "Jabul Waters: Seesyde Village",
12 => "Jabul Waters: Sea Zora Village",
10 => "Jabul Waters: Lord Jabu-Jabu's Den",
201 => "Jabul Waters: Jabul Ruins 1F (Entrance)",
202 => "Jabul Waters: Jabul Ruins 1F (Vocavor)",
// Gerudo Desert
40 => "Gerudo Desert: Entrance",
29 => "Gerudo Desert: Oasis",
32 => "Gerudo Desert: Ancestor's Cave Of Rest",
30 => "Gerudo Desert: Gerudo Town",
31 => "Gerudo Desert: Gerudo Sanctum",
351 => "Gerudo Desert: Stilled Gerudo Sanctum",
303 => "Gerudo Desert: Gerudo Sanctum 1F (West Entrance)",
304 => "Gerudo Desert: Gerudo Sanctum 1F (East Entrance)",
301 => "Gerudo Desert: Gerudo Sanctum 2F (The Key)",
302 => "Gerudo Desert: Gerudo Sanctum 2F (The Elephant Room)",
305 => "Gerudo Desert: Gerudo Sanctum 2F (Mogryph)",
// Eldin Volcano
4 => "Eldin Volcano: Eldin Volcano Trail",
44 => "Eldin Volcano: Lava Lake",
3 => "Eldin Volcano: Goron City",
5 => "Eldin Volcano: Rock Roast Volcano",
49 => "Eldin Volcano: Crater Shortcut",
552 => "Eldin Volcano: Stilled Eldin Volcano",
501 => "Eldin Volcano: Eldin Temple 1F",
503 => "Eldin Volcano: Eldin Temple 2F",
502 => "Eldin Volcano: Eldin Temple 3F",
// Hebra Mountain
34 => "Hebra Mountain: Hebra Mountain Passage (1)",
35 => "Hebra Mountain: Sheltered Hot Spring",
36 => "Hebra Mountain: Condé's House",
38 => "Hebra Mountain: Hebra Mountain Passage (2)",
37 => "Hebra Mountain: Hebra Mountain Passage (3)",
39 => "Hebra Mountain: Summit",
652 => "Hebra Mountain: Stilled Holy Mount Lanayru",
801 => "Hebra Mountain: Lanayru Temple 1F",
802 => "Hebra Mountain: Lanayru Temple B2",
803 => "Hebra Mountain: Lanayru Temple B4",
_ => FormattedValue.ForceReset
};
private static FormattedValue SuperMarioOdysseyChina_AssistMode(SingleValue value)
=> value.Matched.BoxedValue is 1 ? "Playing in 帮助模式" : "Playing in 普通模式";
return locations.Reset
? FormattedValue.ForceReset
: $"Warped to {locations}";
}
private static FormattedValue SuperMario3DAllStars(SingleValue value)
{
// TODO: Is this really necessary?
FormattedValue title = value.Matched.IntValue switch
{
1 => "Super Mario 64",
2 => "Super Mario Sunshine",
3 => "Super Mario Galaxy",
_ => FormattedValue.ForceReset
};
return title.Reset
? FormattedValue.ForceReset
: $"Playing {title}";
}
private static FormattedValue SuperMario3DAllStars_MainMenu(MultiValue value)
{
int albumId = value.Matched[0].IntValue;
int songId = value.Matched[1].IntValue;
string album = value.Matched[0].IntValue switch
{
1 => "Super Mario 64 OST",
2 => "Super Mario Sunshine OST",
3 => "Super Mario Galaxy OST",
_ => "Listening to Super Mario 3D All-Stars"
};
string song = (albumId, songId) switch
{
// Super Mario 64
(1, 0) => "It's a Me, Mario!",
(1, 1) => "Title Theme",
(1, 2) => "Peach's Message",
(1, 3) => "Opening",
(1, 4) => "Super Mario 64 Main Theme",
(1, 5) => "Slider",
(1, 6) => "Inside the Castle Walls",
(1, 7) => "Looping Steps",
(1, 8) => "Dire, Dire Docks",
(1, 9) => "Lethal Lava Land",
(1, 10) => "Snow Mountain",
(1, 11) => "Haunted House",
(1, 12) => "Merry-Go-Round",
(1, 13) => "Cave Dungeon",
(1, 14) => "Piranha Plant's Lullaby",
(1, 15) => "Powerful Mario",
(1, 16) => "Metallic Mario",
(1, 17) => "File Select",
(1, 18) => "Correct Solution",
(1, 19) => "Toad's Message",
(1, 20) => "Power Star",
(1, 21) => "Race Fanfare",
(1, 22) => "Star Catch Fanfare",
(1, 23) => "Game Start",
(1, 24) => "Course Clear",
(1, 25) => "Game Over",
(1, 26) => "Stage Boss",
(1, 27) => "Koopa's Message",
(1, 28) => "Koopa's Road",
(1, 29) => "Koopa's Theme",
(1, 30) => "Koopa Clear",
(1, 31) => "Ultimate Koopa",
(1, 32) => "Ultimate Koopa Clear",
(1, 33) => "Ending Demo",
(1, 34) => "Staff Roll",
(1, 35) => "Piranha Plant's Lullaby - Piano",
// Super Mario Sunshine
(2, 0) => "Isle Delfino",
(2, 1) => "Delfino Airstrip",
(2, 2) => "Bianco Hills",
(2, 3) => "Ricco Harbor",
(2, 4) => "Gelato Beach",
(2, 5) => "Pinna Beach",
(2, 6) => "Pinna Park",
(2, 7) => "Sirena Beach",
(2, 8) => "Hotel Delfino",
(2, 9) => "Casino",
(2, 10) => "Noki Bay",
(2, 11) => "Noki Depths",
(2, 12) => "Pianta Village",
(2, 13) => "Pianta Hot Spring",
(2, 14) => "Pianta Rescue",
(2, 15) => "Pianta Village - Fluff Festival",
(2, 16) => "Underground",
(2, 17) => "Secret Course",
(2, 18) => "Secret Course - Sky and Sea",
(2, 19) => "Corona Mountain",
(2, 20) => "Mid-Boss",
(2, 21) => "Proto Piranha",
(2, 22) => "Phantamanta",
(2, 23) => "Boss Battle",
(2, 24) => "Gooper Blooper Intro",
(2, 25) => "Wiggler Intro",
(2, 26) => "Mecha-Bowser",
(2, 27) => "Bowser",
(2, 28) => "Shadow Mario",
(2, 29) => "Racing Il Piantissimo",
(2, 30) => "Event",
(2, 31) => "Timed Event",
(2, 32) => "Yoshi-Go-Round",
(2, 33) => "Title Screen",
(2, 34) => "Opening Demo",
(2, 35) => "Select Data",
(2, 36) => "Select Scenario",
(2, 37) => "Course Intro",
(2, 38) => "Course Intro - Shadow Mario",
(2, 39) => "A Shine Sprite Appears",
(2, 40) => "Shine!",
(2, 41) => "Race Fanfare",
(2, 42) => "Casino Fanfare",
(2, 43) => "Too Bad!",
(2, 44) => "Game Over",
(2, 45) => "Welcome to Isle Delfino (Movie)",
(2, 46) => "Icky Goop (Movie)",
(2, 47) => "Mario on Trial (Movie)",
(2, 48) => "How to Use FLUDD (Movie)",
(2, 49) => "Shadow Mario Appears (Movie)",
(2, 50) => "The Kidnapping of Princess Peach (Movie)",
(2, 51) => "Mecha-Bowser Rises (Movie)",
(2, 52) => "Meet Bowser Jr. (Movie)",
(2, 53) => "FLUDD Theft (Movie)",
(2, 54) => "Hot Tub Intrusion (Movie)",
(2, 55) => "Epilogue (Movie)",
(2, 56) => "Staff Credits",
(2, 57) => "Have a Relaxing Vacation!",
// Super Mario Galaxy
(3, 0) => "Overture",
(3, 1) => "The Star Festival",
(3, 2) => "Attack of the Airships",
(3, 3) => "Catastrophe",
(3, 4) => "Peach's Castle Stolen",
(3, 5) => "Enter the Galaxy",
(3, 6) => "Egg Planet",
(3, 7) => "Rosaline in the Observatory 1",
(3, 8) => "The Honeyhive",
(3, 9) => "Space Junk Road",
(3, 10) => "Battlerock Galaxy",
(3, 11) => "Beach Bowl Galaxy",
(3, 12) => "Rosalina in the Observatory 2",
(3, 13) => "Enter Bowser Jr.!",
(3, 14) => "Waltz of the Boos",
(3, 15) => "Buoy Base Galaxy",
(3, 16) => "Gusty Garden Galaxy",
(3, 17) => "Rosaline in the Observatory 3",
(3, 18) => "King Bowser",
(3, 19) => "Melty Molten Galaxy",
(3, 20) => "The Galaxy Reactor",
(3, 21) => "Final Battle with Bowser",
(3, 22) => "A New Dawn",
(3, 23) => "Birth",
(3, 24) => "Super Mario Galaxy",
(3, 25) => "Purple Comet",
(3, 26) => "Blue Sky Athletic",
(3, 27) => "Super Mario 2007",
(3, 28) => "File Select",
(3, 29) => "Luma",
(3, 30) => "Gateway Galaxy",
(3, 31) => "Stolen Grand Star",
(3, 32) => "To the Observatory Grounds 1",
(3, 33) => "Observation Dome",
(3, 34) => "Course Select",
(3, 35) => "Dino Piranha",
(3, 36) => "A Chance to Grab a Star!",
(3, 37) => "A Tense Moment",
(3, 38) => "Big Bad Bugaboom",
(3, 39) => "King Kaliente",
(3, 40) => "The Toad Brigade",
(3, 41) => "Airship Armada",
(3, 42) => "Aquatic Race",
(3, 43) => "Space Fantasy",
(3, 44) => "Megaleg",
(3, 45) => "To The Observatory Grounds 2",
(3, 46) => "Space Athletic",
(3, 47) => "Speedy Comet",
(3, 48) => "Beach Bowl Galaxy - Undersea",
(3, 49) => "Interlude",
(3, 50) => "Bowser's Stronghold Appears",
(3, 51) => "The Fiery Stronghold",
(3, 52) => "The Big Staircase",
(3, 53) => "Bowser Appears",
(3, 54) => "Star Ball",
(3, 55) => "The Library",
(3, 56) => "Buoy Base Galaxy - Undersea",
(3, 57) => "Rainbow Mario",
(3, 58) => "Chase the Bunnies",
(3, 59) => "Help!",
(3, 60) => "Major Burrows",
(3, 61) => "Pipe Interior",
(3, 62) => "Cosmic Comet",
(3, 63) => "Drip Drop Galaxy",
(3, 64) => "Kingfin",
(3, 65) => "Boo Race",
(3, 66) => "Ice Mountain",
(3, 67) => "Ice Mario",
(3, 68) => "Lava Path",
(3, 69) => "Fire Mario",
(3, 70) => "Dusty Dune Galaxy",
(3, 71) => "Heavy Metal Mecha-Bowser",
(3, 72) => "A-wa-wa-wa!",
(3, 73) => "Deep Dark Galaxy",
(3, 74) => "Kamella",
(3, 75) => "Star Ball 2",
(3, 76) => "Sad Girl",
(3, 77) => "Flying Mario",
(3, 78) => "Star Child",
(3, 79) => "A Wish",
(3, 80) => "Family",
_ => ""
};
return string.IsNullOrEmpty(song) ? FormattedValue.ForceReset : $"{album} - {song}";
}
private static FormattedValue SuperMarioOdyssey(SingleValue value)
=> value.Matched.LongValue switch
{
// TODO: Needs updated for sub-areas.
2973331007 => "Cap Kingdom: Bonneton",
2661781375 => "Cascade Kingdom: Fossil Falls",
512560049 => "Sand Kingdom: Tostarena",
3079659402 => "Wooded Kingdom: Steam Gardens",
1941286268 => "Lake Kingdom: Lake Lamode",
3098209122 => "Cloud Kingdom: Nimbus Arena",
4088050842 => "Lost Kingdom: Forgotten Isle",
53003352 => "Metro Kingdom: New Donk City",
4265839612 => "Seaside Kingdom: Bubblaine",
3288863344 => "Snow Kingdom: Shiveria",
3180104973 => "Luncheon Kingdom: Mount Volbono",
2284558980 => "Ruined Kingdom: Crumbleden",
3024139598 => "Bowser's Kingdom: Bowser's Castle",
1351608174 => "Moon Kingdom: Honeylune Ridge",
1698750149 => "Dark Side: Rabbit Ridge",
3206301958 => "Darker Side: Culmina Crater",
3963002526 => "Mushroom Kingdom: Peach's Castle",
_ => FormattedValue.ForceReset
};
private static FormattedValue SuperMario3DWorldOrBowsersFury(SingleValue value)
=> value.Matched.BoxedValue is 0 ? "Playing Super Mario 3D World" : "Playing Bowser's Fury";
private static FormattedValue SuperMarioWonder(SingleValue value)
{
// TODO: Needs updated for course names.
MessagePackObject messagePackObject = value.Matched.PackedValue;
MessagePackObjectDictionary messagePackObjectDictionary = messagePackObject.AsDictionary();
int worldNumber = messagePackObjectDictionary["world_no"].AsInt32();
int courseNumber = 0;
if (messagePackObjectDictionary.TryGetValue("course_no", out MessagePackObject courseNumberVariable))
{
courseNumber = courseNumberVariable.AsInt32();
}
FormattedValue world = worldNumber switch
{
1 => "Pipe-Rock Plateau",
2 => "Petal Isles",
3 => "Fluff-Puff Peaks",
4 => "Shining Falls",
5 => "Sunbaked Desert",
6 => "Fungi Mines",
7 => "Deep Magma Bog",
9 => "Special World",
_ => FormattedValue.ForceReset
};
if (courseNumber == 0)
{
return FormattedValue.ForceReset;
}
return world.Reset
? FormattedValue.ForceReset
: $"{world}: {worldNumber}-{courseNumber}";
}
private static FormattedValue MarioKart8Deluxe_Mode(SingleValue value)
=> value.Matched.StringValue switch
{
// Single Player
"Single" => "Single Player",
// Multiplayer
"Multi-2players" => "Multiplayer 2 Players",
"Multi-3players" => "Multiplayer 3 Players",
"Multi-4players" => "Multiplayer 4 Players",
"Multi-2players" => "Multiplayer: 2 Players",
"Multi-3players" => "Multiplayer: 3 Players",
"Multi-4players" => "Multiplayer: 4 Players",
// Wireless/LAN Play
"Local-Single" => "Wireless/LAN Play",
"Local-2players" => "Wireless/LAN Play 2 Players",
@@ -62,8 +421,9 @@ namespace Ryujinx.Ava.Systems.PlayReport
private static FormattedValue PokemonSV(MultiValue values)
{
string playStatus = values.Matched[0].BoxedValue is 0 ? "Playing Alone" : "Playing in a group";
string region = PokemonSV_Region(values.Matched[1].ToString());
string union = values.Matched[0].BoxedValue is 0 ? "" : " with friends";
string academyName = PokemonSV_AcademyName(values.Application.Title);
FormattedValue locations = values.Matched[1].ToString() switch
{
@@ -89,18 +449,86 @@ namespace Ryujinx.Ava.Systems.PlayReport
"a_w20" => "North Area Three",
"a_w21" => "North Area One",
"a_w22" => "North Area Two",
"a_w23" => "The Great Crater of Paldea",
"a_w23" => "Area Zero: The Great Crater of Paldea",
"a_w24" => "South Paldean Sea",
"a_w25" => "West Paldean Sea",
"a_w26" => "East Paldean Sea",
"a_w27" => "North Paldean Sea",
//TODO DLC Locations
// Naranja / Uva Academy
"a_sch_entrance01" => $"{academyName} Academy: Entrance",
"a_sch_cafe01" => $"{academyName} Academy: Cafeteria",
"a_sch_shop01" => $"{academyName} Academy: School Store",
"a_sch_room01" => $"{academyName} Academy: Home Ec Room",
"a_sch_room02" => $"{academyName} Academy: Art Room",
"a_sch_room03" => $"{academyName} Academy: Biology Lab",
"a_sch_room04" => $"{academyName} Academy: Staff Room",
"a_sch_office01" => $"{academyName} Academy: Director's Office",
"a_sch_office03" => $"{academyName} Academy: Nurse's Office",
"a_sch_ground01" => $"{academyName} Academy: School Yard",
"a_sch_class1a" => $"{academyName} Academy: Classroom 1-A",
"a_sch_class1d" => $"{academyName} Academy: Classroom 1-D",
"a_sch_class2g" => $"{academyName} Academy: Classroom 2-G",
"a_sch_dorm01" => $"{academyName} Academy: Dorm Room (Trainer)",
"a_sch_dorm02" => $"{academyName} Academy: Dorm Room (Nemona)",
"a_sch_dorm03" => $"{academyName} Academy: Dorm Room (Arven)",
"a_sch_dorm04" => $"{academyName} Academy: Dorm Room (Penny)",
// DLC
// Kitakami
"a_su0101" => "Mossui Town",
"a_su0102" => "Loyalty Plaza",
"a_su0103" => "Kitakami Hall",
"a_su0104" => "Oni Mountain",
"a_su0105" => "Infernal Pass",
"a_su0106" => "Crystal Pool",
"a_su0107" => "Wistful Fields",
"a_su0108" => "Mossfell Confluence",
"a_su0109" => "Fellhorn Gorge",
"a_su0110" => "Paradise Barrens",
"a_su0111" => "Timeless Woods",
// Blueberry Academy: School
"a_sch_2_entrance0" => "Blueberry Academy: Entrance",
"a_sch_2_clubroom" => "Blueberry Academy: League Clubroom",
"a_sch_2_class1" => "Blueberry Academy: Classroom 1-4",
"a_sch_2_class2" => "Blueberry Academy: Classroom 3-2",
"a_sch_2_shop01" => "Blueberry Academy: School Store",
"a_sch_2_cafe01" => "Blueberry Academy: Cafeteria",
"a_sch_2_dorm01" => "Blueberry Academy: Dorm Room (Trainer)",
"a_sch_2_dorm02" => "Blueberry Academy: Dorm Room (Carmine)",
// Blueberry Academy: Terrarium
"a_su0201" => "Savanna Biome",
"a_su0202" => "Coastal Biome",
"a_su0203" => "Canyon Biome",
"a_su0204" => "Polar Biome",
_ => FormattedValue.ForceReset
};
return locations.Reset
? FormattedValue.ForceReset
: $"{playStatus} in {locations}";
return locations.Reset
? FormattedValue.ForceReset
: $"Exploring {region}{union} | {locations}";
}
private static string PokemonSV_Region(string location)
{
if (location.Contains("a_su02") || location.Contains("a_sch_2")) return "Unova";
if (location.Contains("a_su01")) return "Kitakami";
return "Paldea";
}
private static string PokemonSV_AcademyName(string title)
{
// TODO: Is this even necessary?
if (
title.Contains("Scarlet")
|| title.Contains("Escarlata")
|| title.Contains("Écarlate")
|| title.Contains("Karmesin")
|| title.Contains("Scarlatto")
|| title.Contains("スカーレット")
|| title.Contains("스칼렛")
|| title.Contains("朱")
) { return "Naranja"; }
return "Uva";
}
private static FormattedValue SuperSmashBrosUltimate_Mode(SparseMultiValue values)
@@ -641,5 +1069,7 @@ namespace Ryujinx.Ava.Systems.PlayReport
_ => FormattedValue.ForceReset
};
}
}

View File

@@ -14,7 +14,7 @@ namespace Ryujinx.Ava.Systems.PlayReport
private static readonly Lazy<Analyzer> _analyzerLazy = new(() => new Analyzer()
.AddSpec(
"01007ef00011e000",
"01007ef00011e000", // Breath of the Wild
spec => spec
.WithDescription("based on being in Master Mode.")
.AddValueFormatter("IsHardMode", BreathOfTheWild_MasterMode)
@@ -22,48 +22,74 @@ namespace Ryujinx.Ava.Systems.PlayReport
.AddValueFormatter("AoCVer", FormattedValue.SingleAlwaysResets)
)
.AddSpec(
"0100f2c0115b6000",
"0100f2c0115b6000", // Tears of the Kingdom
spec => spec
.WithDescription("based on where you are in Hyrule (Depths, Surface, Sky).")
.AddValueFormatter("PlayerPosY", TearsOfTheKingdom_CurrentField))
.AddSpec(
"01002da013484000",
"01002da013484000", // Skyward Sword
spec => spec
.WithDescription("based on how many Rupees you have.")
.AddValueFormatter("rupees", SkywardSwordHD_Rupees))
.AddSpec(
"0100000000010000",
"01008cf01baac000", // Echoes of Wisdom
spec => spec
.WithDescription("based on if you're playing with Assist Mode.")
.AddValueFormatter("is_kids_mode", SuperMarioOdyssey_AssistMode)
.WithDescription("based on where you've warped.")
.AddValueFormatter("dest_index", EchoesOfWisdom_Warp)
)
.AddSpec(
"010049900f546000", // Super Mario 3D All Stars
spec => spec
.WithDescription("based on what album and track you're listening to.")
.AddMultiValueFormatter(["app_id","song_id"], SuperMario3DAllStars_MainMenu)
)
.AddSpec(
"010075000ecbe000",
["010049900f546001", "010049900f546002", "010049900F546003"], // Super Mario 3D All Stars
spec => spec
.WithDescription("based on if you're playing with Assist Mode.")
.AddValueFormatter("is_kids_mode", SuperMarioOdysseyChina_AssistMode)
.WithDescription("based on which game you've selected to play in the collection.")
.AddValueFormatter("program_id", SuperMario3DAllStars)
)
.AddSpec(
"010028600ebda000",
"0100000000010000", // Super Mario Odyssey
spec => spec
.WithDescription("based on what kingdom you're in.")
.AddValueFormatter("stage_name", SuperMarioOdyssey)
)
.AddSpec(
"010028600ebda000", // Super Mario 3D World + Bowser's Fury
spec => spec
.WithDescription("based on being in either Super Mario 3D World or Bowser's Fury.")
.AddValueFormatter("mode", SuperMario3DWorldOrBowsersFury)
)
.AddSpec(
["010049900f546000", "010049900f546001", "010049900f546002", "010049900F546003"],
spec => spec
.WithDescription("based on which game you've selected to play in the collection.")
.AddValueFormatter("program_id", SuperMario3DAllStars)
)
.AddSpec(
"010015100b514000", // Super Mario Bros. Wonder
spec => spec
.WithDescription("based on what world and course you're in.")
.AddValueFormatter("stage_info", SuperMarioWonder)
)
.AddSpec( // Global & China IDs
["0100152000022000", "010075100e8ec000"],
["0100152000022000", "010075100e8ec000"], // Mario Kart 8 Deluxe
spec => spec
.WithDescription(
"based on what modes you're selecting in the menu & whether or not you're in a race.")
.AddValueFormatter("To", MarioKart8Deluxe_Mode)
)
.AddSpec(
["0100a3d008c5c000", "01008f6008c5e000"],
["0100a3d008c5c000", "01008f6008c5e000"], // Pokemon Scarlet/Violet
spec => spec
.WithDescription("based on if you're playing alone or in a group and what area of Paldea you're exploring.")
.AddMultiValueFormatter(["team_circle", "area_no"], PokemonSV)
)
.AddSpec(
"01006a800016e000",
"01006a800016e000", // Super Smash Bros. Ultimate
spec => spec
.WithDescription("based on what mode you're playing, who won, and what characters were present.")
.AddSparseMultiValueFormatter(
@@ -83,8 +109,10 @@ namespace Ryujinx.Ava.Systems.PlayReport
)
.AddSpec(
[
"0100c9a00ece6000", "01008d300c50c000", "0100d870045b6000",
"010012f017576000", "0100c62011050000", "0100b3c014bda000"
"0100B4E00444C000", "0100d870045b6000", "01008d300c50c000", "0100c62011050000", "010012f017576000",
/*Famicom*/ /*NES*/ /*SNES*/ /*GBC*/ /*GBA*/
"0100b3c014bda000", "0100c9a00ece6000", "0100e0601c632000", "0100bfc01d976000"
/*SEGA Genesis*/ /*N64*/ /*N64 MATURE*/ /*Virtual Boy*/
],
spec => spec
.WithDescription(