using ARMeilleure; using Gommon; 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; using Ryujinx.Common.Configuration.Multiplayer; using Ryujinx.Common.Helper; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; using Ryujinx.HLE; using System.Collections.Generic; using System.Linq; using RyuLogger = Ryujinx.Common.Logging.Logger; namespace Ryujinx.Ava.Systems.Configuration { public partial class ConfigurationState { /// /// UI configuration section /// public class UISection { public class Columns { public ReactiveObject FavColumn { get; private set; } public ReactiveObject IconColumn { get; private set; } public ReactiveObject AppColumn { get; private set; } public ReactiveObject DevColumn { get; private set; } public ReactiveObject VersionColumn { get; private set; } public ReactiveObject LdnInfoColumn { get; private set; } public ReactiveObject TimePlayedColumn { get; private set; } public ReactiveObject LastPlayedColumn { get; private set; } public ReactiveObject FileExtColumn { get; private set; } public ReactiveObject FileSizeColumn { get; private set; } public ReactiveObject PathColumn { get; private set; } public Columns() { FavColumn = new ReactiveObject(); IconColumn = new ReactiveObject(); AppColumn = new ReactiveObject(); DevColumn = new ReactiveObject(); VersionColumn = new ReactiveObject(); LdnInfoColumn = new ReactiveObject(); TimePlayedColumn = new ReactiveObject(); LastPlayedColumn = new ReactiveObject(); FileExtColumn = new ReactiveObject(); FileSizeColumn = new ReactiveObject(); PathColumn = new ReactiveObject(); } } public class ColumnSortSettings { public ReactiveObject SortColumnId { get; private set; } public ReactiveObject SortAscending { get; private set; } public ColumnSortSettings() { SortColumnId = new ReactiveObject(); SortAscending = new ReactiveObject(); } } /// /// Used to toggle which file types are shown in the UI /// public class ShownFileTypeSettings { public ReactiveObject NSP { get; private set; } public ReactiveObject PFS0 { get; private set; } public ReactiveObject XCI { get; private set; } public ReactiveObject NCA { get; private set; } public ReactiveObject NRO { get; private set; } public ReactiveObject NSO { get; private set; } public ShownFileTypeSettings() { NSP = new ReactiveObject(); PFS0 = new ReactiveObject(); XCI = new ReactiveObject(); NCA = new ReactiveObject(); NRO = new ReactiveObject(); NSO = new ReactiveObject(); } } // /// Determines main window start-up position, size and state /// public class WindowStartupSettings { public ReactiveObject WindowSizeWidth { get; private set; } public ReactiveObject WindowSizeHeight { get; private set; } public ReactiveObject WindowPositionX { get; private set; } public ReactiveObject WindowPositionY { get; private set; } public ReactiveObject WindowMaximized { get; private set; } public WindowStartupSettings() { WindowSizeWidth = new ReactiveObject(); WindowSizeHeight = new ReactiveObject(); WindowPositionX = new ReactiveObject(); WindowPositionY = new ReactiveObject(); WindowMaximized = new ReactiveObject(); } } /// /// Used to toggle columns in the GUI /// public Columns GuiColumns { get; private set; } /// /// Used to configure column sort settings in the GUI /// public ColumnSortSettings ColumnSort { get; private set; } /// /// A list of directories containing games to be used to load games into the games list /// public ReactiveObject> GameDirs { get; private set; } /// /// A list of directories containing DLC/updates the user wants to autoload during library refreshes /// public ReactiveObject> AutoloadDirs { get; private set; } /// /// A list of file types to be hidden in the games List /// public ShownFileTypeSettings ShownFileTypes { get; private set; } /// /// Determines main window start-up position, size and state /// public WindowStartupSettings WindowStartup { get; private set; } /// /// Language Code for the UI /// public ReactiveObject LanguageCode { get; private set; } /// /// Selects the base style /// public ReactiveObject BaseStyle { get; private set; } /// /// Start games in fullscreen mode /// public ReactiveObject StartFullscreen { get; private set; } /// /// Start games with UI hidden /// public ReactiveObject StartNoUI { get; private set; } /// /// Hide / Show Console Window /// public ReactiveObject ShowConsole { get; private set; } /// /// View Mode of the Game list /// public ReactiveObject GameListViewMode { get; private set; } /// /// Show application name in Grid Mode /// public ReactiveObject ShowNames { get; private set; } /// /// Sets App Icon Size in Grid Mode /// public ReactiveObject GridSize { get; private set; } /// /// Sorts Apps in Grid Mode /// public ReactiveObject ApplicationSort { get; private set; } /// /// Sets if Grid is ordered in Ascending Order /// public ReactiveObject IsAscendingOrder { get; private set; } public UISection() { GuiColumns = new Columns(); ColumnSort = new ColumnSortSettings(); GameDirs = new ReactiveObject>(); AutoloadDirs = new ReactiveObject>(); ShownFileTypes = new ShownFileTypeSettings(); WindowStartup = new WindowStartupSettings(); BaseStyle = new ReactiveObject(); StartFullscreen = new ReactiveObject(); StartNoUI = new ReactiveObject(); GameListViewMode = new ReactiveObject(); ShowNames = new ReactiveObject(); GridSize = new ReactiveObject(); ApplicationSort = new ReactiveObject(); IsAscendingOrder = new ReactiveObject(); LanguageCode = new ReactiveObject(); ShowConsole = new ReactiveObject(); ShowConsole.Event += static (_, e) => ConsoleHelper.SetConsoleWindowState(e.NewValue); } } /// /// Logger configuration section /// public class LoggerSection { /// /// Enables printing debug log messages /// public ReactiveObject EnableDebug { get; private set; } /// /// Enables printing stub log messages /// public ReactiveObject EnableStub { get; private set; } /// /// Enables printing info log messages /// public ReactiveObject EnableInfo { get; private set; } /// /// Enables printing warning log messages /// public ReactiveObject EnableWarn { get; private set; } /// /// Enables printing error log messages /// public ReactiveObject EnableError { get; private set; } /// /// Enables printing trace log messages /// public ReactiveObject EnableTrace { get; private set; } /// /// Enables printing guest log messages /// public ReactiveObject EnableGuest { get; private set; } /// /// Enables printing FS access log messages /// public ReactiveObject EnableFsAccessLog { get; private set; } /// /// Enables log messages from Avalonia /// public ReactiveObject EnableAvaloniaLog { get; private set; } /// /// Controls which log messages are written to the log targets /// public ReactiveObject FilteredClasses { get; private set; } /// /// Enables or disables logging to a file on disk /// public ReactiveObject EnableFileLog { get; private set; } /// /// Controls which OpenGL log messages are recorded in the log /// public ReactiveObject GraphicsDebugLevel { get; private set; } public LoggerSection() { EnableDebug = new ReactiveObject(); EnableDebug.LogChangesToValue(nameof(EnableDebug)); EnableStub = new ReactiveObject(); EnableInfo = new ReactiveObject(); EnableWarn = new ReactiveObject(); EnableError = new ReactiveObject(); EnableTrace = new ReactiveObject(); EnableGuest = new ReactiveObject(); EnableFsAccessLog = new ReactiveObject(); EnableAvaloniaLog = new ReactiveObject(); FilteredClasses = new ReactiveObject(); EnableFileLog = new ReactiveObject(); EnableFileLog.LogChangesToValue(nameof(EnableFileLog)); GraphicsDebugLevel = new ReactiveObject(); } } /// /// System configuration section /// public class SystemSection { /// /// Change System Language /// public ReactiveObject Language { get; private set; } /// /// Change System Region /// public ReactiveObject Region { get; private set; } /// /// Change System TimeZone /// public ReactiveObject TimeZone { get; private set; } /// /// System Time Offset in Seconds /// public ReactiveObject SystemTimeOffset { get; private set; } /// /// Instead of setting the time via configuration, use the values provided by the system. /// public ReactiveObject MatchSystemTime { get; private set; } /// /// Enables or disables Docked Mode /// public ReactiveObject EnableDockedMode { get; private set; } /// /// Enables or disables persistent profiled translation cache /// public ReactiveObject EnablePtc { get; private set; } /// /// Clock tick scalar, in percent points (100 = 1.0). /// public ReactiveObject TickScalar { get; set; } /// /// Enables or disables low-power persistent profiled translation cache loading /// public ReactiveObject EnableLowPowerPtc { get; private set; } /// /// Enables or disables guest Internet access /// public ReactiveObject EnableInternetAccess { get; private set; } /// /// Enables integrity checks on Game content files /// public ReactiveObject EnableFsIntegrityChecks { get; private set; } public IntegrityCheckLevel IntegrityCheckLevel => EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None; /// /// Enables FS access log output to the console. Possible modes are 0-3 /// public ReactiveObject FsGlobalAccessLogMode { get; private set; } /// /// The selected audio backend /// public ReactiveObject AudioBackend { get; private set; } /// /// The audio backend volume /// public ReactiveObject AudioVolume { get; private set; } /// /// The selected memory manager mode /// public ReactiveObject MemoryManagerMode { get; private set; } /// /// Defines the amount of RAM available on the emulated system, and how it is distributed /// public ReactiveObject DramSize { get; private set; } /// /// Enable or disable ignoring missing services /// public ReactiveObject IgnoreMissingServices { get; private set; } /// /// Ignore Controller Applet /// public ReactiveObject IgnoreControllerApplet { get; private set; } /// /// Skip User Profiles Manager /// public ReactiveObject SkipUserProfilesManager { get; private set; } /// /// Uses Hypervisor over JIT if available /// public ReactiveObject UseHypervisor { get; private set; } public SystemSection() { Language = new ReactiveObject(); Language.LogChangesToValue(nameof(Language)); Region = new ReactiveObject(); Region.LogChangesToValue(nameof(Region)); TimeZone = new ReactiveObject(); TimeZone.LogChangesToValue(nameof(TimeZone)); SystemTimeOffset = new ReactiveObject(); SystemTimeOffset.LogChangesToValue(nameof(SystemTimeOffset)); MatchSystemTime = new ReactiveObject(); MatchSystemTime.LogChangesToValue(nameof(MatchSystemTime)); EnableDockedMode = new ReactiveObject(); EnableDockedMode.LogChangesToValue(nameof(EnableDockedMode)); EnablePtc = new ReactiveObject(); EnablePtc.LogChangesToValue(nameof(EnablePtc)); EnableLowPowerPtc = new ReactiveObject(); EnableLowPowerPtc.LogChangesToValue(nameof(EnableLowPowerPtc)); EnableLowPowerPtc.Event += (_, evnt) => Optimizations.LowPower = evnt.NewValue; TickScalar = new ReactiveObject(); TickScalar.LogChangesToValue(nameof(TickScalar)); TickScalar.Event += (_, evnt) => { if (Switch.Shared is null) return; Switch.Shared.Configuration.TickScalar = evnt.NewValue; }; EnableInternetAccess = new ReactiveObject(); EnableInternetAccess.LogChangesToValue(nameof(EnableInternetAccess)); EnableFsIntegrityChecks = new ReactiveObject(); EnableFsIntegrityChecks.LogChangesToValue(nameof(EnableFsIntegrityChecks)); FsGlobalAccessLogMode = new ReactiveObject(); FsGlobalAccessLogMode.LogChangesToValue(nameof(FsGlobalAccessLogMode)); AudioBackend = new ReactiveObject(); AudioBackend.LogChangesToValue(nameof(AudioBackend)); MemoryManagerMode = new ReactiveObject(); MemoryManagerMode.LogChangesToValue(nameof(MemoryManagerMode)); DramSize = new ReactiveObject(); DramSize.LogChangesToValue(nameof(DramSize)); IgnoreMissingServices = new ReactiveObject(); IgnoreMissingServices.LogChangesToValue(nameof(IgnoreMissingServices)); IgnoreControllerApplet = new ReactiveObject(); IgnoreControllerApplet.LogChangesToValue(nameof(IgnoreControllerApplet)); SkipUserProfilesManager = new ReactiveObject(); SkipUserProfilesManager.LogChangesToValue(nameof(SkipUserProfilesManager)); AudioVolume = new ReactiveObject(); AudioVolume.LogChangesToValue(nameof(AudioVolume)); UseHypervisor = new ReactiveObject(); UseHypervisor.LogChangesToValue(nameof(UseHypervisor)); } } /// /// Hid configuration section /// public class HidSection { /// /// Enable or disable keyboard support (Independent from controllers binding) /// public ReactiveObject EnableKeyboard { get; private set; } /// /// Enable or disable mouse support (Independent from controllers binding) /// public ReactiveObject EnableMouse { get; private set; } /// /// Enable/disable the ability to control Ryujinx when it's not the currently focused window. /// public ReactiveObject DisableInputWhenOutOfFocus { get; private set; } /// /// Hotkey Keyboard Bindings /// public ReactiveObject Hotkeys { get; private set; } /// /// Input device configuration. /// NOTE: This ReactiveObject won't issue an event when the List has elements added or removed. /// TODO: Implement a ReactiveList class. /// public ReactiveObject> InputConfig { get; private set; } /// /// The speed of spectrum cycling for the Rainbow LED feature. /// public ReactiveObject RainbowSpeed { get; } public HidSection() { EnableKeyboard = new ReactiveObject(); EnableMouse = new ReactiveObject(); DisableInputWhenOutOfFocus = new ReactiveObject(); Hotkeys = new ReactiveObject(); InputConfig = new ReactiveObject>(); RainbowSpeed = new ReactiveObject(); RainbowSpeed.Event += (_, args) => Rainbow.Speed = args.NewValue; } } /// /// Graphics configuration section /// public class GraphicsSection { /// /// Whether or not backend threading is enabled. The "Auto" setting will determine whether threading should be enabled at runtime. /// public ReactiveObject BackendThreading { get; private set; } /// /// Max Anisotropy. Values range from 0 - 16. Set to -1 to let the game decide. /// public ReactiveObject MaxAnisotropy { get; private set; } /// /// Aspect Ratio applied to the renderer window. /// public ReactiveObject AspectRatio { get; private set; } /// /// Resolution Scale. An integer scale applied to applicable render targets. Values 1-4, or -1 to use a custom floating point scale instead. /// public ReactiveObject ResScale { get; private set; } /// /// Custom Resolution Scale. A custom floating point scale applied to applicable render targets. Only active when Resolution Scale is -1. /// public ReactiveObject ResScaleCustom { get; private set; } /// /// Dumps shaders in this local directory /// public ReactiveObject ShadersDumpPath { get; private set; } /// /// Toggles the present interval mode. Options are Switch (60Hz), Unbounded (previously Vsync off), and Custom, if enabled. /// public ReactiveObject VSyncMode { get; private set; } /// /// Enables or disables the custom present interval mode. /// public ReactiveObject EnableCustomVSyncInterval { get; private set; } /// /// Changes the custom present interval. /// public ReactiveObject CustomVSyncInterval { get; private set; } /// /// Enables or disables Shader cache /// public ReactiveObject EnableShaderCache { get; private set; } /// /// Enables or disables texture recompression /// public ReactiveObject EnableTextureRecompression { get; private set; } /// /// Enables or disables Macro high-level emulation /// public ReactiveObject EnableMacroHLE { get; private set; } /// /// Enables or disables color space passthrough, if available. /// public ReactiveObject EnableColorSpacePassthrough { get; private set; } /// /// Graphics backend /// public ReactiveObject GraphicsBackend { get; private set; } /// /// Applies anti-aliasing to the renderer. /// public ReactiveObject AntiAliasing { get; private set; } /// /// Sets the framebuffer upscaling type. /// public ReactiveObject ScalingFilter { get; private set; } /// /// Sets the framebuffer upscaling level. /// public ReactiveObject ScalingFilterLevel { get; private set; } /// /// Preferred GPU /// public ReactiveObject PreferredGpu { get; private set; } public GraphicsSection() { BackendThreading = new ReactiveObject(); BackendThreading.LogChangesToValue(nameof(BackendThreading)); ResScale = new ReactiveObject(); ResScale.LogChangesToValue(nameof(ResScale)); ResScaleCustom = new ReactiveObject(); ResScaleCustom.LogChangesToValue(nameof(ResScaleCustom)); MaxAnisotropy = new ReactiveObject(); MaxAnisotropy.LogChangesToValue(nameof(MaxAnisotropy)); AspectRatio = new ReactiveObject(); AspectRatio.LogChangesToValue(nameof(AspectRatio)); ShadersDumpPath = new ReactiveObject(); VSyncMode = new ReactiveObject(); VSyncMode.LogChangesToValue(nameof(VSyncMode)); EnableCustomVSyncInterval = new ReactiveObject(); EnableCustomVSyncInterval.LogChangesToValue(nameof(EnableCustomVSyncInterval)); CustomVSyncInterval = new ReactiveObject(); CustomVSyncInterval.LogChangesToValue(nameof(CustomVSyncInterval)); EnableShaderCache = new ReactiveObject(); EnableShaderCache.LogChangesToValue(nameof(EnableShaderCache)); EnableTextureRecompression = new ReactiveObject(); EnableTextureRecompression.LogChangesToValue(nameof(EnableTextureRecompression)); GraphicsBackend = new ReactiveObject(); GraphicsBackend.LogChangesToValue(nameof(GraphicsBackend)); PreferredGpu = new ReactiveObject(); PreferredGpu.LogChangesToValue(nameof(PreferredGpu)); EnableMacroHLE = new ReactiveObject(); EnableMacroHLE.LogChangesToValue(nameof(EnableMacroHLE)); EnableColorSpacePassthrough = new ReactiveObject(); EnableColorSpacePassthrough.LogChangesToValue(nameof(EnableColorSpacePassthrough)); AntiAliasing = new ReactiveObject(); AntiAliasing.LogChangesToValue(nameof(AntiAliasing)); ScalingFilter = new ReactiveObject(); ScalingFilter.LogChangesToValue(nameof(ScalingFilter)); ScalingFilterLevel = new ReactiveObject(); ScalingFilterLevel.LogChangesToValue(nameof(ScalingFilterLevel)); } } /// /// Tweaks Section /// public class TweaksSection { /// /// Enable or disable Fix Occlusion Culling /// public ReactiveObject DisableFixOcclusionCulling { get; private set; } public TweaksSection() { DisableFixOcclusionCulling = new ReactiveObject(); } } /// /// Multiplayer configuration section /// public class MultiplayerSection { /// /// GUID for the network interface used by LAN (or 0 for default) /// public ReactiveObject LanInterfaceId { get; private set; } /// /// Multiplayer Mode /// public ReactiveObject Mode { get; private set; } /// /// Disable P2P /// public ReactiveObject DisableP2p { get; private set; } /// /// LDN PassPhrase /// public ReactiveObject LdnPassphrase { get; private set; } /// /// LDN Server /// public ReactiveObject LdnServer { get; private set; } public string GetLdnServer() { string ldnServer = LdnServer; return string.IsNullOrEmpty(ldnServer) ? SharedConstants.DefaultLanPlayHost : ldnServer; } public MultiplayerSection() { LanInterfaceId = new ReactiveObject(); Mode = new ReactiveObject(); Mode.LogChangesToValue(nameof(MultiplayerMode)); DisableP2p = new ReactiveObject(); DisableP2p.LogChangesToValue(nameof(DisableP2p)); LdnPassphrase = new ReactiveObject(); LdnPassphrase.LogChangesToValue(nameof(LdnPassphrase)); LdnServer = new ReactiveObject(); LdnServer.LogChangesToValue(nameof(LdnServer)); } } public class HacksSection { /// /// Show toggles for dirty hacks in the UI. /// public ReactiveObject ShowDirtyHacks { get; private set; } public ReactiveObject Xc2MenuSoftlockFix { get; private set; } public ReactiveObject DisableNifmIsAnyInternetRequestAccepted { get; private set; } public HacksSection() { ShowDirtyHacks = new ReactiveObject(); Xc2MenuSoftlockFix = new ReactiveObject(); Xc2MenuSoftlockFix.Event += HackChanged; DisableNifmIsAnyInternetRequestAccepted = new ReactiveObject(); DisableNifmIsAnyInternetRequestAccepted.Event += HackChanged; } private void HackChanged(object sender, ReactiveEventArgs rxe) { if (!ShowDirtyHacks) return; string newHacks = EnabledHacks.Select(x => x.Hack) .JoinToString(", "); if (newHacks != _lastHackCollection) { RyuLogger.Info?.Print(LogClass.Configuration, $"EnabledDirtyHacks set to: [{newHacks}]", "LogValueChange"); _lastHackCollection = newHacks; } } private static string _lastHackCollection; public EnabledDirtyHack[] EnabledHacks { get { List enabledHacks = []; if (Xc2MenuSoftlockFix) Apply(DirtyHack.Xc2MenuSoftlockFix); if (DisableNifmIsAnyInternetRequestAccepted) Apply(DirtyHack.NifmServiceDisableIsAnyInternetRequestAccepted); return enabledHacks.ToArray(); void Apply(DirtyHack hack, int value = 0) { enabledHacks.Add(new EnabledDirtyHack(hack, value)); } } } } /// /// The default configuration instance /// public static ConfigurationState Instance { get; private set; } /// /// The UI section /// public UISection UI { get; private set; } /// /// The Logger section /// public LoggerSection Logger { get; private set; } /// /// The System section /// public SystemSection System { get; private set; } /// /// The Graphics section /// public GraphicsSection Graphics { get; private set; } /// /// The Tweaks Section /// public TweaksSection Tweaks { get; private set; } /// /// The Hid section /// public HidSection Hid { get; private set; } /// /// The Multiplayer section /// public MultiplayerSection Multiplayer { get; private set; } /// /// The Dirty Hacks section /// public HacksSection Hacks { get; private set; } /// /// Enables or disables Discord Rich Presence /// public ReactiveObject EnableDiscordIntegration { get; private set; } /// /// Checks for updates when Ryujinx starts when enabled, either prompting when an update is found or just showing a notification. /// public ReactiveObject UpdateCheckerType { get; private set; } /// /// How the emulator should behave when you click off/on the window. /// public ReactiveObject FocusLostActionType { get; private set; } /// /// Show "Confirm Exit" Dialog /// public ReactiveObject ShowConfirmExit { get; private set; } /// /// Enables or disables save window size, position and state on close. /// public ReactiveObject RememberWindowState { get; private set; } /// /// Disable the new title bar layout & window layout changes. /// public ReactiveObject ShowOldUI { get; private set; } /// /// Enables hardware-accelerated rendering for Avalonia /// public ReactiveObject EnableHardwareAcceleration { get; private set; } /// /// Hide Cursor on Idle /// public ReactiveObject HideCursor { get; private set; } private ConfigurationState() { UI = new UISection(); Logger = new LoggerSection(); System = new SystemSection(); Graphics = new GraphicsSection(); Tweaks = new TweaksSection(); Hid = new HidSection(); Multiplayer = new MultiplayerSection(); Hacks = new HacksSection(); UpdateCheckerType = new ReactiveObject(); FocusLostActionType = new ReactiveObject(); HideCursor = new ReactiveObject(); EnableDiscordIntegration = new ReactiveObject(); ShowConfirmExit = new ReactiveObject(); RememberWindowState = new ReactiveObject(); ShowOldUI = new ReactiveObject(); EnableHardwareAcceleration = new ReactiveObject(); } public HleConfiguration CreateHleConfiguration() => new( System.DramSize, System.Language.Value.ToHLE(), System.Region.Value.ToHLE(), Graphics.VSyncMode, System.EnableDockedMode, System.EnablePtc, System.TickScalar, 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, Multiplayer.GetLdnServer(), Graphics.CustomVSyncInterval, Hacks.ShowDirtyHacks ? Hacks.EnabledHacks : null); } }