diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml
index 433e65cb2..1554b8f6b 100644
--- a/.github/workflows/canary.yml
+++ b/.github/workflows/canary.yml
@@ -202,7 +202,7 @@ jobs:
macos_release:
name: Release MacOS universal
- runs-on: ubuntu-latest
+ runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 7e11f6edf..072c6bf2f 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -183,7 +183,7 @@ jobs:
macos_release:
name: Release MacOS universal
- runs-on: ubuntu-latest
+ runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
diff --git a/docs/compatibility.csv b/docs/compatibility.csv
index 3528652c8..570c93618 100644
--- a/docs/compatibility.csv
+++ b/docs/compatibility.csv
@@ -1070,6 +1070,7 @@
010017B0102A8000,"Emma: Lost in Memories",nvdec,playable,2021-01-28 16:19:10
010068300E08E000,"Enchanted in the Moonlight - Kiryu, Chikage & Yukinojo -",gpu;nvdec,ingame,2022-11-20 16:18:45
01007A4008486000,"Enchanting Mahjong Match",gpu,ingame,2020-04-17 22:01:31
+0100EF901E552000,"ENDER MAGNOLIA: Bloom in the Mist",deadlock,boots,2025-01-22 17:59:00
01004F3011F92000,"Endless Fables: Dark Moor",gpu;nvdec,ingame,2021-03-07 15:31:03
010067B017588000,"Endless Ocean™ Luminous",services-horizon;crash,ingame,2024-05-30 02:05:57
0100B8700BD14000,"Energy Cycle Edge",services,ingame,2021-11-30 05:02:31
@@ -2681,7 +2682,7 @@
01005EA01C0FC000,"SONIC X SHADOW GENERATIONS",crash,ingame,2025-01-07 04:20:45
010064F00C212000,"Soul Axiom Rebooted",nvdec;slow,ingame,2020-09-04 12:41:01
0100F2100F0B2000,"Soul Searching",,playable,2020-07-09 18:39:07
-01008F2005154000,"South Park™: The Fractured but Whole™ - Standard Edition",slow;online-broken,playable,2024-07-08 17:47:28
+01008F2005154000,"South Park™: The Fractured but Whole™ - Standard Edition",slow;online-broken;vulkan-backend-bug;gpu,ingame,2025-01-21 17:35:10
0100B9F00C162000,"Space Blaze",,playable,2020-08-30 16:18:05
010005500E81E000,"Space Cows",UE4;crash,menus,2020-06-15 11:33:20
0100707011722000,"Space Elite Force",,playable,2020-11-27 15:21:05
diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/GenericControllerInputConfig.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/GenericControllerInputConfig.cs
index e6fe74d53..fbb19767c 100644
--- a/src/Ryujinx.Common/Configuration/Hid/Controller/GenericControllerInputConfig.cs
+++ b/src/Ryujinx.Common/Configuration/Hid/Controller/GenericControllerInputConfig.cs
@@ -78,5 +78,10 @@ namespace Ryujinx.Common.Configuration.Hid.Controller
/// Controller Rumble Settings
///
public RumbleConfigController Rumble { get; set; }
+
+ ///
+ /// Controller LED Settings
+ ///
+ public LedConfigController Led { get; set; }
}
}
diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/LedConfigController.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/LedConfigController.cs
new file mode 100644
index 000000000..21727d62e
--- /dev/null
+++ b/src/Ryujinx.Common/Configuration/Hid/Controller/LedConfigController.cs
@@ -0,0 +1,15 @@
+namespace Ryujinx.Common.Configuration.Hid.Controller
+{
+ public class LedConfigController
+ {
+ ///
+ /// Packed RGB int of the color
+ ///
+ public uint LedColor { get; set; }
+
+ ///
+ /// Enable LED color changing by the emulator
+ ///
+ public bool EnableLed { get; set; }
+ }
+}
diff --git a/src/Ryujinx.Common/TitleIDs.cs b/src/Ryujinx.Common/TitleIDs.cs
index dca634d9d..e0cb12026 100644
--- a/src/Ryujinx.Common/TitleIDs.cs
+++ b/src/Ryujinx.Common/TitleIDs.cs
@@ -14,6 +14,7 @@ namespace Ryujinx.Common
{
switch (currentBackend)
{
+ case GraphicsBackend.Metal when !OperatingSystem.IsMacOS():
case GraphicsBackend.OpenGl when OperatingSystem.IsMacOS():
return GraphicsBackend.Vulkan;
case GraphicsBackend.Vulkan or GraphicsBackend.OpenGl or GraphicsBackend.Metal:
@@ -28,18 +29,28 @@ namespace Ryujinx.Common
public static readonly string[] GreatMetalTitles =
[
- "01006f8002326000", // Animal Crossings: New Horizons
- "01009bf0072d4000", // Captain Toad: Treasure Tracker
+ "010076f0049a2000", // Bayonetta
"0100a5c00d162000", // Cuphead
"010023800d64a000", // Deltarune
+ "01003a30012c0000", // LEGO City Undercover
"010028600EBDA000", // Mario 3D World
"0100152000022000", // Mario Kart 8 Deluxe
- "01005CA01580E000", // Persona 5
- "0100187003A36000", // Pokémon: Let's Go, Evoli!
+ "010075a016a3a000", // Persona 4 Arena Ultimax
+ "0100187003A36000", // Pokémon: Let's Go, Eevee!
"010003f003a34000", // Pokémon: Let's Go, Pikachu!
"01008C0016544000", // Sea of Stars
"01006A800016E000", // Smash Ultimate
- "0100000000010000", // Super Mario Odyessy
+ "01006bb00c6f0000", // The Legend of Zelda: Link's Awakening
+
+ // These ones have small issues, but those happen on Vulkan as well:
+ "01006f8002326000", // Animal Crossings: New Horizons
+ "01009bf0072d4000", // Captain Toad: Treasure Tracker
+ "01009510001ca000", // Fast RMX
+ "01005CA01580E000", // Persona 5 Royale
+ "0100000000010000", // Super Mario Odyssey
+
+ //Isaac claims it has a issue in level 2, but I am not able to replicate it on my M3. More testing would be appreciated:
+ "010015100b514000", // Super Mario Bros. Wonder
];
public static string GetDiscordGameAsset(string titleId)
@@ -47,72 +58,87 @@ namespace Ryujinx.Common
public static readonly string[] DiscordGameAssetKeys =
[
+ //All games are in Alphabetical order by Game name.
+
+ //Dragon Quest Franchise
"010008900705c000", // Dragon Quest Builders
"010042000a986000", // Dragon Quest Builders 2
- "010055d009f78000", // Fire Emblem: Three Houses
- "0100a12011cc8000", // Fire Emblem: Shadow Dragon
+ //Fire Emblem Franchise
"0100a6301214e000", // Fire Emblem Engage
+ "0100a12011cc8000", // Fire Emblem: Shadow Dragon
+ "010055d009f78000", // Fire Emblem: Three Houses
"0100f15003e64000", // Fire Emblem Warriors
"010071f0143ea000", // Fire Emblem Warriors: Three Hopes
-
- "01007e3006dda000", // Kirby Star Allies
+
+ //Kirby Franchise
"01004d300c5ae000", // Kirby and the Forgotten Land
- "01006b601380e000", // Kirby's Return to Dream Land Deluxe
- "01003fb00c5a8000", // Super Kirby Clash
- "0100227010460000", // Kirby Fighters 2
"0100a8e016236000", // Kirby's Dream Buffet
-
+ "0100227010460000", // Kirby Fighters 2
+ "01006b601380e000", // Kirby's Return to Dream Land Deluxe
+ "01007e3006dda000", // Kirby Star Allies
+ "01003fb00c5a8000", // Super Kirby Clash
+
+
+ //The Zelda Franchise
+ "01000b900d8b0000", // Cadence of Hyrule
+ "0100ae00096ea000", // Hyrule Warriors: Definitive Edition
+ "01002b00111a2000", // Hyrule Warriors: Age of Calamity
"01007ef00011e000", // The Legend of Zelda: Breath of the Wild
"01006bb00c6f0000", // The Legend of Zelda: Link's Awakening
"01002da013484000", // The Legend of Zelda: Skyward Sword HD
"0100f2c0115b6000", // The Legend of Zelda: Tears of the Kingdom
"01008cf01baac000", // The Legend of Zelda: Echoes of Wisdom
- "01000b900d8b0000", // Cadence of Hyrule
- "0100ae00096ea000", // Hyrule Warriors: Definitive Edition
- "01002b00111a2000", // Hyrule Warriors: Age of Calamity
+
+ //Luigi Franchise
"010048701995e000", // Luigi's Mansion 2 HD
"0100dca0064a6000", // Luigi's Mansion 3
+ //Metroid Franchise
"010093801237c000", // Metroid Dread
"010012101468c000", // Metroid Prime Remastered
- "0100000000010000", // SUPER MARIO ODYSSEY
- "0100ea80032ea000", // Super Mario Bros. U Deluxe
- "01009b90006dc000", // Super Mario Maker 2
- "010049900f546000", // Super Mario 3D All-Stars
- "010049900F546001", // ^ 64
- "010049900F546002", // ^ Sunshine
- "010049900F546003", // ^ Galaxy
- "010028600ebda000", // Super Mario 3D World + Bowser's Fury
- "010015100b514000", // Super Mario Bros. Wonder
- "0100152000022000", // Mario Kart 8 Deluxe
- "010036b0034e4000", // Super Mario Party
- "01006fe013472000", // Mario Party Superstars
- "0100965017338000", // Super Mario Party Jamboree
+ //Monster Hunter Franchise
+ "0100770008dd8000", // Monster Hunter Generations Ultimate
+ "0100b04011742000", // Monster Hunter Rise
+
+ //Mario Franchise
"01006d0017f7a000", // Mario & Luigi: Brothership
+ "010003000e146000", // Mario & Sonic at the Olympic Games Tokyo 2020
"010067300059a000", // Mario + Rabbids: Kingdom Battle
"0100317013770000", // Mario + Rabbids: Sparks of Hope
+ "0100c9c00e25c000", // Mario Golf: Super Rush
+ "0100152000022000", // Mario Kart 8 Deluxe
+ "01006fe013472000", // Mario Party Superstars
+ "010019401051c000", // Mario Strikers: Battle League
+ "0100bde00862a000", // Mario Tennis Aces
+ "0100b99019412000", // Mario vs. Donkey Kong
+ "010049900f546000", // Super Mario 3D All-Stars
+ "010028600ebda000", // Super Mario 3D World + Bowser's Fury
+ "010049900F546001", // Super Mario 64
+ "0100ea80032ea000", // Super Mario Bros. U Deluxe
+ "010015100b514000", // Super Mario Bros. Wonder
+ "010049900F546003", // Super Mario Galaxy
+ "01009b90006dc000", // Super Mario Maker 2
+ "0100000000010000", // SUPER MARIO ODYSSEY
+ "010036b0034e4000", // Super Mario Party
+ "0100965017338000", // Super Mario Party Jamboree
+ "0100bc0018138000", // Super Mario RPG
+ "010049900F546002", // Super Mario Sunshine
"0100a3900c3e2000", // Paper Mario: The Origami King
"0100ecd018ebe000", // Paper Mario: The Thousand-Year Door
- "0100bc0018138000", // Super Mario RPG
- "0100bde00862a000", // Mario Tennis Aces
- "0100c9c00e25c000", // Mario Golf: Super Rush
- "010019401051c000", // Mario Strikers: Battle League
- "010003000e146000", // Mario & Sonic at the Olympic Games Tokyo 2020
- "0100b99019412000", // Mario vs. Donkey Kong
+ //Pikmin Franchise
"0100aa80194b0000", // Pikmin 1
"0100d680194b2000", // Pikmin 2
"0100f4c009322000", // Pikmin 3 Deluxe
"0100b7c00933a000", // Pikmin 4
+ //The Pokémon Franchise
"0100f4300bf2c000", // New Pokémon Snap
"0100000011d90000", // Pokémon Brilliant Diamond
"01001f5010dfa000", // Pokémon Legends: Arceus
- "010003f003a34000", // Pokémon: Let's Go Pikachu!
- "0100187003a36000", // Pokémon: Let's Go Eevee!
"01003d200baa2000", // Pokémon Mystery Dungeon - Rescue Team DX
"0100a3d008c5c000", // Pokémon Scarlet
"01008db008c2c000", // Pokémon Shield
@@ -120,24 +146,29 @@ namespace Ryujinx.Common
"0100abf008968000", // Pokémon Sword
"01008f6008c5e000", // Pokémon Violet
"0100b3f000be2000", // Pokkén Tournament DX
+ "0100187003a36000", // Pokémon: Let's Go Eevee!
+ "010003f003a34000", // Pokémon: Let's Go Pikachu!
- "01003bc0000a0000", // Splatoon 2 (US)
+ //Splatoon Franchise
"0100f8f0000a2000", // Splatoon 2 (EU)
"01003c700009c000", // Splatoon 2 (JP)
+ "01003bc0000a0000", // Splatoon 2 (US)
"0100c2500fc20000", // Splatoon 3
"0100ba0018500000", // Splatoon 3: Splatfest World Premiere
- "010040600c5ce000", // Tetris 99
- "0100277011f1a000", // Super Mario Bros. 35
- "0100ad9012510000", // PAC-MAN 99
+ //NSO Membership games
"0100ccf019c8c000", // F-ZERO 99
- "0100d870045b6000", // NES - Nintendo Switch Online
- "01008d300c50c000", // SNES - Nintendo Switch Online
- "0100c9a00ece6000", // N64 - Nintendo Switch Online
- "0100e0601c632000", // N64 - Nintendo Switch Online 18+
"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
+ "0100ad9012510000", // PAC-MAN 99
+ "010040600c5ce000", // Tetris 99
+ "01008d300c50c000", // SNES - Nintendo Switch Online
+ "0100277011f1a000", // Super Mario Bros. 35
+ //Misc Nintendo 1st party games
"01000320000cc000", // 1-2 Switch
"0100300012f2a000", // Advance Wars 1+2: Re-Boot Camp
"01006f8002326000", // Animal Crossing: New Horizons
@@ -152,27 +183,32 @@ namespace Ryujinx.Common
"01006a800016e000", // Super Smash Bros. Ultimate
"0100a9400c9c2000", // Tokyo Mirage Sessions #FE Encore
+ //Bayonetta Franchise
"010076f0049a2000", // Bayonetta
"01007960049a0000", // Bayonetta 2
"01004a4010fea000", // Bayonetta 3
"0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon
+ //Persona Franchise
"0100dcd01525a000", // Persona 3 Portable
- "010062b01525c000", // Persona 4 Golden
"010075a016a3a000", // Persona 4 Arena Ultimax
+ "010062b01525c000", // Persona 4 Golden
"01005ca01580e000", // Persona 5 Royal
"0100801011c3e000", // Persona 5 Strikers
"010087701b092000", // Persona 5 Tactica
- "01009aa000faa000", // Sonic Mania
+ //Sonic Franchise
"01004ad014bf0000", // Sonic Frontiers
+ "01009aa000faa000", // Sonic Mania
"01005ea01c0fc000", // SONIC X SHADOW GENERATIONS
"01005ea01c0fc001", // ^
+ //Xenoblade Franchise
"0100ff500e34a000", // Xenoblade Chronicles - Definitive Edition
"0100e95004038000", // Xenoblade Chronicles 2
"010074f013262000", // Xenoblade Chronicles 3
+ //Misc Games
"010056e00853a000", // A Hat in Time
"0100fd1014726000", // Baldurs Gate: Dark Alliance
"0100dbf01000a000", // Burnout Paradise Remastered
@@ -185,8 +221,6 @@ namespace Ryujinx.Common
"01003620068ea000", // Hand of Fate 2
"010085500130a000", // Lego City: Undercover
"010073c01af34000", // LEGO Horizon Adventures
- "0100770008dd8000", // Monster Hunter Generations Ultimate
- "0100b04011742000", // Monster Hunter Rise
"0100853015e86000", // No Man's Sky
"01007bb017812000", // Portal
"0100abd01785c000", // Portal 2
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
index 3a02eb615..b50ea174d 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
@@ -118,7 +118,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
private static string GetDiskCachePath()
{
return GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null
- ? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId, "cache", "shader")
+ ? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId.ToLower(), "cache", "shader")
: null;
}
diff --git a/src/Ryujinx.HLE/FileSystem/ContentManager.cs b/src/Ryujinx.HLE/FileSystem/ContentManager.cs
index ec0f58b01..a87453d00 100644
--- a/src/Ryujinx.HLE/FileSystem/ContentManager.cs
+++ b/src/Ryujinx.HLE/FileSystem/ContentManager.cs
@@ -710,9 +710,8 @@ namespace Ryujinx.HLE.FileSystem
{
updateNcasItem.Add((nca.Header.ContentType, entry.FullName));
}
- else
+ else if (updateNcas.TryAdd(nca.Header.TitleId, new List<(NcaContentType, string)>()))
{
- updateNcas.Add(nca.Header.TitleId, new List<(NcaContentType, string)>());
updateNcas[nca.Header.TitleId].Add((nca.Header.ContentType, entry.FullName));
}
}
@@ -898,9 +897,8 @@ namespace Ryujinx.HLE.FileSystem
{
updateNcasItem.Add((nca.Header.ContentType, entry.FullPath));
}
- else
+ else if (updateNcas.TryAdd(nca.Header.TitleId, new List<(NcaContentType, string)>()))
{
- updateNcas.Add(nca.Header.TitleId, new List<(NcaContentType, string)>());
updateNcas[nca.Header.TitleId].Add((nca.Header.ContentType, entry.FullPath));
}
diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs
index aa57a0310..6fdfe1398 100644
--- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs
@@ -56,7 +56,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
ProfilesJson profilesJson = JsonHelper.DeserializeFromFile(_profilesJsonPath, _serializerContext.ProfilesJson);
return profilesJson.Profiles
- .FindFirst(profile => profile.AccountState == AccountState.Open)
+ .FindFirst(profile => profile.UserId == profilesJson.LastOpened)
.Convert(profileJson => new UserProfile(new UserId(profileJson.UserId), profileJson.Name,
profileJson.Image, profileJson.LastModifiedTimestamp));
}
diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs b/src/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs
index 65748be33..846c4dc4f 100644
--- a/src/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs
@@ -244,6 +244,15 @@ namespace Ryujinx.HLE.HOS.Services.Settings
return ResultCode.Success;
}
+ [CommandCmif(68)]
+ // GetSerialNumber() -> buffer
+ public ResultCode GetSerialNumber(ServiceCtx context)
+ {
+ context.ResponseData.Write(Encoding.ASCII.GetBytes("RYU00000000000"));
+
+ return ResultCode.Success;
+ }
+
[CommandCmif(77)]
// GetDeviceNickName() -> buffer
public ResultCode GetDeviceNickName(ServiceCtx context)
diff --git a/src/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs b/src/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs
index 4a2a910f8..ade67b9c0 100644
--- a/src/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Spl/IGeneralInterface.cs
@@ -51,6 +51,9 @@ namespace Ryujinx.HLE.HOS.Services.Spl
context.ResponseData.Write(configValue);
+ if (result == SmcResult.Success)
+ return ResultCode.Success;
+
return (ResultCode)((int)result << 9) | ResultCode.ModuleId;
}
diff --git a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs
index 12bfab4bb..ed22c3661 100644
--- a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs
+++ b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs
@@ -1,6 +1,7 @@
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.Logging;
+using SDL2;
using System;
using System.Collections.Generic;
using System.Numerics;
@@ -118,6 +119,11 @@ namespace Ryujinx.Input.SDL2
result |= GamepadFeaturesFlag.Rumble;
}
+ if (SDL_GameControllerHasLED(_gamepadHandle) == SDL_bool.SDL_TRUE)
+ {
+ result |= GamepadFeaturesFlag.Led;
+ }
+
return result;
}
diff --git a/src/Ryujinx.Input/GamepadFeaturesFlag.cs b/src/Ryujinx.Input/GamepadFeaturesFlag.cs
index 69ec23686..52e8519d1 100644
--- a/src/Ryujinx.Input/GamepadFeaturesFlag.cs
+++ b/src/Ryujinx.Input/GamepadFeaturesFlag.cs
@@ -24,5 +24,10 @@ namespace Ryujinx.Input
/// Also named sixaxis
///
Motion,
+
+ ///
+ /// The LED on the back of modern PlayStation controllers (DualSense & DualShock 4).
+ ///
+ Led,
}
}
diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs
index 2642f603b..b2cae2348 100644
--- a/src/Ryujinx/AppHost.cs
+++ b/src/Ryujinx/AppHost.cs
@@ -674,7 +674,7 @@ namespace Ryujinx.Ava
public async Task LoadGuestApplication(BlitStruct? customNacpData = null)
{
- InitializeSwitchInstance();
+ InitEmulatedSwitch();
MainWindow.UpdateGraphicsConfig();
SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
@@ -757,6 +757,8 @@ namespace Ryujinx.Ava
{
romFsFiles = Directory.GetFiles(ApplicationPath, "*.romfs");
}
+
+ Logger.Notice.Print(LogClass.Application, $"Loading unpacked content archive from '{ApplicationPath}'.");
if (romFsFiles.Length > 0)
{
@@ -783,6 +785,8 @@ namespace Ryujinx.Ava
}
else if (File.Exists(ApplicationPath))
{
+ Logger.Notice.Print(LogClass.Application, $"Loading content archive from '{ApplicationPath}'.");
+
switch (Path.GetExtension(ApplicationPath).ToLowerInvariant())
{
case ".xci":
@@ -885,7 +889,7 @@ namespace Ryujinx.Ava
Logger.Info?.Print(LogClass.Emulation, "Emulation was paused");
}
- private void InitializeSwitchInstance()
+ private void InitEmulatedSwitch()
{
// Initialize KeySet.
VirtualFileSystem.ReloadKeySet();
@@ -1040,7 +1044,7 @@ namespace Ryujinx.Ava
_viewModel.WindowState = WindowState.FullScreen;
}
- if (_viewModel.WindowState is WindowState.FullScreen)
+ if (_viewModel.WindowState is WindowState.FullScreen || _viewModel.StartGamesWithoutUI)
{
_viewModel.ShowMenuAndStatusBar = false;
}
diff --git a/src/Ryujinx/Assets/Styles/CheckboxMenuItemStyle.axaml b/src/Ryujinx/Assets/Styles/CheckboxMenuItemStyle.axaml
new file mode 100644
index 000000000..13176c84f
--- /dev/null
+++ b/src/Ryujinx/Assets/Styles/CheckboxMenuItemStyle.axaml
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json
index 49ee5a01d..8a101d733 100644
--- a/src/Ryujinx/Assets/locales.json
+++ b/src/Ryujinx/Assets/locales.json
@@ -572,6 +572,31 @@
"zh_TW": "使用全螢幕模式啟動遊戲"
}
},
+ {
+ "ID": "MenuBarOptionsStartGamesWithoutUI",
+ "Translations": {
+ "ar_SA": "",
+ "de_DE": "",
+ "el_GR": "",
+ "en_US": "Start Games with UI Hidden",
+ "es_ES": "",
+ "fr_FR": "",
+ "he_IL": "",
+ "it_IT": "",
+ "ja_JP": "",
+ "ko_KR": "",
+ "no_NO": "",
+ "pl_PL": "",
+ "pt_BR": "",
+ "ru_RU": "",
+ "sv_SE": "",
+ "th_TH": "",
+ "tr_TR": "",
+ "uk_UA": "",
+ "zh_CN": "",
+ "zh_TW": ""
+ }
+ },
{
"ID": "MenuBarOptionsStopEmulation",
"Translations": {
@@ -748,32 +773,7 @@
}
},
{
- "ID": "MenuBarTools",
- "Translations": {
- "ar_SA": "_الأدوات",
- "de_DE": "",
- "el_GR": "_Εργαλεία",
- "en_US": "_Tools",
- "es_ES": "_Herramientas",
- "fr_FR": "_Outils",
- "he_IL": "_כלים",
- "it_IT": "_Strumenti",
- "ja_JP": "ツール(_T)",
- "ko_KR": "도구(_T)",
- "no_NO": "_Verktøy",
- "pl_PL": "_Narzędzia",
- "pt_BR": "_Ferramentas",
- "ru_RU": "_Инструменты",
- "sv_SE": "V_erktyg",
- "th_TH": "_เครื่องมือ",
- "tr_TR": "_Araçlar",
- "uk_UA": "_Інструменти",
- "zh_CN": "工具(_T)",
- "zh_TW": "工具(_T)"
- }
- },
- {
- "ID": "MenuBarToolsInstallFirmware",
+ "ID": "MenuBarActionsInstallFirmware",
"Translations": {
"ar_SA": "تثبيت البرنامج الثابت",
"de_DE": "Firmware installieren",
@@ -798,7 +798,7 @@
}
},
{
- "ID": "MenuBarFileToolsInstallFirmwareFromFile",
+ "ID": "MenuBarActionsInstallFirmwareFromFile",
"Translations": {
"ar_SA": "تثبيت برنامج ثابت من XCI أو ZIP",
"de_DE": "Firmware von einer XCI- oder einer ZIP-Datei installieren",
@@ -823,7 +823,7 @@
}
},
{
- "ID": "MenuBarFileToolsInstallFirmwareFromDirectory",
+ "ID": "MenuBarActionsInstallFirmwareFromDirectory",
"Translations": {
"ar_SA": "تثبيت برنامج ثابت من مجلد",
"de_DE": "Firmware aus einem Verzeichnis installieren",
@@ -848,7 +848,7 @@
}
},
{
- "ID": "MenuBarToolsInstallKeys",
+ "ID": "MenuBarActionsInstallKeys",
"Translations": {
"ar_SA": "",
"de_DE": "",
@@ -873,7 +873,7 @@
}
},
{
- "ID": "MenuBarFileToolsInstallKeysFromFile",
+ "ID": "MenuBarFileActionsInstallKeysFromFile",
"Translations": {
"ar_SA": "",
"de_DE": "",
@@ -898,7 +898,7 @@
}
},
{
- "ID": "MenuBarFileToolsInstallKeysFromFolder",
+ "ID": "MenuBarFileActionsInstallKeysFromFolder",
"Translations": {
"ar_SA": "",
"de_DE": "",
@@ -923,7 +923,7 @@
}
},
{
- "ID": "MenuBarToolsManageFileTypes",
+ "ID": "MenuBarActionsManageFileTypes",
"Translations": {
"ar_SA": "إدارة أنواع الملفات",
"de_DE": "Dateitypen verwalten",
@@ -948,7 +948,7 @@
}
},
{
- "ID": "MenuBarToolsInstallFileTypes",
+ "ID": "MenuBarActionsInstallFileTypes",
"Translations": {
"ar_SA": "تثبيت أنواع الملفات",
"de_DE": "Dateitypen installieren",
@@ -973,7 +973,7 @@
}
},
{
- "ID": "MenuBarToolsUninstallFileTypes",
+ "ID": "MenuBarActionsUninstallFileTypes",
"Translations": {
"ar_SA": "إزالة أنواع الملفات",
"de_DE": "Dateitypen deinstallieren",
@@ -998,7 +998,7 @@
}
},
{
- "ID": "MenuBarToolsXCITrimmer",
+ "ID": "MenuBarActionsXCITrimmer",
"Translations": {
"ar_SA": "",
"de_DE": "",
@@ -2297,6 +2297,56 @@
"zh_TW": "從應用程式的目前配置中提取 RomFS 分區 (包含更新)"
}
},
+ {
+ "ID": "GameListContextMenuExtractDataAocRomFS",
+ "Translations": {
+ "ar_SA": "",
+ "de_DE": "",
+ "el_GR": "",
+ "en_US": "DLC RomFS",
+ "es_ES": "",
+ "fr_FR": "RomFS de DLC",
+ "he_IL": "",
+ "it_IT": "",
+ "ja_JP": "",
+ "ko_KR": "",
+ "no_NO": "",
+ "pl_PL": "",
+ "pt_BR": "",
+ "ru_RU": "",
+ "sv_SE": "",
+ "th_TH": "",
+ "tr_TR": "",
+ "uk_UA": "",
+ "zh_CN": "",
+ "zh_TW": ""
+ }
+ },
+ {
+ "ID": "GameListContextMenuExtractDataAocRomFSToolTip",
+ "Translations": {
+ "ar_SA": "",
+ "de_DE": "",
+ "el_GR": "",
+ "en_US": "Extract the RomFS from a selected DLC file",
+ "es_ES": "",
+ "fr_FR": "Extraire les RomFS d'un fichier DLC choisi",
+ "he_IL": "",
+ "it_IT": "",
+ "ja_JP": "",
+ "ko_KR": "",
+ "no_NO": "Pakk ut RomFS filene fra valgt DLC fil",
+ "pl_PL": "",
+ "pt_BR": "",
+ "ru_RU": "",
+ "sv_SE": "",
+ "th_TH": "",
+ "tr_TR": "",
+ "uk_UA": "",
+ "zh_CN": "",
+ "zh_TW": ""
+ }
+ },
{
"ID": "GameListContextMenuExtractDataLogo",
"Translations": {
@@ -7572,6 +7622,31 @@
"zh_TW": "陀螺儀無感帶:"
}
},
+ {
+ "ID": "ControllerSettingsLedColor",
+ "Translations": {
+ "ar_SA": "",
+ "de_DE": "",
+ "el_GR": "",
+ "en_US": "Custom LED",
+ "es_ES": "",
+ "fr_FR": "",
+ "he_IL": "",
+ "it_IT": "",
+ "ja_JP": "",
+ "ko_KR": "",
+ "no_NO": "",
+ "pl_PL": "",
+ "pt_BR": "",
+ "ru_RU": "",
+ "sv_SE": "",
+ "th_TH": "",
+ "tr_TR": "",
+ "uk_UA": "",
+ "zh_CN": "",
+ "zh_TW": ""
+ }
+ },
{
"ID": "ControllerSettingsSave",
"Translations": {
@@ -22896,6 +22971,31 @@
"zh_CN": "什么都没有",
"zh_TW": "無法啟動 (Nothing)"
}
+ },
+ {
+ "ID": "ExtractAocListHeader",
+ "Translations": {
+ "ar_SA": "",
+ "de_DE": "",
+ "el_GR": "",
+ "en_US": "Select a DLC to Extract",
+ "es_ES": "",
+ "fr_FR": "Choisissez un DLC à extraire",
+ "he_IL": "",
+ "it_IT": "",
+ "ja_JP": "",
+ "ko_KR": "",
+ "no_NO": "Velg en DLC og hente ut",
+ "pl_PL": "",
+ "pt_BR": "",
+ "ru_RU": "",
+ "sv_SE": "",
+ "th_TH": "",
+ "tr_TR": "",
+ "uk_UA": "",
+ "zh_CN": "",
+ "zh_TW": ""
+ }
}
]
}
diff --git a/src/Ryujinx/Common/ApplicationHelper.cs b/src/Ryujinx/Common/ApplicationHelper.cs
index 88a991a3d..e5b4da728 100644
--- a/src/Ryujinx/Common/ApplicationHelper.cs
+++ b/src/Ryujinx/Common/ApplicationHelper.cs
@@ -1,6 +1,6 @@
-using Avalonia.Controls.Notifications;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
+using Gommon;
using LibHac;
using LibHac.Account;
using LibHac.Common;
@@ -15,6 +15,7 @@ using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
+using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common.Helper;
using Ryujinx.Common.Logging;
@@ -296,13 +297,13 @@ namespace Ryujinx.Ava.Common
extractorThread.Start();
}
- public static void ExtractAoc(string destination, NcaSectionType ncaSectionType, string updateFilePath, string updateName)
+ public static void ExtractAoc(string destination, string updateFilePath, string updateName)
{
var cancellationToken = new CancellationTokenSource();
UpdateWaitWindow waitingDialog = new(
RyujinxApp.FormatTitle(LocaleKeys.DialogNcaExtractionTitle),
- LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogNcaExtractionMessage, ncaSectionType, Path.GetFileName(updateFilePath)),
+ LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogNcaExtractionMessage, NcaSectionType.Data, Path.GetFileName(updateFilePath)),
cancellationToken);
Thread extractorThread = new(() =>
@@ -352,7 +353,7 @@ namespace Ryujinx.Ava.Common
? IntegrityCheckLevel.ErrorOnInvalid
: IntegrityCheckLevel.None;
- int index = Nca.GetSectionIndexFromType(ncaSectionType, publicDataNca.Header.ContentType);
+ int index = Nca.GetSectionIndexFromType(NcaSectionType.Data, publicDataNca.Header.ContentType);
try
{
@@ -410,44 +411,35 @@ namespace Ryujinx.Ava.Common
}
})
{
- Name = "GUI.NcaSectionExtractorThread",
+ Name = "GUI.AocExtractorThread",
IsBackground = true,
};
extractorThread.Start();
}
- public static async Task ExtractAoc(IStorageProvider storageProvider, NcaSectionType ncaSectionType,
- string updateFilePath, string updateName)
+ public static async Task ExtractAoc(IStorageProvider storageProvider, string updateFilePath, string updateName)
{
- var result = await storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
+ Optional result = await storageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions
{
- Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle],
- AllowMultiple = false,
+ Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle]
});
- if (result.Count == 0)
- {
- return;
- }
-
- ExtractAoc(result[0].Path.LocalPath, ncaSectionType, updateFilePath, updateName);
+ if (!result.HasValue) return;
+
+ ExtractAoc(result.Value.Path.LocalPath, updateFilePath, updateName);
}
public static async Task ExtractSection(IStorageProvider storageProvider, NcaSectionType ncaSectionType, string titleFilePath, string titleName, int programIndex = 0)
{
- var result = await storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
+ Optional result = await storageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions
{
- Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle],
- AllowMultiple = false,
+ Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle]
});
- if (result.Count == 0)
- {
- return;
- }
+ if (!result.HasValue) return;
- ExtractSection(result[0].Path.LocalPath, ncaSectionType, titleFilePath, titleName, programIndex);
+ ExtractSection(result.Value.Path.LocalPath, ncaSectionType, titleFilePath, titleName, programIndex);
}
public static (Result? result, bool canceled) CopyDirectory(FileSystemClient fs, string sourcePath, string destPath, CancellationToken token)
diff --git a/src/Ryujinx/Common/Models/DownloadableContentModel.cs b/src/Ryujinx/Common/Models/DownloadableContentModel.cs
index ad9934bd2..de7a334ee 100644
--- a/src/Ryujinx/Common/Models/DownloadableContentModel.cs
+++ b/src/Ryujinx/Common/Models/DownloadableContentModel.cs
@@ -6,7 +6,7 @@ namespace Ryujinx.Ava.Common.Models
public bool IsBundled { get; } = System.IO.Path.GetExtension(ContainerPath)?.ToLower() == ".xci";
public string FileName => System.IO.Path.GetFileName(ContainerPath);
- public string TitleIdStr => TitleId.ToString("x16");
+ public string TitleIdStr => TitleId.ToString("x16").ToUpper();
public ulong TitleIdBase => TitleId & ~0x1FFFUL;
}
}
diff --git a/src/Ryujinx/Headless/Options.cs b/src/Ryujinx/Headless/Options.cs
index 0d7e46285..f527e9811 100644
--- a/src/Ryujinx/Headless/Options.cs
+++ b/src/Ryujinx/Headless/Options.cs
@@ -149,7 +149,7 @@ namespace Ryujinx.Headless
IgnoreMissingServices = configurationState.System.IgnoreMissingServices;
if (NeedsOverride(nameof(IgnoreControllerApplet)))
- IgnoreControllerApplet = configurationState.IgnoreApplet;
+ IgnoreControllerApplet = configurationState.System.IgnoreApplet;
return;
diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs
index b9aa402c5..cf0e6f7e6 100644
--- a/src/Ryujinx/Program.cs
+++ b/src/Ryujinx/Program.cs
@@ -230,13 +230,16 @@ namespace Ryujinx.Ava
internal static void PrintSystemInfo()
{
Logger.Notice.Print(LogClass.Application, $"{RyujinxApp.FullAppName} Version: {Version}");
+ Logger.Notice.Print(LogClass.Application, $".NET Runtime: {RuntimeInformation.FrameworkDescription}");
SystemInfo.Gather().Print();
- var enabledLogLevels = Logger.GetEnabledLevels().ToArray();
-
- Logger.Notice.Print(LogClass.Application, $"Logs Enabled: {(enabledLogLevels.Length is 0
- ? ""
- : enabledLogLevels.JoinToString(", "))}");
+ Logger.Notice.Print(LogClass.Application, $"Logs Enabled: {
+ Logger.GetEnabledLevels()
+ .FormatCollection(
+ x => x.ToString(),
+ separator: ", ",
+ emptyCollectionFallback: "")
+ }");
Logger.Notice.Print(LogClass.Application,
AppDataManager.Mode == AppDataManager.LaunchMode.Custom
diff --git a/src/Ryujinx/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj
index 8c72d5a3c..c6a4840d2 100644
--- a/src/Ryujinx/Ryujinx.csproj
+++ b/src/Ryujinx/Ryujinx.csproj
@@ -123,12 +123,13 @@
MSBuild:Compile
+
-
+
@@ -150,6 +151,7 @@
+
@@ -173,10 +175,5 @@
-
-
- UserSelectorDialog.axaml
- Code
-
-
+
diff --git a/src/Ryujinx/RyujinxApp.axaml b/src/Ryujinx/RyujinxApp.axaml
index aca69645a..636535ea4 100644
--- a/src/Ryujinx/RyujinxApp.axaml
+++ b/src/Ryujinx/RyujinxApp.axaml
@@ -16,5 +16,6 @@
+
diff --git a/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs b/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs
index a2cac35c7..d2fad58ac 100644
--- a/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs
+++ b/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs
@@ -6,7 +6,6 @@ using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
-using Ryujinx.Ava.UI.ViewModels.Input;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common;
@@ -42,7 +41,7 @@ namespace Ryujinx.Ava.UI.Applet
bool okPressed = false;
- if (ConfigurationState.Instance.IgnoreApplet)
+ if (ConfigurationState.Instance.System.IgnoreApplet)
return false;
Dispatcher.UIThread.InvokeAsync(async () =>
diff --git a/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml b/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml
index 7708936ca..9fed95aa7 100644
--- a/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml
+++ b/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml
@@ -106,6 +106,10 @@
Click="ExtractApplicationRomFs_Click"
Header="{ext:Locale GameListContextMenuExtractDataRomFS}"
ToolTip.Tip="{ext:Locale GameListContextMenuExtractDataRomFSToolTip}" />
+
+ IsVisible="{Binding !EnableNonGameRunningControls}">
-
-
-
-
+
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
+
diff --git a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs
index 521460012..9a63c022d 100644
--- a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs
+++ b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs
@@ -37,26 +37,20 @@ namespace Ryujinx.Ava.UI.Views.Main
ToggleFileTypesMenuItem.ItemsSource = GenerateToggleFileTypeItems();
ChangeLanguageMenuItem.ItemsSource = GenerateLanguageMenuItems();
- MiiAppletMenuItem.Command = new AsyncRelayCommand(OpenMiiApplet);
- CloseRyujinxMenuItem.Command = new RelayCommand(CloseWindow);
- OpenSettingsMenuItem.Command = new AsyncRelayCommand(OpenSettings);
- PauseEmulationMenuItem.Command = new RelayCommand(() => ViewModel.AppHost?.Pause());
- ResumeEmulationMenuItem.Command = new RelayCommand(() => ViewModel.AppHost?.Resume());
- StopEmulationMenuItem.Command = new AsyncRelayCommand(() => ViewModel.AppHost?.ShowExitPrompt().OrCompleted());
- CheatManagerMenuItem.Command = new AsyncRelayCommand(async () =>
- {
- try
- {
- await OpenCheatManagerForCurrentApp();
- } catch {}
- });
- InstallFileTypesMenuItem.Command = new AsyncRelayCommand(InstallFileTypes);
- UninstallFileTypesMenuItem.Command = new AsyncRelayCommand(UninstallFileTypes);
- XciTrimmerMenuItem.Command = new AsyncRelayCommand(() => XCITrimmerWindow.Show(ViewModel));
- AboutWindowMenuItem.Command = new AsyncRelayCommand(AboutWindow.Show);
- CompatibilityListMenuItem.Command = new AsyncRelayCommand(CompatibilityList.Show);
+ MiiAppletMenuItem.Command = Commands.Create(OpenMiiApplet);
+ CloseRyujinxMenuItem.Command = Commands.Create(CloseWindow);
+ OpenSettingsMenuItem.Command = Commands.Create(OpenSettings);
+ PauseEmulationMenuItem.Command = Commands.Create(() => ViewModel.AppHost?.Pause());
+ ResumeEmulationMenuItem.Command = Commands.Create(() => ViewModel.AppHost?.Resume());
+ StopEmulationMenuItem.Command = Commands.Create(() => ViewModel.AppHost?.ShowExitPrompt().OrCompleted());
+ CheatManagerMenuItem.Command = Commands.CreateSilentFail(OpenCheatManagerForCurrentApp);
+ InstallFileTypesMenuItem.Command = Commands.Create(InstallFileTypes);
+ UninstallFileTypesMenuItem.Command = Commands.Create(UninstallFileTypes);
+ XciTrimmerMenuItem.Command = Commands.Create(XCITrimmerWindow.Show);
+ AboutWindowMenuItem.Command = Commands.Create(AboutWindow.Show);
+ CompatibilityListMenuItem.Command = Commands.Create(CompatibilityList.Show);
- UpdateMenuItem.Command = new AsyncRelayCommand(async () =>
+ UpdateMenuItem.Command = Commands.Create(async () =>
{
if (Updater.CanUpdate(true))
await Updater.BeginUpdateAsync(true);
@@ -64,12 +58,12 @@ namespace Ryujinx.Ava.UI.Views.Main
FaqMenuItem.Command =
SetupGuideMenuItem.Command =
- LdnGuideMenuItem.Command = new RelayCommand(OpenHelper.OpenUrl);
+ LdnGuideMenuItem.Command = Commands.Create(OpenHelper.OpenUrl);
WindowSize720PMenuItem.Command =
WindowSize1080PMenuItem.Command =
WindowSize1440PMenuItem.Command =
- WindowSize2160PMenuItem.Command = new RelayCommand(ChangeWindowSize);
+ WindowSize2160PMenuItem.Command = Commands.Create(ChangeWindowSize);
}
private IEnumerable GenerateToggleFileTypeItems() =>
diff --git a/src/Ryujinx/UI/Views/Main/MainViewControls.axaml b/src/Ryujinx/UI/Views/Main/MainViewControls.axaml
index 1c6895db1..cdc66a138 100644
--- a/src/Ryujinx/UI/Views/Main/MainViewControls.axaml
+++ b/src/Ryujinx/UI/Views/Main/MainViewControls.axaml
@@ -105,6 +105,12 @@
GroupName="Sort"
IsChecked="{Binding IsSortedByTitle, Mode=OneTime}"
Tag="Title" />
+
+ Text="Highly specific hacks & tricks to alleviate performance issues, crashing, or freezing. Will cause issues." />
+ ToolTip.Tip="{ext:Locale AddGameDirTooltip}">
@@ -168,8 +167,7 @@
Grid.Column="1"
MinWidth="90"
Margin="10,0,0,0"
- ToolTip.Tip="{ext:Locale AddAutoloadDirTooltip}"
- Click="AddAutoloadDirButton_OnClick">
+ ToolTip.Tip="{ext:Locale AddAutoloadDirTooltip}">
diff --git a/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml.cs b/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml.cs
index 3532e1855..9707b3193 100644
--- a/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml.cs
+++ b/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml.cs
@@ -1,12 +1,17 @@
+using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Platform.Storage;
using Avalonia.VisualTree;
+using Gommon;
+using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
+using Ryujinx.Ava.Utilities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Views.Settings
{
@@ -18,31 +23,39 @@ namespace Ryujinx.Ava.UI.Views.Settings
{
InitializeComponent();
ShowTitleBarBox.IsVisible = OperatingSystem.IsWindows();
+ AddGameDirButton.Command =
+ Commands.Create(() => AddDirButton(GameDirPathBox, ViewModel.GameDirectories, true));
+ AddAutoloadDirButton.Command =
+ Commands.Create(() => AddDirButton(AutoloadDirPathBox, ViewModel.AutoloadDirectories, false));
}
- private async void AddGameDirButton_OnClick(object sender, RoutedEventArgs e)
+ private async Task AddDirButton(TextBox addDirBox, AvaloniaList directories, bool isGameList)
{
- string path = GameDirPathBox.Text;
+ string path = addDirBox.Text;
- if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path) && !ViewModel.GameDirectories.Contains(path))
+ if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path) && !directories.Contains(path))
{
- ViewModel.GameDirectories.Add(path);
- ViewModel.GameDirectoryChanged = true;
+ directories.Add(path);
+
+ addDirBox.Clear();
+
+ if (isGameList)
+ ViewModel.GameDirectoryChanged = true;
+ else
+ ViewModel.AutoloadDirectoryChanged = true;
}
else
{
- if (this.GetVisualRoot() is Window window)
- {
- var result = await window.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
- {
- AllowMultiple = false,
- });
+ Optional folder = await RyujinxApp.MainWindow.ViewModel.StorageProvider.OpenSingleFolderPickerAsync();
- if (result.Count > 0)
- {
- ViewModel.GameDirectories.Add(result[0].Path.LocalPath);
+ if (folder.HasValue)
+ {
+ directories.Add(folder.Value.Path.LocalPath);
+
+ if (isGameList)
ViewModel.GameDirectoryChanged = true;
- }
+ else
+ ViewModel.AutoloadDirectoryChanged = true;
}
}
}
@@ -63,33 +76,6 @@ namespace Ryujinx.Ava.UI.Views.Settings
}
}
- private async void AddAutoloadDirButton_OnClick(object sender, RoutedEventArgs e)
- {
- string path = AutoloadDirPathBox.Text;
-
- if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path) && !ViewModel.AutoloadDirectories.Contains(path))
- {
- ViewModel.AutoloadDirectories.Add(path);
- ViewModel.AutoloadDirectoryChanged = true;
- }
- else
- {
- if (this.GetVisualRoot() is Window window)
- {
- var result = await window.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
- {
- AllowMultiple = false,
- });
-
- if (result.Count > 0)
- {
- ViewModel.AutoloadDirectories.Add(result[0].Path.LocalPath);
- ViewModel.AutoloadDirectoryChanged = true;
- }
- }
- }
- }
-
private void RemoveAutoloadDirButton_OnClick(object sender, RoutedEventArgs e)
{
int oldIndex = AutoloadDirsList.SelectedIndex;
diff --git a/src/Ryujinx/UI/Windows/DownloadableContentManagerWindow.axaml b/src/Ryujinx/UI/Windows/DownloadableContentManagerWindow.axaml
index 8270e070a..e2c4fe16e 100644
--- a/src/Ryujinx/UI/Windows/DownloadableContentManagerWindow.axaml
+++ b/src/Ryujinx/UI/Windows/DownloadableContentManagerWindow.axaml
@@ -7,7 +7,6 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
- xmlns:pi="using:Projektanker.Icons.Avalonia"
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
xmlns:models="clr-namespace:Ryujinx.Ava.Common.Models"
Width="500"
@@ -15,9 +14,6 @@
mc:Ignorable="d"
x:DataType="viewModels:DownloadableContentManagerViewModel"
Focusable="True">
-
-
-
-
+
@@ -113,22 +109,13 @@
Margin="10 0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
- Text="{Binding TitleId}" />
+ Text="{Binding TitleIdStr}" />
-