Updated Zh-CN Simplified Chinese. #737

Merged
shinyoyo merged 4 commits from master into master 2025-03-11 06:35:19 +00:00
174 changed files with 2020 additions and 1728 deletions
Showing only changes of commit b248b3a769 - Show all commits

View File

@ -631,6 +631,7 @@
010030D012FF6000,"Bus Driver Simulator",,playable,2022-10-17 13:55:27
0100A9101418C000,"BUSTAFELLOWS",nvdec,playable,2020-10-17 20:04:41
0100177005C8A000,"BUTCHER",,playable,2021-01-11 18:50:17
01008c2019598000,"Bluey: The Videogame",,playable,2025-02-11 04:38:00
01000B900D8B0000,"Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda",slow;nvdec,playable,2024-04-01 22:43:40
010065700EE06000,"Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda Demo",demo;gpu;nvdec,ingame,2021-02-14 21:48:15
01005C00117A8000,"Café Enchanté",,playable,2020-11-13 14:54:25
@ -1382,6 +1383,9 @@
0100763015C2E000,"Gunvolt Chronicles: Luminous Avenger iX 2",crash;Needs Update,nothing,2022-04-29 15:34:34
01002C8018554000,"Gurimugurimoa OnceMore Demo",,playable,2022-07-29 22:07:31
0100AC601DCA8000,"GYLT",crash,ingame,2024-03-18 20:16:51
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
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
@ -2729,7 +2733,7 @@
0100C2500FC20000,"Splatoon™ 3",ldn-works;opengl-backend-bug;LAN;amd-vendor-bug,playable,2024-08-04 23:49:11
0100BA0018500000,"Splatoon™ 3: Splatfest World Premiere",gpu;online-broken;demo,ingame,2022-09-19 03:17:12
010062800D39C000,"SpongeBob SquarePants: Battle for Bikini Bottom - Rehydrated",online-broken;UE4;ldn-broken;vulkan-backend-bug,playable,2023-08-01 19:29:34
01009FB0172F4000,"SpongeBob SquarePants: The Cosmic Shake",gpu;UE4,ingame,2023-08-01 19:29:53
01009FB0172F4000,"SpongeBob SquarePants: The Cosmic Shake",gpu;UE4,ingame,2024-03-04 16:35:00
010097C01336A000,"Spooky Chase",,playable,2022-11-04 12:17:44
0100C6100D75E000,"Spooky Ghosts Dot Com",,playable,2021-06-15 15:16:11
0100DE9005170000,"Sports Party",nvdec,playable,2021-03-05 13:40:42

1 title_id game_name labels status last_updated
631 010030D012FF6000 Bus Driver Simulator playable 2022-10-17 13:55:27
632 0100A9101418C000 BUSTAFELLOWS nvdec playable 2020-10-17 20:04:41
633 0100177005C8A000 BUTCHER playable 2021-01-11 18:50:17
634 01008c2019598000 Bluey: The Videogame playable 2025-02-11 04:38:00
635 01008c2019598000 01000B900D8B0000 Bluey: The Videogame Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda slow;nvdec playable 2025-02-11 04:38:00 2024-04-01 22:43:40
636 01000B900D8B0000 010065700EE06000 Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda Demo slow;nvdec demo;gpu;nvdec playable ingame 2024-04-01 22:43:40 2021-02-14 21:48:15
637 010065700EE06000 01005C00117A8000 Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda Demo Café Enchanté demo;gpu;nvdec ingame playable 2021-02-14 21:48:15 2020-11-13 14:54:25
1383 01008C800E654000 0100763015C2E000 Gunvolt Chronicles Luminous Avenger iX - Retail Version Gunvolt Chronicles: Luminous Avenger iX 2 crash;Needs Update playable nothing 2020-06-16 22:47:07 2022-04-29 15:34:34
1384 0100763015C2E000 01002C8018554000 Gunvolt Chronicles: Luminous Avenger iX 2 Gurimugurimoa OnceMore Demo crash;Needs Update nothing playable 2022-04-29 15:34:34 2022-07-29 22:07:31
1385 01002C8018554000 0100AC601DCA8000 Gurimugurimoa OnceMore Demo GYLT crash playable ingame 2022-07-29 22:07:31 2024-03-18 20:16:51
1386 0100c3c012718000 Grand Theft Auto: III – The Definitive Edition gpu;UE4 ingame 2022-10-31 20:13:52
1387 0100182014022000 Grand Theft Auto: Vice City – The Definitive Edition gpu;UE4 ingame 2022-10-31 20:13:52
1388 010065a014024000 Grand Theft Auto: San Andreas – The Definitive Edition gpu;UE4 ingame 2022-10-31 20:13:52
1389 0100AC601DCA8000 0100822012D76000 GYLT HAAK crash gpu ingame 2024-03-18 20:16:51 2023-02-19 14:31:05
1390 0100c3c012718000 01007E100EFA8000 Grand Theft Auto: III – The Definitive Edition Habroxia gpu;UE4 ingame playable 2022-10-31 20:13:52 2020-06-16 23:04:42
1391 0100182014022000 0100535012974000 Grand Theft Auto: Vice City – The Definitive Edition Hades gpu;UE4 vulkan ingame playable 2022-10-31 20:13:52 2022-10-05 10:45:21
2733 0100BD400DC52000 0100C2500FC20000 Spiritfarer Splatoon™ 3 gpu ldn-works;opengl-backend-bug;LAN;amd-vendor-bug ingame playable 2022-10-06 16:31:38 2024-08-04 23:49:11
2734 01009D60080B4000 0100BA0018500000 SpiritSphere DX Splatoon™ 3: Splatfest World Premiere gpu;online-broken;demo playable ingame 2021-07-03 23:37:49 2022-09-19 03:17:12
2735 010042700E3FC000 010062800D39C000 Spitlings SpongeBob SquarePants: Battle for Bikini Bottom - Rehydrated online-broken online-broken;UE4;ldn-broken;vulkan-backend-bug playable 2022-10-06 16:42:39 2023-08-01 19:29:34
2736 01003BC0000A0000 01009FB0172F4000 Splatoon™ 2 SpongeBob SquarePants: The Cosmic Shake ldn-works;LAN gpu;UE4 playable ingame 2024-07-12 19:11:15 2024-03-04 16:35:00
2737 0100C2500FC20000 010097C01336A000 Splatoon™ 3 Spooky Chase ldn-works;opengl-backend-bug;LAN;amd-vendor-bug playable 2024-08-04 23:49:11 2022-11-04 12:17:44
2738 0100BA0018500000 0100C6100D75E000 Splatoon™ 3: Splatfest World Premiere Spooky Ghosts Dot Com gpu;online-broken;demo ingame playable 2022-09-19 03:17:12 2021-06-15 15:16:11
2739 010062800D39C000 0100DE9005170000 SpongeBob SquarePants: Battle for Bikini Bottom - Rehydrated Sports Party online-broken;UE4;ldn-broken;vulkan-backend-bug nvdec playable 2023-08-01 19:29:34 2021-03-05 13:40:42

View File

@ -9,7 +9,8 @@ namespace Ryujinx.Common.Configuration
public enum DirtyHack : byte
{
Xc2MenuSoftlockFix = 1,
ShaderTranslationDelay = 2
// ShaderTranslationDelay = 2
NifmServiceDisableIsAnyInternetRequestAccepted = 3
}
public readonly struct EnabledDirtyHack(DirtyHack hack, int value)

View File

@ -1,7 +1,4 @@
using Gommon;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Helper;
using System;
using System.Linq;
namespace Ryujinx.Common

View File

@ -1,4 +1,6 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL.Multithreading;
using System;
using System.Threading;
@ -10,6 +12,20 @@ namespace Ryujinx.Graphics.GAL
bool PreferThreading { get; }
public IRenderer TryMakeThreaded(BackendThreading backendThreading = BackendThreading.Auto)
{
if (backendThreading is BackendThreading.On ||
(backendThreading is BackendThreading.Auto && PreferThreading))
{
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({backendThreading}): True");
return new ThreadedRenderer(this);
}
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({backendThreading}): False");
return this;
}
IPipeline Pipeline { get; }
IWindow Window { get; }

View File

@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm
private readonly LanDiscovery _lanDiscovery;
public LdnMitmClient(HLEConfiguration config)
public LdnMitmClient(HleConfiguration config)
{
UnicastIPAddressInformation localIpInterface = NetworkHelpers.GetLocalInterface(config.MultiplayerLanInterfaceId).Item2;

View File

@ -51,13 +51,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
private string _passphrase;
private byte[] _gameVersion = new byte[0x10];
private readonly HLEConfiguration _config;
private readonly HleConfiguration _config;
public event EventHandler<NetworkChangeEventArgs> NetworkChange;
public ProxyConfig Config { get; private set; }
public LdnMasterProxyClient(string address, int port, HLEConfiguration config) : base(address, port)
public LdnMasterProxyClient(string address, int port, HleConfiguration config) : base(address, port)
{
if (ProxyHelpers.SupportsNoDelay())
{

View File

@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm
// CreateGeneralServiceOld() -> object<nn::nifm::detail::IGeneralService>
public ResultCode CreateGeneralServiceOld(ServiceCtx context)
{
MakeObject(context, new IGeneralService());
MakeObject(context, new IGeneralService(context));
return ResultCode.Success;
}
@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm
// CreateGeneralService(u64, pid) -> object<nn::nifm::detail::IGeneralService>
public ResultCode CreateGeneralService(ServiceCtx context)
{
MakeObject(context, new IGeneralService());
MakeObject(context, new IGeneralService(context));
return ResultCode.Success;
}

View File

@ -1,4 +1,5 @@
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.HOS.Services.Nifm.StaticService.GeneralService;
@ -17,12 +18,12 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
private UnicastIPAddressInformation _targetAddressInfoCache = null;
private string _cacheChosenInterface = null;
public IGeneralService()
public IGeneralService(ServiceCtx context)
{
_generalServiceDetail = new GeneralServiceDetail
{
ClientId = GeneralServiceManager.Count,
IsAnyInternetRequestAccepted = true, // NOTE: Why not accept any internet request?
IsAnyInternetRequestAccepted = !context.Device.DirtyHacks.IsEnabled(DirtyHack.NifmServiceDisableIsAnyInternetRequestAccepted), // NOTE: Why not accept any internet request?
};
NetworkChange.NetworkAddressChanged += LocalInterfaceCacheHandler;

View File

@ -1,6 +1,5 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy;
using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@ -15,55 +15,55 @@ namespace Ryujinx.HLE
/// <summary>
/// HLE configuration.
/// </summary>
public class HLEConfiguration
public class HleConfiguration
{
/// <summary>
/// The virtual file system used by the FS service.
/// </summary>
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
internal readonly VirtualFileSystem VirtualFileSystem;
internal VirtualFileSystem VirtualFileSystem { get; private set; }
/// <summary>
/// The manager for handling a LibHac Horizon instance.
/// </summary>
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
internal readonly LibHacHorizonManager LibHacHorizonManager;
internal LibHacHorizonManager LibHacHorizonManager { get; private set; }
/// <summary>
/// The account manager used by the account service.
/// </summary>
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
internal readonly AccountManager AccountManager;
internal AccountManager AccountManager { get; private set; }
/// <summary>
/// The content manager used by the NCM service.
/// </summary>
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
internal readonly ContentManager ContentManager;
internal ContentManager ContentManager { get; private set; }
/// <summary>
/// The persistent information between run for multi-application capabilities.
/// </summary>
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
public readonly UserChannelPersistence UserChannelPersistence;
public UserChannelPersistence UserChannelPersistence { get; private set; }
/// <summary>
/// The GPU renderer to use for all GPU operations.
/// </summary>
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
internal readonly IRenderer GpuRenderer;
internal IRenderer GpuRenderer { get; private set; }
/// <summary>
/// The audio device driver to use for all audio operations.
/// </summary>
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
internal readonly IHardwareDeviceDriver AudioDeviceDriver;
internal IHardwareDeviceDriver AudioDeviceDriver { get; private set; }
/// <summary>
/// The handler for various UI related operations needed outside of HLE.
/// </summary>
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
internal readonly IHostUIHandler HostUIHandler;
internal IHostUIHandler HostUIHandler { get; private set; }
/// <summary>
/// Control the memory configuration used by the emulation context.
@ -195,15 +195,7 @@ namespace Ryujinx.HLE
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
public EnabledDirtyHack[] Hacks { internal get; set; }
public HLEConfiguration(VirtualFileSystem virtualFileSystem,
LibHacHorizonManager libHacHorizonManager,
ContentManager contentManager,
AccountManager accountManager,
UserChannelPersistence userChannelPersistence,
IRenderer gpuRenderer,
IHardwareDeviceDriver audioDeviceDriver,
MemoryConfiguration memoryConfiguration,
IHostUIHandler hostUIHandler,
public HleConfiguration(MemoryConfiguration memoryConfiguration,
SystemLanguage systemLanguage,
RegionCode region,
VSyncMode vSyncMode,
@ -227,15 +219,7 @@ namespace Ryujinx.HLE
int customVSyncInterval,
EnabledDirtyHack[] dirtyHacks = null)
{
VirtualFileSystem = virtualFileSystem;
LibHacHorizonManager = libHacHorizonManager;
AccountManager = accountManager;
ContentManager = contentManager;
UserChannelPersistence = userChannelPersistence;
GpuRenderer = gpuRenderer;
AudioDeviceDriver = audioDeviceDriver;
MemoryConfiguration = memoryConfiguration;
HostUIHandler = hostUIHandler;
SystemLanguage = systemLanguage;
Region = region;
VSyncMode = vSyncMode;
@ -259,5 +243,30 @@ namespace Ryujinx.HLE
MultiplayerLdnServer = multiplayerLdnServer;
Hacks = dirtyHacks ?? [];
}
/// <summary>
/// Set the pre-configured services to use for this <see cref="HleConfiguration"/> instance.
/// </summary>
public HleConfiguration Configure(
VirtualFileSystem virtualFileSystem,
LibHacHorizonManager libHacHorizonManager,
ContentManager contentManager,
AccountManager accountManager,
UserChannelPersistence userChannelPersistence,
IRenderer gpuRenderer,
IHardwareDeviceDriver audioDeviceDriver,
IHostUIHandler hostUIHandler
)
{
VirtualFileSystem = virtualFileSystem;
LibHacHorizonManager = libHacHorizonManager;
AccountManager = accountManager;
ContentManager = contentManager;
UserChannelPersistence = userChannelPersistence;
GpuRenderer = gpuRenderer;
AudioDeviceDriver = audioDeviceDriver;
HostUIHandler = hostUIHandler;
return this;
}
}
}

View File

@ -20,7 +20,7 @@ namespace Ryujinx.HLE
{
public static Switch Shared { get; private set; }
public HLEConfiguration Configuration { get; }
public HleConfiguration Configuration { get; }
public IHardwareDeviceDriver AudioDeviceDriver { get; }
public MemoryBlock Memory { get; }
public GpuContext Gpu { get; }
@ -44,7 +44,7 @@ namespace Ryujinx.HLE
public DirtyHacks DirtyHacks { get; }
public Switch(HLEConfiguration configuration)
public Switch(HleConfiguration configuration)
{
ArgumentNullException.ThrowIfNull(configuration.GpuRenderer);
ArgumentNullException.ThrowIfNull(configuration.AudioDeviceDriver);
@ -94,16 +94,20 @@ namespace Ryujinx.HLE
Gpu.GPFifo.DispatchCalls();
}
public void IncrementCustomVSyncInterval()
public int IncrementCustomVSyncInterval()
{
CustomVSyncInterval += 1;
UpdateVSyncInterval();
return CustomVSyncInterval;
}
public void DecrementCustomVSyncInterval()
public int DecrementCustomVSyncInterval()
{
CustomVSyncInterval -= 1;
UpdateVSyncInterval();
return CustomVSyncInterval;
}
public void UpdateVSyncInterval()

View File

@ -440,7 +440,7 @@
<x:Double x:Key="ControlContentThemeFontSize">13</x:Double>
<x:Double x:Key="MenuItemHeight">26</x:Double>
<x:Double x:Key="TabItemMinHeight">28</x:Double>
<x:Double x:Key="ContentDialogMaxWidth">900</x:Double>
<x:Double x:Key="ContentDialogMaxWidth">700</x:Double>
<x:Double x:Key="ContentDialogMaxHeight">756</x:Double>
</Styles.Resources>
</Styles>

View File

@ -3823,28 +3823,53 @@
}
},
{
"ID": "SettingsTabGeneralShowTitleBar",
"ID": "SettingsTabGeneralShowOldUI",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Show Title Bar (Requires restart)",
"es_ES": "Mostrar Barra de Título (Requiere reinicio)",
"fr_FR": "Afficher Barre de Titre (Nécessite redémarrage)",
"en_US": "Show Original UI Style (Requires restart)",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "Mostra barra del titolo (Richiede il riavvio)",
"it_IT": "",
"ja_JP": "",
"ko_KR": "제목 표시줄 표시(다시 시작해야 함)",
"no_NO": "Vis tittellinje (krever omstart)",
"ko_KR": "",
"no_NO": "Vis original UI-stil (krever omstart)",
"pl_PL": "",
"pt_BR": "Mostrar Barra de Título (Requer reinicialização)",
"ru_RU": "Показать строку заголовка (требуется перезапуск)",
"sv_SE": "Visa titelrad (kräver omstart)",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "Показувати рядок заголовка (Потрібен перезапуск)",
"zh_CN": "显示标题栏 (需要重启)",
"zh_TW": "顯示「標題列」 (需要重新開啟Ryujinx)"
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{
"ID": "SettingsTabGeneralShowOldUIToolTip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Show the older Avalonia Ryujinx UI reminiscent of Ryujinx 1.1.1403. This is enabled by default on platforms that are not Windows.\nThe classic-style title bar is back and major window layout reworkings are reversed; such as the settings navigation placement above this tooltip.",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "Vis det eldre Avalonia Ryujinx-grensesnittet som minner om Ryujinx 1.1.1403. Dette er aktivert som standard på plattformer som ikke er Windows.\nTittellinjen i klassisk stil er tilbake, og store omarbeidinger av vindusoppsettet er reversert, for eksempel plasseringen av innstillingsnavigasjonen over dette verktøytipset.",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{
@ -23822,6 +23847,31 @@
"zh_TW": "上次更新時間: {0}"
}
},
{
"ID": "CompatibilityListTitle",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Compatibility List - {0} entries",
"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": "CompatibilityListWarning",
"Translations": {
@ -23872,6 +23922,31 @@
"zh_TW": "搜尋相容性列表紀錄..."
}
},
{
"ID": "CompatibilityListSearchBoxWatermarkWithCount",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Search {0} compatibility entries...",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "Søk i {0} kompatibilitetsoppføringer...",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{
"ID": "CompatibilityListOpen",
"Translations": {

View File

@ -13,10 +13,10 @@ using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Common.Helper;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem;
@ -216,11 +216,7 @@ namespace Ryujinx.Ava.Common
return;
}
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
? IntegrityCheckLevel.ErrorOnInvalid
: IntegrityCheckLevel.None;
(Nca updatePatchNca, _) = mainNca.GetUpdateData(_virtualFileSystem, checkLevel, programIndex, out _);
(Nca updatePatchNca, _) = mainNca.GetUpdateData(_virtualFileSystem, ConfigurationState.Instance.System.IntegrityCheckLevel, programIndex, out _);
if (updatePatchNca is not null)
{
patchNca = updatePatchNca;

View File

@ -1,6 +1,7 @@
using Gommon;
using Ryujinx.Ava.Systems;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Common;
using Ryujinx.Common.Utilities;
using System;
@ -25,7 +26,21 @@ namespace Ryujinx.Ava.Common.Locale
public LocaleManager()
{
_localeStrings = new Dictionary<LocaleKeys, string>();
_dynamicValues = new ConcurrentDictionary<LocaleKeys, object[]>();
_dynamicValues = new ConcurrentDictionary<LocaleKeys, object[]>(new Dictionary<LocaleKeys, object[]>
{
{ LocaleKeys.DialogConfirmationTitle, [RyujinxApp.FullAppName] },
{ LocaleKeys.DialogUpdaterTitle, [RyujinxApp.FullAppName] },
{ LocaleKeys.DialogErrorTitle, [RyujinxApp.FullAppName] },
{ LocaleKeys.DialogWarningTitle, [RyujinxApp.FullAppName] },
{ LocaleKeys.DialogExitTitle, [RyujinxApp.FullAppName] },
{ LocaleKeys.DialogStopEmulationTitle, [RyujinxApp.FullAppName] },
{ LocaleKeys.RyujinxInfo, [RyujinxApp.FullAppName] },
{ LocaleKeys.RyujinxConfirm, [RyujinxApp.FullAppName] },
{ LocaleKeys.RyujinxUpdater, [RyujinxApp.FullAppName] },
{ LocaleKeys.RyujinxRebooter, [RyujinxApp.FullAppName] },
{ LocaleKeys.CompatibilityListSearchBoxWatermarkWithCount, [CompatibilityDatabase.Entries.Length] },
{ LocaleKeys.CompatibilityListTitle, [CompatibilityDatabase.Entries.Length] }
});
Load();
}
@ -44,17 +59,6 @@ namespace Ryujinx.Ava.Common.Locale
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
}
SetDynamicValues(LocaleKeys.DialogConfirmationTitle, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.DialogUpdaterTitle, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.DialogErrorTitle, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.DialogWarningTitle, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.DialogExitTitle, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.DialogStopEmulationTitle, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.RyujinxInfo, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.RyujinxConfirm, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.RyujinxUpdater, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.RyujinxRebooter, RyujinxApp.FullAppName);
}
public string this[LocaleKeys key]

View File

@ -24,4 +24,17 @@ namespace Ryujinx.Ava.Common.Markup
protected override void ConfigureBindingExtension(CompiledBindingExtension bindingExtension)
=> bindingExtension.Source = LocaleManager.Instance;
}
internal class WindowTitleExtension(LocaleKeys key, bool includeVersion) : BasicMarkupExtension<string>
{
public WindowTitleExtension(LocaleKeys key) : this(key, true)
{
}
public override string Name => "WindowTitleTranslation";
protected override string Value => RyujinxApp.FormatTitle(key, includeVersion);
protected override void ConfigureBindingExtension(CompiledBindingExtension bindingExtension)
=> bindingExtension.Source = LocaleManager.Instance;
}
}

View File

@ -1,4 +1,4 @@
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.AppLibrary;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;

View File

@ -26,9 +26,9 @@ namespace Ryujinx.Ava.Common
internal class TrimmerWindow : Ryujinx.Common.Logging.XCIFileTrimmerLog
{
private readonly XCITrimmerViewModel _viewModel;
private readonly XciTrimmerViewModel _viewModel;
public TrimmerWindow(XCITrimmerViewModel viewModel)
public TrimmerWindow(XciTrimmerViewModel viewModel)
{
_viewModel = viewModel;
}

View File

@ -2,7 +2,8 @@ using DiscordRPC;
using LibHac.Tools.FsSystem;
using Ryujinx.Audio.Backends.SDL2;
using Ryujinx.Ava;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller;
@ -11,7 +12,6 @@ using Ryujinx.Common.Configuration.Hid.Keyboard;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.GAL.Multithreading;
using Ryujinx.Graphics.OpenGL;
using Ryujinx.Graphics.Vulkan;
using Ryujinx.HLE;
@ -312,49 +312,42 @@ namespace Ryujinx.Headless
return new OpenGLRenderer();
}
private static Switch InitializeEmulationContext(WindowBase window, IRenderer renderer, Options options)
{
BackendThreading threadingMode = options.BackendThreading;
bool threadedGAL = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading);
if (threadedGAL)
{
renderer = new ThreadedRenderer(renderer);
}
HLEConfiguration configuration = new(_virtualFileSystem,
_libHacHorizonManager,
_contentManager,
_accountManager,
_userChannelPersistence,
renderer,
new SDL2HardwareDeviceDriver(),
options.DramSize,
window,
options.SystemLanguage,
options.SystemRegion,
options.VSyncMode,
!options.DisableDockedMode,
!options.DisablePTC,
options.EnableInternetAccess,
!options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
options.FsGlobalAccessLogMode,
options.SystemTimeOffset,
options.SystemTimeZone,
options.MemoryManagerMode,
options.IgnoreMissingServices,
options.AspectRatio,
options.AudioVolume,
options.UseHypervisor ?? true,
options.MultiplayerLanInterfaceId,
Common.Configuration.Multiplayer.MultiplayerMode.Disabled,
false,
string.Empty,
string.Empty,
options.CustomVSyncInterval);
return new Switch(configuration);
}
private static Switch InitializeEmulationContext(WindowBase window, IRenderer renderer, Options options) =>
new(
new HleConfiguration(
options.DramSize,
options.SystemLanguage,
options.SystemRegion,
options.VSyncMode,
!options.DisableDockedMode,
!options.DisablePTC,
options.EnableInternetAccess,
!options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
options.FsGlobalAccessLogMode,
options.SystemTimeOffset,
options.SystemTimeZone,
options.MemoryManagerMode,
options.IgnoreMissingServices,
options.AspectRatio,
options.AudioVolume,
options.UseHypervisor ?? true,
options.MultiplayerLanInterfaceId,
Common.Configuration.Multiplayer.MultiplayerMode.Disabled,
false,
string.Empty,
string.Empty,
options.CustomVSyncInterval
)
.Configure(
_virtualFileSystem,
_libHacHorizonManager,
_contentManager,
_accountManager,
_userChannelPersistence,
renderer.TryMakeThreaded(options.BackendThreading),
new SDL2HardwareDeviceDriver(),
window
)
);
}
}

View File

@ -1,7 +1,7 @@
using CommandLine;
using Gommon;
using Ryujinx.Ava;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;

View File

@ -1,6 +1,7 @@
using CommandLine;
using Gommon;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Ava.Systems.Configuration.System;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.HLE;
@ -37,7 +38,7 @@ namespace Ryujinx.Headless
EnableInternetAccess = configurationState.System.EnableInternetAccess;
if (NeedsOverride(nameof(DisableFsIntegrityChecks)))
DisableFsIntegrityChecks = configurationState.System.EnableFsIntegrityChecks;
DisableFsIntegrityChecks = !configurationState.System.EnableFsIntegrityChecks;
if (NeedsOverride(nameof(FsGlobalAccessLogMode)))
FsGlobalAccessLogMode = configurationState.System.FsGlobalAccessLogMode;
@ -58,10 +59,10 @@ namespace Ryujinx.Headless
DisableDockedMode = !configurationState.System.EnableDockedMode;
if (NeedsOverride(nameof(SystemLanguage)))
SystemLanguage = (SystemLanguage)(int)configurationState.System.Language.Value;
SystemLanguage = configurationState.System.Language.Value.ToHLE();
if (NeedsOverride(nameof(SystemRegion)))
SystemRegion = (RegionCode)(int)configurationState.System.Region.Value;
SystemRegion = configurationState.System.Region.Value.ToHLE();
if (NeedsOverride(nameof(SystemTimeZone)))
SystemTimeZone = configurationState.System.TimeZone;

View File

@ -5,10 +5,12 @@ using Gommon;
using Projektanker.Icons.Avalonia;
using Projektanker.Icons.Avalonia.FontAwesome;
using Projektanker.Icons.Avalonia.MaterialDesign;
using Ryujinx.Ava.Systems;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Ava.Systems.Configuration.System;
using Ryujinx.Ava.Utilities.SystemInfo;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
@ -282,16 +284,16 @@ namespace Ryujinx.Ava
// Check if region was overridden.
if (CommandLineState.OverrideSystemRegion is not null)
if (Enum.TryParse(CommandLineState.OverrideSystemRegion, true, out Ryujinx.HLE.HOS.SystemState.RegionCode result))
if (Enum.TryParse(CommandLineState.OverrideSystemRegion, true, out HLE.HOS.SystemState.RegionCode result))
{
ConfigurationState.Instance.System.Region.Value = (Utilities.Configuration.System.Region)result;
ConfigurationState.Instance.System.Region.Value = result.ToUI();
}
//Check if language was overridden.
if (CommandLineState.OverrideSystemLanguage is not null)
if (Enum.TryParse(CommandLineState.OverrideSystemLanguage, true, out Ryujinx.HLE.HOS.SystemState.SystemLanguage result))
if (Enum.TryParse(CommandLineState.OverrideSystemLanguage, true, out HLE.HOS.SystemState.SystemLanguage result))
{
ConfigurationState.Instance.System.Language.Value = (Utilities.Configuration.System.Language)result;
ConfigurationState.Instance.System.Language.Value = result.ToUI();
}
// Check if hardware-acceleration was overridden.

View File

@ -91,10 +91,12 @@
<Content Include="..\..\distribution\legal\THIRDPARTY.md">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>THIRDPARTY.md</TargetPath>
<Visible>False</Visible>
</Content>
<Content Include="..\..\LICENSE.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>LICENSE.txt</TargetPath>
<Visible>False</Visible>
</Content>
</ItemGroup>

View File

@ -6,7 +6,6 @@ using Avalonia.Threading;
using DiscordRPC;
using LibHac.Common;
using LibHac.Ns;
using LibHac.Tools.FsSystem;
using Ryujinx.Audio.Backends.Dummy;
using Ryujinx.Audio.Backends.OpenAL;
using Ryujinx.Audio.Backends.SDL2;
@ -21,8 +20,8 @@ using Ryujinx.Ava.UI.Renderer;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems.AppLibrary;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Multiplayer;
@ -35,11 +34,9 @@ using Ryujinx.Graphics.GAL.Multithreading;
using Ryujinx.Graphics.Gpu;
using Ryujinx.Graphics.OpenGL;
using Ryujinx.Graphics.Vulkan;
using Ryujinx.HLE;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.Input;
using Ryujinx.Input.HLE;
using SkiaSharp;
@ -62,7 +59,7 @@ using Size = Avalonia.Size;
using Switch = Ryujinx.HLE.Switch;
using VSyncMode = Ryujinx.Common.Configuration.VSyncMode;
namespace Ryujinx.Ava
namespace Ryujinx.Ava.Systems
{
internal class AppHost
{
@ -470,7 +467,7 @@ namespace Ryujinx.Ava
Dispatcher.UIThread.InvokeAsync(() =>
{
_viewModel.Title = TitleHelper.ActiveApplicationTitle(Device.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowTitleBar);
_viewModel.Title = TitleHelper.ActiveApplicationTitle(Device.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI);
});
_viewModel.SetUiProgressHandlers(Device);
@ -872,7 +869,7 @@ namespace Ryujinx.Ava
Device?.System.TogglePauseEmulation(false);
_viewModel.IsPaused = false;
_viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowTitleBar);
_viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI);
Logger.Info?.Print(LogClass.Emulation, "Emulation was resumed");
}
@ -881,7 +878,7 @@ namespace Ryujinx.Ava
Device?.System.TogglePauseEmulation(true);
_viewModel.IsPaused = true;
_viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowTitleBar, LocaleManager.Instance[LocaleKeys.Paused]);
_viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI, LocaleManager.Instance[LocaleKeys.Paused]);
Logger.Info?.Print(LogClass.Emulation, "Emulation was paused");
}
@ -902,53 +899,19 @@ namespace Ryujinx.Ava
_ => new OpenGLRenderer()
};
BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading;
bool isGALThreaded = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading);
if (isGALThreaded)
{
renderer = new ThreadedRenderer(renderer);
}
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALThreaded}");
// Initialize Configuration.
MemoryConfiguration memoryConfiguration = ConfigurationState.Instance.System.DramSize.Value;
Device = new Switch(new HLEConfiguration(
VirtualFileSystem,
_viewModel.LibHacHorizonManager,
ContentManager,
_accountManager,
_userChannelPersistence,
renderer,
InitializeAudio(),
memoryConfiguration,
_viewModel.UiHandler,
(SystemLanguage)ConfigurationState.Instance.System.Language.Value,
(RegionCode)ConfigurationState.Instance.System.Region.Value,
ConfigurationState.Instance.Graphics.VSyncMode,
ConfigurationState.Instance.System.EnableDockedMode,
ConfigurationState.Instance.System.EnablePtc,
ConfigurationState.Instance.System.EnableInternetAccess,
ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
ConfigurationState.Instance.System.FsGlobalAccessLogMode,
ConfigurationState.Instance.System.MatchSystemTime
? 0
: ConfigurationState.Instance.System.SystemTimeOffset,
ConfigurationState.Instance.System.TimeZone,
ConfigurationState.Instance.System.MemoryManagerMode,
ConfigurationState.Instance.System.IgnoreMissingServices,
ConfigurationState.Instance.Graphics.AspectRatio,
ConfigurationState.Instance.System.AudioVolume,
ConfigurationState.Instance.System.UseHypervisor,
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value,
ConfigurationState.Instance.Multiplayer.Mode,
ConfigurationState.Instance.Multiplayer.DisableP2p,
ConfigurationState.Instance.Multiplayer.LdnPassphrase,
ConfigurationState.Instance.Multiplayer.GetLdnServer(),
ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value,
ConfigurationState.Instance.Hacks.ShowDirtyHacks ? ConfigurationState.Instance.Hacks.EnabledHacks : null));
Device = new Switch(ConfigurationState.Instance.CreateHleConfiguration()
.Configure(
VirtualFileSystem,
_viewModel.LibHacHorizonManager,
ContentManager,
_accountManager,
_userChannelPersistence,
renderer.TryMakeThreaded(ConfigurationState.Instance.Graphics.BackendThreading),
InitializeAudio(),
_viewModel.UiHandler
)
);
}
private static IHardwareDeviceDriver InitializeAudio()
@ -1182,6 +1145,9 @@ namespace Ryujinx.Ava
private void UpdateShaderCount()
{
if (_displayCount is 0 && _renderer.ProgramCount is 0)
return;
// If there is a mismatch between total program compile and previous count
// this means new shaders have been compiled and should be displayed.
if (_renderer.ProgramCount != _previousCount)
@ -1255,12 +1221,10 @@ namespace Ryujinx.Ava
VSyncModeToggle();
break;
case KeyboardHotkeyState.CustomVSyncIntervalDecrement:
Device.DecrementCustomVSyncInterval();
_viewModel.CustomVSyncInterval -= 1;
_viewModel.CustomVSyncInterval = Device.DecrementCustomVSyncInterval();
break;
case KeyboardHotkeyState.CustomVSyncIntervalIncrement:
Device.IncrementCustomVSyncInterval();
_viewModel.CustomVSyncInterval += 1;
_viewModel.CustomVSyncInterval = Device.IncrementCustomVSyncInterval();
break;
case KeyboardHotkeyState.Screenshot:
ScreenshotRequested = true;

View File

@ -1,6 +1,6 @@
using System;
namespace Ryujinx.Ava.Utilities.AppLibrary
namespace Ryujinx.Ava.Systems.AppLibrary
{
public class ApplicationCountUpdatedEventArgs : EventArgs
{

View File

@ -9,8 +9,8 @@ using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Utilities.Compat;
using Ryujinx.Ava.Utilities.PlayReport;
using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Systems.PlayReport;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.Loaders.Processes.Extensions;
@ -18,7 +18,7 @@ using System;
using System.IO;
using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Utilities.AppLibrary
namespace Ryujinx.Ava.Systems.AppLibrary
{
public class ApplicationData
{
@ -36,7 +36,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
{
_id = value;
Compatibility = CompatibilityCsv.Find(value);
Compatibility = CompatibilityDatabase.Find(value);
RichPresenceSpec = PlayReports.Analyzer.TryGetSpec(IdString, out GameSpec gameSpec)
? gameSpec
: default(Optional<GameSpec>);

View File

@ -1,6 +1,6 @@
using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Utilities.AppLibrary
namespace Ryujinx.Ava.Systems.AppLibrary
{
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ApplicationMetadata))]

View File

@ -12,8 +12,9 @@ using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Utilities.Configuration.System;
using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Ava.Systems.Configuration.System;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Multiplayer;
@ -38,7 +39,7 @@ using MissingKeyException = LibHac.Common.Keys.MissingKeyException;
using Path = System.IO.Path;
using TimeSpan = System.TimeSpan;
namespace Ryujinx.Ava.Utilities.AppLibrary
namespace Ryujinx.Ava.Systems.AppLibrary
{
public class ApplicationLibrary
{
@ -617,15 +618,11 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
case ".xci":
case ".nsp":
{
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
? IntegrityCheckLevel.ErrorOnInvalid
: IntegrityCheckLevel.None;
using IFileSystem pfs =
PartitionFileSystemUtils.OpenApplicationFileSystem(filePath, _virtualFileSystem);
Dictionary<ulong, ContentMetaData> updates =
pfs.GetContentData(ContentMetaType.Patch, _virtualFileSystem, checkLevel);
pfs.GetContentData(ContentMetaType.Patch, _virtualFileSystem, ConfigurationState.Instance.System.IntegrityCheckLevel);
if (updates.Count == 0)
{

View File

@ -1,7 +1,7 @@
using System;
using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Utilities.AppLibrary
namespace Ryujinx.Ava.Systems.AppLibrary
{
public class ApplicationMetadata
{

View File

@ -4,7 +4,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Ava.Utilities.AppLibrary
namespace Ryujinx.Ava.Systems.AppLibrary
{
public struct LdnGameData
{

View File

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
namespace Ryujinx.Ava.Utilities.AppLibrary
namespace Ryujinx.Ava.Systems.AppLibrary
{
public class LdnGameDataReceivedEventArgs : EventArgs
{

View File

@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Utilities.AppLibrary
namespace Ryujinx.Ava.Systems.AppLibrary
{
[JsonSerializable(typeof(IEnumerable<LdnGameData>))]
internal partial class LdnGameDataSerializerContext : JsonSerializerContext;

View File

@ -9,26 +9,11 @@ using System.Linq;
using System.Reflection;
using System.Text;
namespace Ryujinx.Ava.Utilities.Compat
namespace Ryujinx.Ava.Systems
{
public struct ColumnIndices(Func<ReadOnlySpan<char>, int> getIndex)
public class CompatibilityDatabase
{
public const string TitleIdCol = "\"title_id\"";
public const string GameNameCol = "\"game_name\"";
public const string LabelsCol = "\"labels\"";
public const string StatusCol = "\"status\"";
public const string LastUpdatedCol = "\"last_updated\"";
public readonly int TitleId = getIndex(TitleIdCol);
public readonly int GameName = getIndex(GameNameCol);
public readonly int Labels = getIndex(LabelsCol);
public readonly int Status = getIndex(StatusCol);
public readonly int LastUpdated = getIndex(LastUpdatedCol);
}
public class CompatibilityCsv
{
static CompatibilityCsv() => Load();
static CompatibilityDatabase() => Load();
public static void Load()
{
@ -65,16 +50,6 @@ namespace Ryujinx.Ava.Utilities.Compat
public static CompatibilityEntry Find(ulong titleId)
=> Find(titleId.ToString("X16"));
public static LocaleKeys? GetStatus(string titleId)
=> Find(titleId)?.Status;
public static LocaleKeys? GetStatus(ulong titleId) => GetStatus(titleId.ToString("X16"));
public static string GetLabels(string titleId)
=> Find(titleId)?.FormattedIssueLabels;
public static string GetLabels(ulong titleId) => GetLabels(titleId.ToString("X16"));
}
public class CompatibilityEntry
@ -135,6 +110,7 @@ namespace Ryujinx.Ava.Utilities.Compat
public string FormattedIssueLabels => Labels
.Select(FormatLabelName)
.Where(x => x != null)
.JoinToString(", ");
public override string ToString() =>
@ -158,7 +134,6 @@ namespace Ryujinx.Ava.Utilities.Compat
"gui" => "GUI",
"help wanted" => "Help Wanted",
"horizon" => "Horizon",
"infra" => "Project Infra",
"invalid" => "Invalid",
"kernel" => "Kernel",
"ldn" => "LDN",
@ -172,9 +147,9 @@ namespace Ryujinx.Ava.Utilities.Compat
"ldn-untested" => "LDN Untested",
"ldn-broken" => "LDN Broken",
"ldn-partial" => "Partial LDN",
"nvdec" => "NVDEC",
"services" => "NX Services",
"services-horizon" => "Horizon OS Services",
"nvdec" => "GPU Video Decoding",
"services" => "HLE Services",
"services-horizon" => "New HLE Services",
"slow" => "Runs Slow",
"crash" => "Crashes",
"deadlock" => "Deadlock",
@ -182,7 +157,7 @@ namespace Ryujinx.Ava.Utilities.Compat
"opengl" => "OpenGL",
"opengl-backend-bug" => "OpenGL Backend Bug",
"vulkan-backend-bug" => "Vulkan Backend Bug",
"mac-bug" => "Mac-specific Bug(s)",
"mac-bug" => "Mac-specific Problems",
"amd-vendor-bug" => "AMD GPU Bug",
"intel-vendor-bug" => "Intel GPU Bug",
"loader-allocator" => "Loader Allocator",
@ -191,18 +166,22 @@ namespace Ryujinx.Ava.Utilities.Compat
"UE4" => "Unreal Engine 4",
"homebrew" => "Homebrew Content",
"online-broken" => "Online Broken",
_ => Capitalize(labelName)
_ => null
};
}
public struct ColumnIndices(Func<ReadOnlySpan<char>, int> getIndex)
{
private const string TitleIdCol = "\"title_id\"";
private const string GameNameCol = "\"game_name\"";
private const string LabelsCol = "\"labels\"";
private const string StatusCol = "\"status\"";
private const string LastUpdatedCol = "\"last_updated\"";
public static string Capitalize(string value)
{
if (value == string.Empty)
return string.Empty;
char firstChar = value[0];
string rest = value[1..];
return $"{char.ToUpper(firstChar)}{rest}";
}
public readonly int TitleId = getIndex(TitleIdCol);
public readonly int GameName = getIndex(GameNameCol);
public readonly int Labels = getIndex(LabelsCol);
public readonly int Status = getIndex(StatusCol);
public readonly int LastUpdated = getIndex(LastUpdatedCol);
}
}

View File

@ -1,7 +1,7 @@
using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Utilities.Configuration
namespace Ryujinx.Ava.Systems.Configuration
{
[JsonConverter(typeof(TypedStringEnumConverter<AudioBackend>))]
public enum AudioBackend

View File

@ -1,5 +1,5 @@
using Ryujinx.Ava.Utilities.Configuration.System;
using Ryujinx.Ava.Utilities.Configuration.UI;
using Ryujinx.Ava.Systems.Configuration.System;
using Ryujinx.Ava.Systems.Configuration.UI;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Multiplayer;
@ -8,14 +8,14 @@ using Ryujinx.Common.Utilities;
using Ryujinx.HLE;
using System.Collections.Generic;
namespace Ryujinx.Ava.Utilities.Configuration
namespace Ryujinx.Ava.Systems.Configuration
{
public class ConfigurationFileFormat
{
/// <summary>
/// The current version of the file format
/// </summary>
public const int CurrentVersion = 67;
public const int CurrentVersion = 68;
/// <summary>
/// Version of the configuration file format

View File

@ -1,6 +1,6 @@
using Ryujinx.Common.Utilities;
namespace Ryujinx.Ava.Utilities.Configuration
namespace Ryujinx.Ava.Systems.Configuration
{
internal static class ConfigurationFileFormatSettings
{

View File

@ -1,6 +1,6 @@
using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Utilities.Configuration
namespace Ryujinx.Ava.Systems.Configuration
{
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ConfigurationFileFormat))]

View File

@ -1,7 +1,7 @@
using Avalonia.Media;
using Gommon;
using Ryujinx.Ava.Utilities.Configuration.System;
using Ryujinx.Ava.Utilities.Configuration.UI;
using Ryujinx.Ava.Systems.Configuration.System;
using Ryujinx.Ava.Systems.Configuration.UI;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller;
@ -14,7 +14,7 @@ using System.Collections.Generic;
using System.Linq;
using RyuLogger = Ryujinx.Common.Logging.Logger;
namespace Ryujinx.Ava.Utilities.Configuration
namespace Ryujinx.Ava.Systems.Configuration
{
public partial class ConfigurationState
{
@ -46,12 +46,11 @@ namespace Ryujinx.Ava.Utilities.Configuration
EnableDiscordIntegration.Value = cff.EnableDiscordIntegration;
CheckUpdatesOnStart.Value = shouldLoadFromFile ? cff.CheckUpdatesOnStart : CheckUpdatesOnStart.Value; // Get from global config only
UpdateCheckerType.Value = shouldLoadFromFile ? cff.UpdateCheckerType : UpdateCheckerType.Value; // Get from global config only
FocusLostActionType.Value = cff.FocusLostActionType;
ShowConfirmExit.Value = shouldLoadFromFile ? cff.ShowConfirmExit : ShowConfirmExit.Value; // Get from global config only
RememberWindowState.Value = shouldLoadFromFile ? cff.RememberWindowState : RememberWindowState.Value; // Get from global config only
ShowTitleBar.Value = shouldLoadFromFile ? cff.ShowTitleBar : ShowTitleBar.Value; // Get from global config only
ShowOldUI.Value = shouldLoadFromFile ? cff.ShowTitleBar : ShowOldUI.Value; // Get from global config only
EnableHardwareAcceleration.Value = shouldLoadFromFile ? cff.EnableHardwareAcceleration : EnableHardwareAcceleration.Value; // Get from global config only
HideCursor.Value = cff.HideCursor;
@ -161,9 +160,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
DirtyHacks hacks = new (cff.DirtyHacks ?? []);
Hacks.Xc2MenuSoftlockFix.Value = hacks.IsEnabled(DirtyHack.Xc2MenuSoftlockFix);
Hacks.EnableShaderTranslationDelay.Value = hacks.IsEnabled(DirtyHack.ShaderTranslationDelay);
Hacks.ShaderTranslationDelay.Value = hacks[DirtyHack.ShaderTranslationDelay].CoerceAtLeast(0);
}
if (configurationFileUpdated)
@ -442,6 +439,8 @@ namespace Ryujinx.Ava.Utilities.Configuration
(65, static cff => cff.UpdateCheckerType = cff.CheckUpdatesOnStart ? UpdaterType.PromptAtStartup : UpdaterType.Off),
(66, static cff => cff.DisableInputWhenOutOfFocus = false),
(67, static cff => cff.FocusLostActionType = cff.DisableInputWhenOutOfFocus ? FocusLostType.BlockInput : FocusLostType.DoNothing)
// 68 was the version that added per-game configs; the file structure did not change
// the version was increased so external tools could know that your Ryujinx version has per-game config capabilities.
);
}
}

View File

@ -1,8 +1,8 @@
using ARMeilleure;
using Gommon;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Utilities.Configuration.System;
using Ryujinx.Ava.Utilities.Configuration.UI;
using LibHac.Tools.FsSystem;
using Ryujinx.Ava.Systems.Configuration.System;
using Ryujinx.Ava.Systems.Configuration.UI;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
@ -15,11 +15,11 @@ using System.Collections.Generic;
using System.Linq;
using RyuLogger = Ryujinx.Common.Logging.Logger;
namespace Ryujinx.Ava.Utilities.Configuration
namespace Ryujinx.Ava.Systems.Configuration
{
public partial class ConfigurationState
{
/// <summary>
/// <summary>
/// UI configuration section
/// </summary>
public class UISection
@ -351,6 +351,10 @@ namespace Ryujinx.Ava.Utilities.Configuration
/// </summary>
public ReactiveObject<bool> EnableFsIntegrityChecks { get; private set; }
public IntegrityCheckLevel IntegrityCheckLevel => EnableFsIntegrityChecks
? IntegrityCheckLevel.ErrorOnInvalid
: IntegrityCheckLevel.None;
/// <summary>
/// Enables FS access log output to the console. Possible modes are 0-3
/// </summary>
@ -679,18 +683,15 @@ namespace Ryujinx.Ava.Utilities.Configuration
public ReactiveObject<bool> Xc2MenuSoftlockFix { get; private set; }
public ReactiveObject<bool> EnableShaderTranslationDelay { get; private set; }
public ReactiveObject<int> ShaderTranslationDelay { get; private set; }
public ReactiveObject<bool> DisableNifmIsAnyInternetRequestAccepted { get; private set; }
public HacksSection()
{
ShowDirtyHacks = new ReactiveObject<bool>();
Xc2MenuSoftlockFix = new ReactiveObject<bool>();
Xc2MenuSoftlockFix.Event += HackChanged;
EnableShaderTranslationDelay = new ReactiveObject<bool>();
EnableShaderTranslationDelay.Event += HackChanged;
ShaderTranslationDelay = new ReactiveObject<int>();
DisableNifmIsAnyInternetRequestAccepted = new ReactiveObject<bool>();
DisableNifmIsAnyInternetRequestAccepted.Event += HackChanged;
}
private void HackChanged(object sender, ReactiveEventArgs<bool> rxe)
@ -721,8 +722,8 @@ namespace Ryujinx.Ava.Utilities.Configuration
if (Xc2MenuSoftlockFix)
Apply(DirtyHack.Xc2MenuSoftlockFix);
if (EnableShaderTranslationDelay)
Apply(DirtyHack.ShaderTranslationDelay, ShaderTranslationDelay);
if (DisableNifmIsAnyInternetRequestAccepted)
Apply(DirtyHack.NifmServiceDisableIsAnyInternetRequestAccepted);
return enabledHacks.ToArray();
@ -778,11 +779,6 @@ namespace Ryujinx.Ava.Utilities.Configuration
/// Enables or disables Discord Rich Presence
/// </summary>
public ReactiveObject<bool> EnableDiscordIntegration { get; private set; }
/// <summary>
/// Checks for updates when Ryujinx starts when enabled
/// </summary>
public ReactiveObject<bool> CheckUpdatesOnStart { get; private set; }
/// <summary>
/// Checks for updates when Ryujinx starts when enabled, either prompting when an update is found or just showing a notification.
@ -805,9 +801,9 @@ namespace Ryujinx.Ava.Utilities.Configuration
public ReactiveObject<bool> RememberWindowState { get; private set; }
/// <summary>
/// Enables or disables the redesigned title bar
/// Disable the new title bar layout & window layout changes.
/// </summary>
public ReactiveObject<bool> ShowTitleBar { get; private set; }
public ReactiveObject<bool> ShowOldUI { get; private set; }
/// <summary>
/// Enables hardware-accelerated rendering for Avalonia
@ -828,15 +824,44 @@ namespace Ryujinx.Ava.Utilities.Configuration
Hid = new HidSection();
Multiplayer = new MultiplayerSection();
Hacks = new HacksSection();
EnableDiscordIntegration = new ReactiveObject<bool>();
CheckUpdatesOnStart = new ReactiveObject<bool>();
UpdateCheckerType = new ReactiveObject<UpdaterType>();
FocusLostActionType = new ReactiveObject<FocusLostType>();
HideCursor = new ReactiveObject<HideCursorMode>();
EnableDiscordIntegration = new ReactiveObject<bool>();
ShowConfirmExit = new ReactiveObject<bool>();
RememberWindowState = new ReactiveObject<bool>();
ShowTitleBar = new ReactiveObject<bool>();
ShowOldUI = new ReactiveObject<bool>();
EnableHardwareAcceleration = new ReactiveObject<bool>();
HideCursor = new ReactiveObject<HideCursorMode>();
}
public HleConfiguration CreateHleConfiguration() =>
new(
System.DramSize,
System.Language.Value.ToHLE(),
System.Region.Value.ToHLE(),
Graphics.VSyncMode,
System.EnableDockedMode,
System.EnablePtc,
System.EnableInternetAccess,
System.EnableFsIntegrityChecks
? IntegrityCheckLevel.ErrorOnInvalid
: IntegrityCheckLevel.None,
System.FsGlobalAccessLogMode,
System.MatchSystemTime
? 0
: System.SystemTimeOffset,
System.TimeZone,
System.MemoryManagerMode,
System.IgnoreMissingServices,
Graphics.AspectRatio,
System.AudioVolume,
System.UseHypervisor,
Multiplayer.LanInterfaceId,
Multiplayer.Mode,
Multiplayer.DisableP2p,
Multiplayer.LdnPassphrase,
Instance.Multiplayer.GetLdnServer(),
Instance.Graphics.CustomVSyncInterval,
Instance.Hacks.ShowDirtyHacks ? Instance.Hacks.EnabledHacks : null);
}
}

View File

@ -1,5 +1,5 @@
using Ryujinx.Ava.Utilities.Configuration.System;
using Ryujinx.Ava.Utilities.Configuration.UI;
using Ryujinx.Ava.Systems.Configuration.System;
using Ryujinx.Ava.Systems.Configuration.UI;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Keyboard;
@ -9,7 +9,7 @@ using Ryujinx.HLE;
using System;
using System.Linq;
namespace Ryujinx.Ava.Utilities.Configuration
namespace Ryujinx.Ava.Systems.Configuration
{
public partial class ConfigurationState
{
@ -56,12 +56,11 @@ namespace Ryujinx.Ava.Utilities.Configuration
MatchSystemTime = System.MatchSystemTime,
DockedMode = System.EnableDockedMode,
EnableDiscordIntegration = EnableDiscordIntegration,
CheckUpdatesOnStart = CheckUpdatesOnStart,
UpdateCheckerType = UpdateCheckerType,
FocusLostActionType = FocusLostActionType,
ShowConfirmExit = ShowConfirmExit,
RememberWindowState = RememberWindowState,
ShowTitleBar = ShowTitleBar,
ShowTitleBar = ShowOldUI,
EnableHardwareAcceleration = EnableHardwareAcceleration,
HideCursor = HideCursor,
VSyncMode = Graphics.VSyncMode,
@ -183,7 +182,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
FocusLostActionType.Value = FocusLostType.DoNothing;
ShowConfirmExit.Value = true;
RememberWindowState.Value = true;
ShowTitleBar.Value = !OperatingSystem.IsWindows();
ShowOldUI.Value = !OperatingSystem.IsWindows();
EnableHardwareAcceleration.Value = true;
HideCursor.Value = HideCursorMode.OnIdle;
Graphics.VSyncMode.Value = VSyncMode.Switch;
@ -327,5 +326,5 @@ namespace Ryujinx.Ava.Utilities.Configuration
return GraphicsBackend.OpenGl;
}
}
}
}
}

View File

@ -1,8 +1,8 @@
using System;
using static Ryujinx.Ava.Utilities.Configuration.ConfigurationState.UISection;
using static Ryujinx.Ava.Systems.Configuration.ConfigurationState.UISection;
namespace Ryujinx.Ava.Utilities.Configuration
namespace Ryujinx.Ava.Systems.Configuration
{
public enum FileTypes
{

View File

@ -4,7 +4,7 @@ using Ryujinx.Common.Logging.Targets;
using System;
using System.IO;
namespace Ryujinx.Ava.Utilities.Configuration
namespace Ryujinx.Ava.Systems.Configuration
{
public static class LoggerModule
{

View File

@ -1,7 +1,7 @@
using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Utilities.Configuration.System
namespace Ryujinx.Ava.Systems.Configuration.System
{
[JsonConverter(typeof(TypedStringEnumConverter<Language>))]
public enum Language
@ -25,4 +25,13 @@ namespace Ryujinx.Ava.Utilities.Configuration.System
TraditionalChinese,
BrazilianPortuguese,
}
public static class LanguageEnumHelper
{
public static Language ToUI(this HLE.HOS.SystemState.SystemLanguage hleLanguage)
=> (Language)hleLanguage;
public static HLE.HOS.SystemState.SystemLanguage ToHLE(this Language uiLanguage)
=> (HLE.HOS.SystemState.SystemLanguage)uiLanguage;
}
}

View File

@ -0,0 +1,26 @@
using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Systems.Configuration.System
{
[JsonConverter(typeof(TypedStringEnumConverter<Region>))]
public enum Region
{
Japan,
USA,
Europe,
Australia,
China,
Korea,
Taiwan,
}
public static class RegionEnumHelper
{
public static Region ToUI(this HLE.HOS.SystemState.RegionCode hleRegion)
=> (Region)hleRegion;
public static HLE.HOS.SystemState.RegionCode ToHLE(this Region uiRegion)
=> (HLE.HOS.SystemState.RegionCode)uiRegion;
}
}

View File

@ -1,4 +1,4 @@
namespace Ryujinx.Ava.Utilities.Configuration.UI
namespace Ryujinx.Ava.Systems.Configuration.UI
{
public struct ColumnSort
{

View File

@ -1,7 +1,7 @@
using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Utilities.Configuration.UI
namespace Ryujinx.Ava.Systems.Configuration.UI
{
[JsonConverter(typeof(TypedStringEnumConverter<FocusLostType>))]
public enum FocusLostType

View File

@ -1,4 +1,4 @@
namespace Ryujinx.Ava.Utilities.Configuration.UI
namespace Ryujinx.Ava.Systems.Configuration.UI
{
public struct GuiColumns
{

View File

@ -1,4 +1,4 @@
namespace Ryujinx.Ava.Utilities.Configuration.UI
namespace Ryujinx.Ava.Systems.Configuration.UI
{
public struct ShownFileTypes
{

View File

@ -1,7 +1,7 @@
using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Ava.Utilities.Configuration.UI
namespace Ryujinx.Ava.Systems.Configuration.UI
{
[JsonConverter(typeof(TypedStringEnumConverter<UpdaterType>))]
public enum UpdaterType

View File

@ -1,4 +1,4 @@
namespace Ryujinx.Ava.Utilities.Configuration.UI
namespace Ryujinx.Ava.Systems.Configuration.UI
{
public struct WindowStartup
{

View File

@ -1,18 +1,17 @@
using DiscordRPC;
using Gommon;
using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Utilities.PlayReport;
using Ryujinx.Ava.Systems.AppLibrary;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Ava.Systems.PlayReport;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE;
using Ryujinx.HLE.Loaders.Processes;
using Ryujinx.Horizon;
using Ryujinx.Horizon.Prepo.Types;
using System.Text;
namespace Ryujinx.Ava
namespace Ryujinx.Ava.Systems
{
public static class DiscordIntegrationModule
{
@ -124,7 +123,7 @@ namespace Ryujinx.Ava
_currentApp = null;
}
private static void HandlePlayReport(PlayReport playReport)
private static void HandlePlayReport(Horizon.Prepo.Types.PlayReport playReport)
{
if (_discordClient is null) return;
if (!TitleIDs.CurrentApplication.Value.HasValue) return;

View File

@ -1,5 +1,5 @@
using Gommon;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.AppLibrary;
using Ryujinx.Common.Logging;
using System;
using System.Collections.Generic;
@ -7,7 +7,7 @@ using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
namespace Ryujinx.Ava.Utilities.PlayReport
namespace Ryujinx.Ava.Systems.PlayReport
{
/// <summary>
/// The entrypoint for the Play Report analysis system.

View File

@ -1,4 +1,4 @@
namespace Ryujinx.Ava.Utilities.PlayReport
namespace Ryujinx.Ava.Systems.PlayReport
{
/// <summary>
/// The delegate type that powers single value formatters.<br/>

View File

@ -1,8 +1,8 @@
using MsgPack;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.AppLibrary;
using System.Collections.Generic;
namespace Ryujinx.Ava.Utilities.PlayReport
namespace Ryujinx.Ava.Systems.PlayReport
{
public abstract class MatchedValue<T>
{

View File

@ -5,7 +5,7 @@ using System.Buffers.Binary;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Ava.Utilities.PlayReport
namespace Ryujinx.Ava.Systems.PlayReport
{
public partial class PlayReports
{

View File

@ -1,6 +1,6 @@
using System;
namespace Ryujinx.Ava.Utilities.PlayReport
namespace Ryujinx.Ava.Systems.PlayReport
{
public static partial class PlayReports
{

View File

@ -1,10 +1,10 @@
using MsgPack;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.AppLibrary;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Ava.Utilities.PlayReport
namespace Ryujinx.Ava.Systems.PlayReport
{
/// <summary>
/// A mapping of title IDs to value formatter specs.
@ -103,7 +103,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
/// matching a specific set of keys that could exist in a Play Report for the previously specified title IDs.
/// <br/><br/>
/// The 'Sparse' multi-value formatters do not require every key to be present.
/// If you need this requirement, use <see cref="AddMultiValueFormatter(string[], Ryujinx.Ava.Utilities.PlayReport.MultiValueFormatter)"/>.
/// If you need this requirement, use <see cref="AddMultiValueFormatter(string[], MultiValueFormatter)"/>.
/// </summary>
/// <param name="reportKeys">The key names to match.</param>
/// <param name="valueFormatter">The function which can format the values.</param>
@ -118,7 +118,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
/// matching a specific set of keys that could exist in a Play Report for the previously specified title IDs.
/// <br/><br/>
/// The 'Sparse' multi-value formatters do not require every key to be present.
/// If you need this requirement, use <see cref="AddMultiValueFormatter(int, string[], Ryujinx.Ava.Utilities.PlayReport.MultiValueFormatter)"/>.
/// If you need this requirement, use <see cref="AddMultiValueFormatter(int, string[], MultiValueFormatter)"/>.
/// </summary>
/// <param name="priority">The resolution priority of this value formatter. Higher resolves sooner.</param>
/// <param name="reportKeys">The key names to match.</param>

View File

@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Ava.Utilities.PlayReport
namespace Ryujinx.Ava.Systems.PlayReport
{
/// <summary>
/// The base input data to a ValueFormatter delegate,

View File

@ -1,8 +1,6 @@
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Utilities;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@ -10,7 +8,7 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Ryujinx.Ava
namespace Ryujinx.Ava.Systems
{
internal static class Rebooter
{

View File

@ -27,7 +27,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Ryujinx.Ava
namespace Ryujinx.Ava.Systems
{
internal static class Updater
{

View File

@ -7,7 +7,7 @@ using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Common;
using Ryujinx.HLE;
using Ryujinx.HLE.HOS.Applets;

View File

@ -17,12 +17,8 @@
<viewModels:ProfileSelectorDialogViewModel />
</Design.DataContext>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RowDefinitions="*,Auto">
<Border
CornerRadius="5"
BorderBrush="{DynamicResource AppListHoverBackgroundColor}"

View File

@ -9,17 +9,14 @@ using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading.Tasks;
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
using UserProfileSft = Ryujinx.HLE.HOS.Services.Account.Acc.UserProfile;
namespace Ryujinx.Ava.UI.Applet
{
public partial class ProfileSelectorDialog : UserControl
public partial class ProfileSelectorDialog : RyujinxControl<ProfileSelectorDialogViewModel>
{
public ProfileSelectorDialogViewModel ViewModel { get; set; }
public ProfileSelectorDialog(ProfileSelectorDialogViewModel viewModel)
{
DataContext = ViewModel = viewModel;

View File

@ -11,8 +11,8 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Utilities.Compat;
using Ryujinx.Ava.Systems.AppLibrary;
using Ryujinx.Ava.UI.Views.Dialog;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Helper;
using Ryujinx.HLE.HOS;
@ -26,6 +26,7 @@ namespace Ryujinx.Ava.UI.Controls
{
public class ApplicationContextMenu : MenuFlyout
{
public ApplicationContextMenu()
{
InitializeComponent();
@ -79,13 +80,13 @@ namespace Ryujinx.Ava.UI.Controls
public async void OpenTitleUpdateManager_Click(object sender, RoutedEventArgs args)
{
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
await TitleUpdateWindow.Show(viewModel.ApplicationLibrary, viewModel.SelectedApplication);
await TitleUpdateManagerView.Show(viewModel.ApplicationLibrary, viewModel.SelectedApplication);
}
public async void OpenDownloadableContentManager_Click(object sender, RoutedEventArgs args)
{
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
await DownloadableContentManagerWindow.Show(viewModel.ApplicationLibrary, viewModel.SelectedApplication);
await DownloadableContentManagerView.Show(viewModel.ApplicationLibrary, viewModel.SelectedApplication);
}
public async void OpenCheatManager_Click(object sender, RoutedEventArgs args)
@ -126,7 +127,7 @@ namespace Ryujinx.Ava.UI.Controls
public async void OpenModManager_Click(object sender, RoutedEventArgs args)
{
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
await ModManagerWindow.Show(
await ModManagerView.Show(
viewModel.SelectedApplication.Id,
viewModel.SelectedApplication.IdBase,
viewModel.ApplicationLibrary,
@ -406,7 +407,7 @@ namespace Ryujinx.Ava.UI.Controls
public async void OpenApplicationCompatibility_Click(object sender, RoutedEventArgs args)
{
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
await CompatibilityList.Show(viewModel.SelectedApplication.IdString);
await CompatibilityListWindow.Show(viewModel.SelectedApplication.IdString);
}
public async void OpenApplicationData_Click(object sender, RoutedEventArgs args)

View File

@ -23,13 +23,12 @@ using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
namespace Ryujinx.Ava.UI.Controls
{
public partial class NavigationDialogHost : UserControl
public partial class NavigationDialogHost : RyujinxControl<UserProfileViewModel>
{
public AccountManager AccountManager { get; }
public ContentManager ContentManager { get; }
public VirtualFileSystem VirtualFileSystem { get; }
public HorizonClient HorizonClient { get; }
public UserProfileViewModel ViewModel { get; set; }
public NavigationDialogHost()
{

View File

@ -0,0 +1,24 @@
using Avalonia.Controls;
using Gommon;
using Ryujinx.Ava.UI.ViewModels;
using System;
namespace Ryujinx.Ava.UI.Controls
{
public class RyujinxControl<TViewModel> : UserControl where TViewModel : BaseModel
{
public TViewModel ViewModel
{
get
{
if (DataContext is not TViewModel viewModel)
throw new InvalidOperationException(
$"Underlying DataContext is not of type {typeof(TViewModel).AsPrettyString()}; " +
$"Actual type is {DataContext?.GetType().AsPrettyString()}");
return viewModel;
}
set => DataContext = value;
}
}
}

View File

@ -0,0 +1,28 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media.Imaging;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Ava.UI.ViewModels;
using System.Reflection;
namespace Ryujinx.Ava.UI.Controls
{
public class RyujinxLogo : Image
{
// The UI specifically uses a thicker bordered variant of the icon to avoid crunching out the border at lower resolutions.
// For an example of this, download canary 1.2.95, then open the settings menu, and look at the icon in the top-left.
// The border gets reduced to colored pixels in the 4 corners.
public static readonly Bitmap Bitmap =
new(Assembly.GetAssembly(typeof(MainWindowViewModel))!
.GetManifestResourceStream("Ryujinx.Assets.UIImages.Logo_Ryujinx_AntiAlias.png")!);
public RyujinxLogo()
{
Margin = new Thickness(7, 7, 7, 0);
Height = 25;
Width = 25;
Source = Bitmap;
IsVisible = !ConfigurationState.Instance.ShowOldUI;
}
}
}

View File

@ -1,5 +1,5 @@
using Avalonia.Interactivity;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.AppLibrary;
namespace Ryujinx.Ava.UI.Helpers
{

View File

@ -4,6 +4,7 @@ using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Styling;
using Avalonia.Threading;
using FluentAvalonia.Core;
using FluentAvalonia.UI.Controls;
@ -21,6 +22,23 @@ namespace Ryujinx.Ava.UI.Helpers
private static bool _isChoiceDialogOpen;
private static ContentDialogOverlayWindow _contentDialogOverlayWindow;
public static ContentDialog ApplyStyles(
this ContentDialog contentDialog,
double closeButtonWidth = 80,
HorizontalAlignment buttonSpaceAlignment = HorizontalAlignment.Right)
{
Style closeButton = new(x => x.Name("CloseButton"));
closeButton.Setters.Add(new Setter(Layoutable.WidthProperty, closeButtonWidth));
Style closeButtonParent = new(x => x.Name("CommandSpace"));
closeButtonParent.Setters.Add(new Setter(Layoutable.HorizontalAlignmentProperty, buttonSpaceAlignment));
contentDialog.Styles.Add(closeButton);
contentDialog.Styles.Add(closeButtonParent);
return contentDialog;
}
private async static Task<UserResult> ShowContentDialog(
string title,
object content,
@ -40,19 +58,19 @@ namespace Ryujinx.Ava.UI.Helpers
SecondaryButtonText = secondaryButton,
CloseButtonText = closeButton,
Content = content,
PrimaryButtonCommand = MiniCommand.Create(() =>
PrimaryButtonCommand = Commands.Create(() =>
{
result = primaryButtonResult;
})
};
contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
contentDialog.SecondaryButtonCommand = Commands.Create(() =>
{
result = UserResult.No;
contentDialog.PrimaryButtonClick -= deferCloseAction;
});
contentDialog.CloseButtonCommand = MiniCommand.Create(() =>
contentDialog.CloseButtonCommand = Commands.Create(() =>
{
result = UserResult.Cancel;
contentDialog.PrimaryButtonClick -= deferCloseAction;

View File

@ -2,7 +2,7 @@ using Avalonia.Data.Converters;
using Avalonia.Markup.Xaml;
using Gommon;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.AppLibrary;
using System;
using System.Globalization;
using System.Text;

View File

@ -2,9 +2,9 @@ using SkiaSharp;
using System;
using System.Collections.Generic;
namespace Ryujinx.Ava.UI.Windows
namespace Ryujinx.Ava.UI.Helpers
{
static class IconColorPicker
public static class IconColorPicker
{
private const int ColorsPerLine = 64;
private const int TotalColors = ColorsPerLine * ColorsPerLine;

View File

@ -1,7 +1,7 @@
using Avalonia.Logging;
using Avalonia.Utilities;
using Gommon;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Common.Logging;
using System;
using System.Text;

View File

@ -1,72 +0,0 @@
using System;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Ryujinx.Ava.UI.Helpers
{
public sealed class MiniCommand<T> : MiniCommand, ICommand
{
private readonly Action<T> _callback;
private bool _busy;
private readonly Func<T, Task> _asyncCallback;
public MiniCommand(Action<T> callback)
{
_callback = callback;
}
public MiniCommand(Func<T, Task> callback)
{
_asyncCallback = callback;
}
private bool Busy
{
get => _busy;
set
{
_busy = value;
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
public override event EventHandler CanExecuteChanged;
public override bool CanExecute(object parameter) => !_busy;
public override async void Execute(object parameter)
{
if (Busy)
{
return;
}
try
{
Busy = true;
if (_callback != null)
{
_callback((T)parameter);
}
else
{
await _asyncCallback((T)parameter);
}
}
finally
{
Busy = false;
}
}
}
public abstract class MiniCommand : ICommand
{
public static MiniCommand Create(Action callback) => new MiniCommand<object>(_ => callback());
public static MiniCommand Create<TArg>(Action<TArg> callback) => new MiniCommand<TArg>(callback);
public static MiniCommand CreateFromTask(Func<Task> callback) => new MiniCommand<object>(_ => callback());
public static MiniCommand CreateFromTask<TArg>(Func<TArg, Task> callback) => new MiniCommand<TArg>(callback);
public abstract bool CanExecute(object parameter);
public abstract void Execute(object parameter);
public abstract event EventHandler CanExecuteChanged;
}
}

View File

@ -1,4 +1,4 @@
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.AppLibrary;
using System;
using System.Collections.Generic;

View File

@ -1,4 +1,4 @@
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.AppLibrary;
using System;
using System.Collections.Generic;

View File

@ -0,0 +1,260 @@
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.ViewModels.Input;
using Ryujinx.Input;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Models.Input
{
public class StickVisualizer : BaseModel, IDisposable
{
public const int DrawStickPollRate = 50; // Milliseconds per poll.
public const int DrawStickCircumference = 5;
public const float DrawStickScaleFactor = DrawStickCanvasCenter;
public const int DrawStickCanvasSize = 100;
public const int DrawStickBorderSize = DrawStickCanvasSize + 5;
public const float DrawStickCanvasCenter = (DrawStickCanvasSize - DrawStickCircumference) / 2;
public const float MaxVectorLength = DrawStickCanvasSize / 2;
public CancellationTokenSource PollTokenSource;
public CancellationToken PollToken;
private static float _vectorLength;
private static float _vectorMultiplier;
private bool disposedValue;
private DeviceType _type;
public DeviceType Type
{
get => _type;
set
{
_type = value;
OnPropertyChanged();
}
}
private GamepadInputConfig _gamepadConfig;
public GamepadInputConfig GamepadConfig
{
get => _gamepadConfig;
set
{
_gamepadConfig = value;
OnPropertyChanged();
}
}
private KeyboardInputConfig _keyboardConfig;
public KeyboardInputConfig KeyboardConfig
{
get => _keyboardConfig;
set
{
_keyboardConfig = value;
OnPropertyChanged();
}
}
private (float, float) _uiStickLeft;
public (float, float) UiStickLeft
{
get => (_uiStickLeft.Item1 * DrawStickScaleFactor, _uiStickLeft.Item2 * DrawStickScaleFactor);
set
{
_uiStickLeft = value;
OnPropertyChanged();
OnPropertyChanged(nameof(UiStickRightX));
OnPropertyChanged(nameof(UiStickRightY));
OnPropertyChanged(nameof(UiDeadzoneRight));
}
}
private (float, float) _uiStickRight;
public (float, float) UiStickRight
{
get => (_uiStickRight.Item1 * DrawStickScaleFactor, _uiStickRight.Item2 * DrawStickScaleFactor);
set
{
_uiStickRight = value;
OnPropertyChanged();
OnPropertyChanged(nameof(UiStickLeftX));
OnPropertyChanged(nameof(UiStickLeftY));
OnPropertyChanged(nameof(UiDeadzoneLeft));
}
}
public float UiStickLeftX => ClampVector(UiStickLeft).Item1;
public float UiStickLeftY => ClampVector(UiStickLeft).Item2;
public float UiStickRightX => ClampVector(UiStickRight).Item1;
public float UiStickRightY => ClampVector(UiStickRight).Item2;
public int UiStickCircumference => DrawStickCircumference;
public int UiCanvasSize => DrawStickCanvasSize;
public int UiStickBorderSize => DrawStickBorderSize;
public float? UiDeadzoneLeft => _gamepadConfig?.DeadzoneLeft * DrawStickCanvasSize - DrawStickCircumference;
public float? UiDeadzoneRight => _gamepadConfig?.DeadzoneRight * DrawStickCanvasSize - DrawStickCircumference;
private InputViewModel Parent;
public StickVisualizer(InputViewModel parent)
{
Parent = parent;
PollTokenSource = new CancellationTokenSource();
PollToken = PollTokenSource.Token;
Task.Run(Initialize, PollToken);
}
public void UpdateConfig(object config)
{
if (config is ControllerInputViewModel padConfig)
{
GamepadConfig = padConfig.Config;
Type = DeviceType.Controller;
return;
}
else if (config is KeyboardInputViewModel keyConfig)
{
KeyboardConfig = keyConfig.Config;
Type = DeviceType.Keyboard;
return;
}
Type = DeviceType.None;
}
public async Task Initialize()
{
(float, float) leftBuffer;
(float, float) rightBuffer;
while (!PollToken.IsCancellationRequested)
{
leftBuffer = (0f, 0f);
rightBuffer = (0f, 0f);
switch (Type)
{
case DeviceType.Keyboard:
IKeyboard keyboard = (IKeyboard)Parent.AvaloniaKeyboardDriver.GetGamepad("0");
if (keyboard != null)
{
KeyboardStateSnapshot snapshot = keyboard.GetKeyboardStateSnapshot();
if (snapshot.IsPressed((Key)KeyboardConfig.LeftStickRight))
{
leftBuffer.Item1 += 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.LeftStickLeft))
{
leftBuffer.Item1 -= 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.LeftStickUp))
{
leftBuffer.Item2 += 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.LeftStickDown))
{
leftBuffer.Item2 -= 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.RightStickRight))
{
rightBuffer.Item1 += 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.RightStickLeft))
{
rightBuffer.Item1 -= 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.RightStickUp))
{
rightBuffer.Item2 += 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.RightStickDown))
{
rightBuffer.Item2 -= 1;
}
UiStickLeft = leftBuffer;
UiStickRight = rightBuffer;
}
break;
case DeviceType.Controller:
IGamepad controller = Parent.SelectedGamepad;
if (controller != null)
{
leftBuffer = controller.GetStick((StickInputId)GamepadConfig.LeftJoystick);
rightBuffer = controller.GetStick((StickInputId)GamepadConfig.RightJoystick);
}
break;
case DeviceType.None:
break;
default:
throw new ArgumentException($"Unable to poll device type \"{Type}\"");
}
UiStickLeft = leftBuffer;
UiStickRight = rightBuffer;
await Task.Delay(DrawStickPollRate, PollToken);
}
PollTokenSource.Dispose();
}
public static (float, float) ClampVector((float, float) vect)
{
_vectorMultiplier = 1;
_vectorLength = MathF.Sqrt((vect.Item1 * vect.Item1) + (vect.Item2 * vect.Item2));
if (_vectorLength > MaxVectorLength)
{
_vectorMultiplier = MaxVectorLength / _vectorLength;
}
vect.Item1 = vect.Item1 * _vectorMultiplier + DrawStickCanvasCenter;
vect.Item2 = vect.Item2 * _vectorMultiplier + DrawStickCanvasCenter;
return vect;
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
PollTokenSource.Cancel();
}
KeyboardConfig = null;
GamepadConfig = null;
Parent = null;
disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}

View File

@ -3,7 +3,7 @@ using LibHac.Fs;
using LibHac.Ncm;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.AppLibrary;
using Ryujinx.HLE.FileSystem;
using System.IO;
using System.Linq;

View File

@ -1,7 +1,7 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Platform;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Helper;
using SPB.Graphics;

View File

@ -1,5 +1,5 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;

View File

@ -1,10 +1,8 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using System;
namespace Ryujinx.Ava.UI.Renderer
@ -38,32 +36,6 @@ namespace Ryujinx.Ava.UI.Renderer
EmbeddedWindowOpenGL => GraphicsBackend.OpenGl,
_ => throw new NotImplementedException()
};
public RendererHost(string titleId)
{
Focusable = true;
FlowDirection = FlowDirection.LeftToRight;
EmbeddedWindow =
#pragma warning disable CS8524
ConfigurationState.Instance.Graphics.GraphicsBackend.Value switch
#pragma warning restore CS8524
{
GraphicsBackend.OpenGl => new EmbeddedWindowOpenGL(),
GraphicsBackend.Vulkan => new EmbeddedWindowVulkan(),
};
string backendText = EmbeddedWindow switch
{
EmbeddedWindowVulkan => "Vulkan",
EmbeddedWindowOpenGL => "OpenGL",
_ => throw new NotImplementedException()
};
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend ({ConfigurationState.Instance.Graphics.GraphicsBackend.Value}): {backendText}");
Initialize();
}
private void Initialize()

View File

@ -17,4 +17,9 @@
<sty:FluentAvaloniaTheme PreferUserAccentColor="True" PreferSystemTheme="False" />
<StyleInclude Source="/Assets/Styles/Styles.xaml" />
</Application.Styles>
<NativeMenu.Menu>
<NativeMenu>
<NativeMenuItem Header="About Ryujinx" Click="AboutRyujinx_OnClick" />
</NativeMenu>
</NativeMenu.Menu>
</Application>

View File

@ -7,12 +7,12 @@ using Avalonia.Styling;
using Avalonia.Threading;
using FluentAvalonia.UI.Windowing;
using Gommon;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Ava.UI.Views.Dialog;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using System;
@ -147,5 +147,10 @@ namespace Ryujinx.Ava
Current is RyujinxApp { PlatformSettings: not null } app
? ConvertThemeVariant(app.PlatformSettings.GetColorValues().ThemeVariant)
: ThemeVariant.Default;
private async void AboutRyujinx_OnClick(object sender, EventArgs e)
{
await AboutView.Show();
}
}
}

View File

@ -3,9 +3,8 @@ using Avalonia.Styling;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using Gommon;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems.Configuration;
using System;
namespace Ryujinx.Ava.UI.ViewModels

View File

@ -1,4 +1,4 @@
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.AppLibrary;
using System;
namespace Ryujinx.Ava.UI.ViewModels

View File

@ -1,7 +1,7 @@
using Gommon;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Utilities.PlayReport;
using Ryujinx.Ava.Systems.AppLibrary;
using Ryujinx.Ava.Systems.PlayReport;
namespace Ryujinx.Ava.UI.ViewModels
{

View File

@ -1,16 +1,17 @@
using Gommon;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems;
using Ryujinx.Ava.Systems.AppLibrary;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Ava.Utilities.Compat
namespace Ryujinx.Ava.UI.ViewModels
{
public class CompatibilityViewModel : BaseModel
public class CompatibilityViewModel : BaseModel, IDisposable
{
private bool _onlyShowOwnedGames = true;
private readonly ApplicationLibrary _appLibrary;
private IEnumerable<CompatibilityEntry> _currentEntries = CompatibilityCsv.Entries;
private IEnumerable<CompatibilityEntry> _currentEntries = CompatibilityDatabase.Entries;
private string[] _ownedGameTitleIds = [];
public IEnumerable<CompatibilityEntry> CurrentEntries => OnlyShowOwnedGames
@ -19,15 +20,27 @@ namespace Ryujinx.Ava.Utilities.Compat
: _currentEntries;
public CompatibilityViewModel() {}
private void AppCountUpdated(object _, ApplicationCountUpdatedEventArgs __)
=> _ownedGameTitleIds = _appLibrary.Applications.Keys.Select(x => x.ToString("X16")).ToArray();
public CompatibilityViewModel(ApplicationLibrary appLibrary)
{
appLibrary.ApplicationCountUpdated += (_, _)
=> _ownedGameTitleIds = appLibrary.Applications.Keys.Select(x => x.ToString("X16")).ToArray();
_appLibrary = appLibrary;
_ownedGameTitleIds = appLibrary.Applications.Keys.Select(x => x.ToString("X16")).ToArray();
AppCountUpdated(null, null);
_appLibrary.ApplicationCountUpdated += AppCountUpdated;
}
void IDisposable.Dispose()
{
GC.SuppressFinalize(this);
_appLibrary.ApplicationCountUpdated -= AppCountUpdated;
}
private bool _onlyShowOwnedGames = true;
public bool OnlyShowOwnedGames
{
get => _onlyShowOwnedGames;
@ -45,11 +58,11 @@ namespace Ryujinx.Ava.Utilities.Compat
{
if (string.IsNullOrEmpty(searchTerm))
{
SetEntries(CompatibilityCsv.Entries);
SetEntries(CompatibilityDatabase.Entries);
return;
}
SetEntries(CompatibilityCsv.Entries.Where(x =>
SetEntries(CompatibilityDatabase.Entries.Where(x =>
x.GameName.ContainsIgnoreCase(searchTerm)
|| x.TitleId.Check(tid => tid.ContainsIgnoreCase(searchTerm))));
}

View File

@ -1,6 +1,6 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.AppLibrary;
using System.Linq;
namespace Ryujinx.Ava.UI.ViewModels

View File

@ -7,7 +7,7 @@ using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.AppLibrary;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;

View File

@ -10,8 +10,30 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{
public partial class ControllerInputViewModel : BaseModel
{
[ObservableProperty] private GamepadInputConfig _config;
private GamepadInputConfig _config;
public GamepadInputConfig Config
{
get => _config;
set
{
_config = value;
OnPropertyChanged();
}
}
private StickVisualizer _visualizer;
public StickVisualizer Visualizer
{
get => _visualizer;
set
{
_visualizer = value;
OnPropertyChanged();
}
}
private bool _isLeft;
public bool IsLeft
{
@ -37,14 +59,15 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
}
public bool HasSides => IsLeft ^ IsRight;
[ObservableProperty] private SvgImage _image;
public InputViewModel ParentModel { get; }
public ControllerInputViewModel(InputViewModel model, GamepadInputConfig config)
public ControllerInputViewModel(InputViewModel model, GamepadInputConfig config, StickVisualizer visualizer)
{
ParentModel = model;
Visualizer = visualizer;
model.NotifyChangesEvent += OnParentModelChanged;
OnParentModelChanged();
config.PropertyChanged += (_, args) =>

View File

@ -10,7 +10,7 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.Models.Input;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
@ -49,7 +49,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
private int _controller;
private string _controllerImage;
private int _device;
[ObservableProperty] private object _configViewModel;
private object _configViewModel;
[ObservableProperty] private string _profileName;
private bool _isLoaded;
@ -74,6 +74,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
OnPropertiesChanged(nameof(HasLed), nameof(CanClearLed));
}
}
public StickVisualizer VisualStick { get; private set; }
public ObservableCollection<PlayerModel> PlayerIndexes { get; set; }
public ObservableCollection<(DeviceType Type, string Id, string Name)> Devices { get; set; }
@ -94,6 +95,19 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
public bool IsModified { get; set; }
public event Action NotifyChangesEvent;
public object ConfigViewModel
{
get => _configViewModel;
set
{
_configViewModel = value;
VisualStick.UpdateConfig(value);
OnPropertyChanged();
}
}
public PlayerIndex PlayerIdChoose
{
get => _playerIdChoose;
@ -269,6 +283,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
Devices = [];
ProfilesList = [];
DeviceList = [];
VisualStick = new StickVisualizer(this);
ControllerImage = ProControllerResource;
@ -289,12 +304,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
if (Config is StandardKeyboardInputConfig keyboardInputConfig)
{
ConfigViewModel = new KeyboardInputViewModel(this, new KeyboardInputConfig(keyboardInputConfig));
ConfigViewModel = new KeyboardInputViewModel(this, new KeyboardInputConfig(keyboardInputConfig), VisualStick);
}
if (Config is StandardControllerInputConfig controllerInputConfig)
{
ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig));
ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig), VisualStick);
}
}
@ -893,6 +908,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
_mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates();
VisualStick.Dispose();
SelectedGamepad?.Dispose();
AvaloniaKeyboardDriver.Dispose();

View File

@ -6,7 +6,29 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{
public partial class KeyboardInputViewModel : BaseModel
{
[ObservableProperty] private KeyboardInputConfig _config;
private KeyboardInputConfig _config;
public KeyboardInputConfig Config
{
get => _config;
set
{
_config = value;
OnPropertyChanged();
}
}
private StickVisualizer _visualizer;
public StickVisualizer Visualizer
{
get => _visualizer;
set
{
_visualizer = value;
OnPropertyChanged();
}
}
private bool _isLeft;
public bool IsLeft
@ -38,9 +60,10 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
public readonly InputViewModel ParentModel;
public KeyboardInputViewModel(InputViewModel model, KeyboardInputConfig config)
public KeyboardInputViewModel(InputViewModel model, KeyboardInputConfig config, StickVisualizer visualizer)
{
ParentModel = model;
Visualizer = visualizer;
model.NotifyChangesEvent += OnParentModelChanged;
OnParentModelChanged();
Config = config;

View File

@ -3,7 +3,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Humanizer;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems.Configuration;
using System.Globalization;
namespace Ryujinx.Ava.UI.ViewModels.Input

View File

@ -3,7 +3,6 @@ using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Input;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
@ -17,14 +16,15 @@ using LibHac.Ns;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Input;
using Ryujinx.Ava.Systems;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.Models.Generic;
using Ryujinx.Ava.UI.Renderer;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems.AppLibrary;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Helper;
@ -46,7 +46,6 @@ using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Key = Ryujinx.Input.Key;
@ -137,13 +136,6 @@ namespace Ryujinx.Ava.UI.ViewModels
// Key is Title ID
public SafeDictionary<string, LdnGameData.Array> LdnData = [];
// The UI specifically uses a thicker bordered variant of the icon to avoid crunching out the border at lower resolutions.
// For an example of this, download canary 1.2.95, then open the settings menu, and look at the icon in the top-left.
// The border gets reduced to colored pixels in the 4 corners.
public static readonly Bitmap IconBitmap =
new(Assembly.GetAssembly(typeof(MainWindowViewModel))!
.GetManifestResourceStream("Ryujinx.Assets.UIImages.Logo_Ryujinx_AntiAlias.png")!);
public MainWindow Window { get; init; }
internal AppHost AppHost { get; set; }
@ -1794,7 +1786,7 @@ namespace Ryujinx.Ava.UI.ViewModels
if (WindowState is not WindowState.Normal)
{
WindowState = WindowState.Normal;
Window.TitleBar.ExtendsContentIntoTitleBar = !ConfigurationState.Instance.ShowTitleBar;
Window.TitleBar.ExtendsContentIntoTitleBar = !ConfigurationState.Instance.ShowOldUI;
if (IsGameRunning)
{

View File

@ -7,7 +7,7 @@ using Gommon;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.AppLibrary;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;

View File

@ -1,6 +1,6 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Gommon;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Systems.Configuration;
namespace Ryujinx.Ava.UI.ViewModels
{
@ -16,11 +16,12 @@ namespace Ryujinx.Ava.UI.ViewModels
}
[ObservableProperty] private bool _xc2MenuSoftlockFix = ConfigurationState.Instance.Hacks.Xc2MenuSoftlockFix;
[ObservableProperty] private bool _nifmDisableIsAnyInternetRequestAccepted = ConfigurationState.Instance.Hacks.DisableNifmIsAnyInternetRequestAccepted;
public static string Xc2MenuFixTooltip { get; } = Lambda.String(sb =>
{
sb.AppendLine(
"This fix applies a 2ms delay (via 'Thread.Sleep(2)') every time the game tries to read data from the emulated Switch filesystem.")
"This hack applies a 2ms delay (via 'Thread.Sleep(2)') every time the game tries to read data from the emulated Switch filesystem.")
.AppendLine();
sb.AppendLine("From the issue on GitHub:").AppendLine();
@ -29,5 +30,14 @@ namespace Ryujinx.Ava.UI.ViewModels
"there is a low chance that the game will softlock, " +
"the submenu won't show up, while background music is still there.");
});
public static string NifmDisableIsAnyInternetRequestAcceptedTooltip { get; } = Lambda.String(sb =>
{
sb.AppendLine(
"This hack simply sets 'IsAnyInternetRequestAccepted' to 'false' when initializing the Nifm IGeneralService.")
.AppendLine();
sb.Append("Lets DOOM 2016 go in game.");
});
}
}

View File

@ -12,9 +12,9 @@ using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models.Input;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Utilities.Configuration.System;
using Ryujinx.Ava.Utilities.Configuration.UI;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Ava.Systems.Configuration.System;
using Ryujinx.Ava.Systems.Configuration.UI;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Multiplayer;
using Ryujinx.Common.GraphicsDriver;
@ -130,11 +130,10 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool IsOpenGLAvailable => !OperatingSystem.IsMacOS();
public bool EnableDiscordIntegration { get; set; }
public bool CheckUpdatesOnStart { get; set; }
public bool ShowConfirmExit { get; set; }
public bool IgnoreApplet { get; set; }
public bool RememberWindowState { get; set; }
public bool ShowTitleBar { get; set; }
public bool ShowOldUI { get; set; }
public int HideCursor { get; set; }
public int UpdateCheckerType { get; set; }
public bool EnableDockedMode { get; set; }
@ -537,10 +536,9 @@ namespace Ryujinx.Ava.UI.ViewModels
// User Interface
EnableDiscordIntegration = config.EnableDiscordIntegration;
CheckUpdatesOnStart = config.CheckUpdatesOnStart;
ShowConfirmExit = config.ShowConfirmExit;
RememberWindowState = config.RememberWindowState;
ShowTitleBar = config.ShowTitleBar;
ShowOldUI = config.ShowOldUI;
HideCursor = (int)config.HideCursor.Value;
UpdateCheckerType = (int)config.UpdateCheckerType.Value;
FocusLostActionType = (int)config.FocusLostActionType.Value;
@ -646,10 +644,9 @@ namespace Ryujinx.Ava.UI.ViewModels
// User Interface
config.EnableDiscordIntegration.Value = EnableDiscordIntegration;
config.CheckUpdatesOnStart.Value = CheckUpdatesOnStart;
config.ShowConfirmExit.Value = ShowConfirmExit;
config.RememberWindowState.Value = RememberWindowState;
config.ShowTitleBar.Value = ShowTitleBar;
config.ShowOldUI.Value = ShowOldUI;
config.HideCursor.Value = (HideCursorMode)HideCursor;
config.UpdateCheckerType.Value = (UpdaterType)UpdateCheckerType;
config.FocusLostActionType.Value = (FocusLostType)FocusLostActionType;
@ -760,6 +757,8 @@ namespace Ryujinx.Ava.UI.ViewModels
// Dirty Hacks
config.Hacks.Xc2MenuSoftlockFix.Value = DirtyHacks.Xc2MenuSoftlockFix;
config.Hacks.DisableNifmIsAnyInternetRequestAccepted.Value =
DirtyHacks.NifmDisableIsAnyInternetRequestAccepted;
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);

View File

@ -6,7 +6,7 @@ using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Systems.AppLibrary;
using System.Collections.Generic;
using System.IO;
using System.Linq;

View File

@ -21,7 +21,7 @@ using Image = SkiaSharp.SKImage;
namespace Ryujinx.Ava.UI.ViewModels
{
internal partial class UserFirmwareAvatarSelectorViewModel : BaseModel
public partial class UserFirmwareAvatarSelectorViewModel : BaseModel
{
private static readonly Dictionary<string, byte[]> _avatarStore = new();

View File

@ -2,7 +2,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace Ryujinx.Ava.UI.ViewModels
{
internal partial class UserProfileImageSelectorViewModel : BaseModel
public partial class UserProfileImageSelectorViewModel : BaseModel
{
[ObservableProperty] private bool _firmwareFound;
}

Some files were not shown because too many files have changed in this diff Show More