mirror of
https://github.com/GreemDev/Ryujinx.git
synced 2025-01-09 14:41:59 +00:00
misc: Dirty Hacks
Enable this settings screen via a boolean in Config.json First one is the xb2 menu softlock fix
This commit is contained in:
parent
09107b67ff
commit
8b3a945b5f
21 changed files with 222 additions and 22 deletions
11
src/Ryujinx.Common/Configuration/DirtyHacks.cs
Normal file
11
src/Ryujinx.Common/Configuration/DirtyHacks.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
public enum DirtyHacks
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Xc2MenuSoftlockFix = 1 << 10
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,8 @@ namespace Ryujinx.Common
|
||||||
{
|
{
|
||||||
public static class TitleIDs
|
public static class TitleIDs
|
||||||
{
|
{
|
||||||
|
public static Optional<string> CurrentApplication;
|
||||||
|
|
||||||
public static GraphicsBackend SelectGraphicsBackend(string titleId, GraphicsBackend currentBackend)
|
public static GraphicsBackend SelectGraphicsBackend(string titleId, GraphicsBackend currentBackend)
|
||||||
{
|
{
|
||||||
switch (currentBackend)
|
switch (currentBackend)
|
||||||
|
|
|
@ -47,12 +47,6 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool EnableMacroHLE = true;
|
public static bool EnableMacroHLE = true;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Title id of the current running game.
|
|
||||||
/// Used by the shader cache.
|
|
||||||
/// </summary>
|
|
||||||
public static string TitleId;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enables or disables the shader cache.
|
/// Enables or disables the shader cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
@ -116,8 +117,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static string GetDiskCachePath()
|
private static string GetDiskCachePath()
|
||||||
{
|
{
|
||||||
return GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null
|
return GraphicsConfig.EnableShaderCache && TitleIDs.CurrentApplication.HasValue
|
||||||
? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId, "cache", "shader")
|
? Path.Combine(AppDataManager.GamesDirPath, TitleIDs.CurrentApplication, "cache", "shader")
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -189,6 +189,11 @@ namespace Ryujinx.HLE
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Action RefreshInputConfig { internal get; set; }
|
public Action RefreshInputConfig { internal get; set; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The desired hacky workarounds.
|
||||||
|
*/
|
||||||
|
public DirtyHacks Hacks { internal get; set; }
|
||||||
|
|
||||||
public HLEConfiguration(VirtualFileSystem virtualFileSystem,
|
public HLEConfiguration(VirtualFileSystem virtualFileSystem,
|
||||||
LibHacHorizonManager libHacHorizonManager,
|
LibHacHorizonManager libHacHorizonManager,
|
||||||
ContentManager contentManager,
|
ContentManager contentManager,
|
||||||
|
@ -218,7 +223,8 @@ namespace Ryujinx.HLE
|
||||||
bool multiplayerDisableP2p,
|
bool multiplayerDisableP2p,
|
||||||
string multiplayerLdnPassphrase,
|
string multiplayerLdnPassphrase,
|
||||||
string multiplayerLdnServer,
|
string multiplayerLdnServer,
|
||||||
int customVSyncInterval)
|
int customVSyncInterval,
|
||||||
|
DirtyHacks dirtyHacks = DirtyHacks.None)
|
||||||
{
|
{
|
||||||
VirtualFileSystem = virtualFileSystem;
|
VirtualFileSystem = virtualFileSystem;
|
||||||
LibHacHorizonManager = libHacHorizonManager;
|
LibHacHorizonManager = libHacHorizonManager;
|
||||||
|
@ -250,6 +256,7 @@ namespace Ryujinx.HLE
|
||||||
MultiplayerDisableP2p = multiplayerDisableP2p;
|
MultiplayerDisableP2p = multiplayerDisableP2p;
|
||||||
MultiplayerLdnPassphrase = multiplayerLdnPassphrase;
|
MultiplayerLdnPassphrase = multiplayerLdnPassphrase;
|
||||||
MultiplayerLdnServer = multiplayerLdnServer;
|
MultiplayerLdnServer = multiplayerLdnServer;
|
||||||
|
Hacks = dirtyHacks;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
using LibHac;
|
using LibHac;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Sf;
|
using LibHac.Sf;
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Configuration;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
|
namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
|
||||||
{
|
{
|
||||||
|
@ -13,6 +16,8 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
|
||||||
_baseStorage = SharedRef<LibHac.FsSrv.Sf.IStorage>.CreateMove(ref baseStorage);
|
_baseStorage = SharedRef<LibHac.FsSrv.Sf.IStorage>.CreateMove(ref baseStorage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const string Xc2TitleId = "0100e95004038000";
|
||||||
|
|
||||||
[CommandCmif(0)]
|
[CommandCmif(0)]
|
||||||
// Read(u64 offset, u64 length) -> buffer<u8, 0x46, 0> buffer
|
// Read(u64 offset, u64 length) -> buffer<u8, 0x46, 0> buffer
|
||||||
public ResultCode Read(ServiceCtx context)
|
public ResultCode Read(ServiceCtx context)
|
||||||
|
@ -34,6 +39,13 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
|
||||||
using var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true);
|
using var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true);
|
||||||
Result result = _baseStorage.Get.Read((long)offset, new OutBuffer(region.Memory.Span), (long)size);
|
Result result = _baseStorage.Get.Read((long)offset, new OutBuffer(region.Memory.Span), (long)size);
|
||||||
|
|
||||||
|
if (context.Device.DirtyHacks.HasFlag(DirtyHacks.Xc2MenuSoftlockFix) && TitleIDs.CurrentApplication == Xc2TitleId)
|
||||||
|
{
|
||||||
|
// Add a load-bearing sleep to avoid XC2 softlock
|
||||||
|
// https://web.archive.org/web/20240728045136/https://github.com/Ryujinx/Ryujinx/issues/2357
|
||||||
|
Thread.Sleep(2);
|
||||||
|
}
|
||||||
|
|
||||||
return (ResultCode)result.Value;
|
return (ResultCode)result.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ using LibHac.Fs.Fsa;
|
||||||
using LibHac.Loader;
|
using LibHac.Loader;
|
||||||
using LibHac.Ns;
|
using LibHac.Ns;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.Loaders.Executables;
|
using Ryujinx.HLE.Loaders.Executables;
|
||||||
|
@ -102,7 +103,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize GPU.
|
// Initialize GPU.
|
||||||
Graphics.Gpu.GraphicsConfig.TitleId = programId.ToString("X16");
|
TitleIDs.CurrentApplication = programId.ToString("X16");
|
||||||
device.Gpu.HostInitalized.Set();
|
device.Gpu.HostInitalized.Set();
|
||||||
|
|
||||||
if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible))
|
if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible))
|
||||||
|
|
|
@ -6,6 +6,7 @@ using LibHac.Ns;
|
||||||
using LibHac.Tools.Fs;
|
using LibHac.Tools.Fs;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.Loaders.Executables;
|
using Ryujinx.HLE.Loaders.Executables;
|
||||||
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
||||||
|
@ -204,7 +205,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicitly null TitleId to disable the shader cache.
|
// Explicitly null TitleId to disable the shader cache.
|
||||||
Graphics.Gpu.GraphicsConfig.TitleId = null;
|
TitleIDs.CurrentApplication = default;
|
||||||
_device.Gpu.HostInitalized.Set();
|
_device.Gpu.HostInitalized.Set();
|
||||||
|
|
||||||
ProcessResult processResult = ProcessLoaderHelper.LoadNsos(_device,
|
ProcessResult processResult = ProcessLoaderHelper.LoadNsos(_device,
|
||||||
|
|
|
@ -37,6 +37,8 @@ namespace Ryujinx.HLE
|
||||||
|
|
||||||
public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable;
|
public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable;
|
||||||
|
|
||||||
|
public DirtyHacks DirtyHacks { get; }
|
||||||
|
|
||||||
public Switch(HLEConfiguration configuration)
|
public Switch(HLEConfiguration configuration)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(configuration.GpuRenderer);
|
ArgumentNullException.ThrowIfNull(configuration.GpuRenderer);
|
||||||
|
@ -72,6 +74,7 @@ namespace Ryujinx.HLE
|
||||||
System.EnablePtc = Configuration.EnablePtc;
|
System.EnablePtc = Configuration.EnablePtc;
|
||||||
System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel;
|
System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel;
|
||||||
System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode;
|
System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode;
|
||||||
|
DirtyHacks = Configuration.Hacks;
|
||||||
UpdateVSyncInterval();
|
UpdateVSyncInterval();
|
||||||
#pragma warning restore IDE0055
|
#pragma warning restore IDE0055
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace Ryujinx.UI.Common.Configuration
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current version of the file format
|
/// The current version of the file format
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int CurrentVersion = 57;
|
public const int CurrentVersion = 58;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Version of the configuration file format
|
/// Version of the configuration file format
|
||||||
|
@ -430,6 +430,16 @@ namespace Ryujinx.UI.Common.Configuration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UseHypervisor { get; set; }
|
public bool UseHypervisor { get; set; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show toggles for dirty hacks in the UI.
|
||||||
|
*/
|
||||||
|
public bool ShowDirtyHacks { get; set; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The packed value of the enabled dirty hacks.
|
||||||
|
*/
|
||||||
|
public int EnabledDirtyHacks { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads a configuration file from disk
|
/// Loads a configuration file from disk
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -736,6 +736,9 @@ namespace Ryujinx.UI.Common.Configuration
|
||||||
Multiplayer.LdnPassphrase.Value = configurationFileFormat.MultiplayerLdnPassphrase;
|
Multiplayer.LdnPassphrase.Value = configurationFileFormat.MultiplayerLdnPassphrase;
|
||||||
Multiplayer.LdnServer.Value = configurationFileFormat.LdnServer;
|
Multiplayer.LdnServer.Value = configurationFileFormat.LdnServer;
|
||||||
|
|
||||||
|
Hacks.ShowDirtyHacks.Value = configurationFileFormat.ShowDirtyHacks;
|
||||||
|
Hacks.Xc2MenuSoftlockFix.Value = ((DirtyHacks)configurationFileFormat.EnabledDirtyHacks).HasFlag(DirtyHacks.Xc2MenuSoftlockFix);
|
||||||
|
|
||||||
if (configurationFileUpdated)
|
if (configurationFileUpdated)
|
||||||
{
|
{
|
||||||
ToFileFormat().SaveConfig(configurationFilePath);
|
ToFileFormat().SaveConfig(configurationFilePath);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using ARMeilleure;
|
using ARMeilleure;
|
||||||
|
using Gommon;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Configuration.Hid;
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
|
@ -617,6 +618,49 @@ namespace Ryujinx.UI.Common.Configuration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class HacksSection
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Show toggles for dirty hacks in the UI.
|
||||||
|
/// </summary>
|
||||||
|
public ReactiveObject<bool> ShowDirtyHacks { get; private set; }
|
||||||
|
|
||||||
|
public ReactiveObject<bool> Xc2MenuSoftlockFix { get; private set; }
|
||||||
|
|
||||||
|
public HacksSection()
|
||||||
|
{
|
||||||
|
ShowDirtyHacks = new ReactiveObject<bool>();
|
||||||
|
Xc2MenuSoftlockFix = new ReactiveObject<bool>();
|
||||||
|
Xc2MenuSoftlockFix.Event += HackChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HackChanged(object sender, ReactiveEventArgs<bool> rxe)
|
||||||
|
{
|
||||||
|
Ryujinx.Common.Logging.Logger.Info?.Print(LogClass.Configuration, $"EnabledDirtyHacks set to: {EnabledHacks}", "LogValueChange");
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirtyHacks EnabledHacks
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
DirtyHacks dirtyHacks = DirtyHacks.None;
|
||||||
|
|
||||||
|
if (Xc2MenuSoftlockFix)
|
||||||
|
Apply(DirtyHacks.Xc2MenuSoftlockFix);
|
||||||
|
|
||||||
|
return dirtyHacks;
|
||||||
|
|
||||||
|
void Apply(DirtyHacks hack)
|
||||||
|
{
|
||||||
|
if (dirtyHacks is not DirtyHacks.None)
|
||||||
|
dirtyHacks |= hack;
|
||||||
|
else
|
||||||
|
dirtyHacks = hack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The default configuration instance
|
/// The default configuration instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -652,6 +696,11 @@ namespace Ryujinx.UI.Common.Configuration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MultiplayerSection Multiplayer { get; private set; }
|
public MultiplayerSection Multiplayer { get; private set; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Dirty Hacks section
|
||||||
|
*/
|
||||||
|
public HacksSection Hacks { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enables or disables Discord Rich Presence
|
/// Enables or disables Discord Rich Presence
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -700,6 +749,7 @@ namespace Ryujinx.UI.Common.Configuration
|
||||||
Graphics = new GraphicsSection();
|
Graphics = new GraphicsSection();
|
||||||
Hid = new HidSection();
|
Hid = new HidSection();
|
||||||
Multiplayer = new MultiplayerSection();
|
Multiplayer = new MultiplayerSection();
|
||||||
|
Hacks = new HacksSection();
|
||||||
EnableDiscordIntegration = new ReactiveObject<bool>();
|
EnableDiscordIntegration = new ReactiveObject<bool>();
|
||||||
CheckUpdatesOnStart = new ReactiveObject<bool>();
|
CheckUpdatesOnStart = new ReactiveObject<bool>();
|
||||||
ShowConfirmExit = new ReactiveObject<bool>();
|
ShowConfirmExit = new ReactiveObject<bool>();
|
||||||
|
|
|
@ -138,6 +138,8 @@ namespace Ryujinx.UI.Common.Configuration
|
||||||
MultiplayerDisableP2p = Multiplayer.DisableP2p,
|
MultiplayerDisableP2p = Multiplayer.DisableP2p,
|
||||||
MultiplayerLdnPassphrase = Multiplayer.LdnPassphrase,
|
MultiplayerLdnPassphrase = Multiplayer.LdnPassphrase,
|
||||||
LdnServer = Multiplayer.LdnServer,
|
LdnServer = Multiplayer.LdnServer,
|
||||||
|
ShowDirtyHacks = Hacks.ShowDirtyHacks,
|
||||||
|
EnabledDirtyHacks = (int)Hacks.EnabledHacks,
|
||||||
};
|
};
|
||||||
|
|
||||||
return configurationFile;
|
return configurationFile;
|
||||||
|
|
|
@ -311,6 +311,7 @@ namespace Ryujinx.Ava
|
||||||
Device.VSyncMode = e.NewValue;
|
Device.VSyncMode = e.NewValue;
|
||||||
Device.UpdateVSyncInterval();
|
Device.UpdateVSyncInterval();
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderer.Window?.ChangeVSyncMode(e.NewValue);
|
_renderer.Window?.ChangeVSyncMode(e.NewValue);
|
||||||
|
|
||||||
_viewModel.ShowCustomVSyncIntervalPicker = (e.NewValue == VSyncMode.Custom);
|
_viewModel.ShowCustomVSyncIntervalPicker = (e.NewValue == VSyncMode.Custom);
|
||||||
|
@ -923,7 +924,7 @@ namespace Ryujinx.Ava
|
||||||
// Initialize Configuration.
|
// Initialize Configuration.
|
||||||
var memoryConfiguration = ConfigurationState.Instance.System.DramSize.Value;
|
var memoryConfiguration = ConfigurationState.Instance.System.DramSize.Value;
|
||||||
|
|
||||||
Device = new HLE.Switch(new HLEConfiguration(
|
Device = new Switch(new HLEConfiguration(
|
||||||
VirtualFileSystem,
|
VirtualFileSystem,
|
||||||
_viewModel.LibHacHorizonManager,
|
_viewModel.LibHacHorizonManager,
|
||||||
ContentManager,
|
ContentManager,
|
||||||
|
@ -953,7 +954,8 @@ namespace Ryujinx.Ava
|
||||||
ConfigurationState.Instance.Multiplayer.DisableP2p,
|
ConfigurationState.Instance.Multiplayer.DisableP2p,
|
||||||
ConfigurationState.Instance.Multiplayer.LdnPassphrase,
|
ConfigurationState.Instance.Multiplayer.LdnPassphrase,
|
||||||
ConfigurationState.Instance.Multiplayer.LdnServer,
|
ConfigurationState.Instance.Multiplayer.LdnServer,
|
||||||
ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value));
|
ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value,
|
||||||
|
ConfigurationState.Instance.Hacks.ShowDirtyHacks ? ConfigurationState.Instance.Hacks.EnabledHacks : DirtyHacks.None));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IHardwareDeviceDriver InitializeAudio()
|
private static IHardwareDeviceDriver InitializeAudio()
|
||||||
|
|
|
@ -139,4 +139,10 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AdditionalFiles Include="Assets\locales.json" />
|
<AdditionalFiles Include="Assets\locales.json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Update="UI\Views\Settings\SettingsHacksView.axaml.cs">
|
||||||
|
<DependentUpon>SettingsHacksView.axaml</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -13,8 +13,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void OnPropertiesChanged(params ReadOnlySpan<string> propertyNames)
|
protected void OnPropertiesChanged(string firstPropertyName, params ReadOnlySpan<string> propertyNames)
|
||||||
{
|
{
|
||||||
|
OnPropertyChanged(firstPropertyName);
|
||||||
foreach (var propertyName in propertyNames)
|
foreach (var propertyName in propertyNames)
|
||||||
{
|
{
|
||||||
OnPropertyChanged(propertyName);
|
OnPropertyChanged(propertyName);
|
||||||
|
|
|
@ -62,7 +62,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
private int _networkInterfaceIndex;
|
private int _networkInterfaceIndex;
|
||||||
private int _multiplayerModeIndex;
|
private int _multiplayerModeIndex;
|
||||||
private string _ldnPassphrase;
|
private string _ldnPassphrase;
|
||||||
private string _LdnServer;
|
private string _ldnServer;
|
||||||
|
|
||||||
|
private bool _xc2MenuSoftlockFix = ConfigurationState.Instance.Hacks.Xc2MenuSoftlockFix;
|
||||||
|
|
||||||
public int ResolutionScale
|
public int ResolutionScale
|
||||||
{
|
{
|
||||||
|
@ -162,9 +164,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
get => _vSyncMode;
|
get => _vSyncMode;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value == VSyncMode.Custom ||
|
if (value is VSyncMode.Custom or VSyncMode.Switch or VSyncMode.Unbounded)
|
||||||
value == VSyncMode.Switch ||
|
|
||||||
value == VSyncMode.Unbounded)
|
|
||||||
{
|
{
|
||||||
_vSyncMode = value;
|
_vSyncMode = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
|
@ -258,6 +258,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
public bool UseHypervisor { get; set; }
|
public bool UseHypervisor { get; set; }
|
||||||
public bool DisableP2P { get; set; }
|
public bool DisableP2P { get; set; }
|
||||||
|
|
||||||
|
public bool ShowDirtyHacks => ConfigurationState.Instance.Hacks.ShowDirtyHacks;
|
||||||
|
|
||||||
public string TimeZone { get; set; }
|
public string TimeZone { get; set; }
|
||||||
public string ShaderDumpPath { get; set; }
|
public string ShaderDumpPath { get; set; }
|
||||||
|
|
||||||
|
@ -274,6 +276,17 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Xc2MenuSoftlockFixEnabled
|
||||||
|
{
|
||||||
|
get => _xc2MenuSoftlockFix;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_xc2MenuSoftlockFix = value;
|
||||||
|
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int Language { get; set; }
|
public int Language { get; set; }
|
||||||
public int Region { get; set; }
|
public int Region { get; set; }
|
||||||
public int FsGlobalAccessLogMode { get; set; }
|
public int FsGlobalAccessLogMode { get; set; }
|
||||||
|
@ -374,10 +387,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
public string LdnServer
|
public string LdnServer
|
||||||
{
|
{
|
||||||
get => _LdnServer;
|
get => _ldnServer;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_LdnServer = value;
|
_ldnServer = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -747,6 +760,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
config.Multiplayer.LdnPassphrase.Value = LdnPassphrase;
|
config.Multiplayer.LdnPassphrase.Value = LdnPassphrase;
|
||||||
config.Multiplayer.LdnServer.Value = LdnServer;
|
config.Multiplayer.LdnServer.Value = LdnServer;
|
||||||
|
|
||||||
|
// Dirty Hacks
|
||||||
|
config.Hacks.Xc2MenuSoftlockFix.Value = Xc2MenuSoftlockFixEnabled;
|
||||||
|
|
||||||
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||||
|
|
||||||
MainWindow.UpdateGraphicsConfig();
|
MainWindow.UpdateGraphicsConfig();
|
||||||
|
@ -779,5 +795,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
RevertIfNotSaved();
|
RevertIfNotSaved();
|
||||||
CloseWindow?.Invoke();
|
CloseWindow?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string Xc2MenuFixTooltip =>
|
||||||
|
"From the issue on GitHub:\n\nWhen clicking very fast from game main menu to 2nd submenu, there is a low chance that the game will softlock, the submenu won't show up, while background music is still there.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
48
src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml
Normal file
48
src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<UserControl
|
||||||
|
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsHacksView"
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
x:DataType="viewModels:SettingsViewModel">
|
||||||
|
<Design.DataContext>
|
||||||
|
<viewModels:SettingsViewModel />
|
||||||
|
</Design.DataContext>
|
||||||
|
<ScrollViewer
|
||||||
|
Name="HacksPage"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
HorizontalScrollBarVisibility="Disabled"
|
||||||
|
VerticalScrollBarVisibility="Auto">
|
||||||
|
<Border Classes="settings">
|
||||||
|
<StackPanel
|
||||||
|
Margin="10"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Orientation="Vertical"
|
||||||
|
Spacing="5">
|
||||||
|
<TextBlock
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Classes="h1"
|
||||||
|
Text="Dirty Hacks" />
|
||||||
|
<TextBlock
|
||||||
|
Foreground="{DynamicResource SecondaryTextColor}"
|
||||||
|
TextDecorations="Underline"
|
||||||
|
Text="Game-specific hacks & tricks to alleviate performance issues or crashing. May cause issues." />
|
||||||
|
<StackPanel
|
||||||
|
Margin="0,10,0,0"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
ToolTip.Tip="{Binding Xc2MenuFixTooltip}">
|
||||||
|
<CheckBox
|
||||||
|
Margin="0"
|
||||||
|
IsChecked="{Binding Xc2MenuSoftlockFixEnabled}"/>
|
||||||
|
<TextBlock
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Xenoblade Chronicles 2 Menu Softlock Fix" />
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</ScrollViewer>
|
||||||
|
</UserControl>
|
17
src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml.cs
Normal file
17
src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
|
using Ryujinx.UI.Common.Configuration;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.UI.Views.Settings
|
||||||
|
{
|
||||||
|
public partial class SettingsHacksView : UserControl
|
||||||
|
{
|
||||||
|
public SettingsViewModel ViewModel;
|
||||||
|
|
||||||
|
public SettingsHacksView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,6 +37,7 @@
|
||||||
<settings:SettingsAudioView Name="AudioPage" />
|
<settings:SettingsAudioView Name="AudioPage" />
|
||||||
<settings:SettingsNetworkView Name="NetworkPage" />
|
<settings:SettingsNetworkView Name="NetworkPage" />
|
||||||
<settings:SettingsLoggingView Name="LoggingPage" />
|
<settings:SettingsLoggingView Name="LoggingPage" />
|
||||||
|
<settings:SettingsHacksView Name="HacksPage" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<ui:NavigationView
|
<ui:NavigationView
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
|
@ -91,6 +92,11 @@
|
||||||
Content="{ext:Locale SettingsTabLogging}"
|
Content="{ext:Locale SettingsTabLogging}"
|
||||||
Tag="LoggingPage"
|
Tag="LoggingPage"
|
||||||
IconSource="Document" />
|
IconSource="Document" />
|
||||||
|
<ui:NavigationViewItem
|
||||||
|
IsVisible="{Binding ShowDirtyHacks}"
|
||||||
|
Content="Dirty Hacks"
|
||||||
|
Tag="HacksPage"
|
||||||
|
IconSource="Code" />
|
||||||
</ui:NavigationView.MenuItems>
|
</ui:NavigationView.MenuItems>
|
||||||
<ui:NavigationView.Styles>
|
<ui:NavigationView.Styles>
|
||||||
<Style Selector="Grid#PlaceholderGrid">
|
<Style Selector="Grid#PlaceholderGrid">
|
||||||
|
|
|
@ -86,6 +86,10 @@ namespace Ryujinx.Ava.UI.Windows
|
||||||
case nameof(LoggingPage):
|
case nameof(LoggingPage):
|
||||||
NavPanel.Content = LoggingPage;
|
NavPanel.Content = LoggingPage;
|
||||||
break;
|
break;
|
||||||
|
case nameof(HacksPage):
|
||||||
|
HacksPage.ViewModel = ViewModel;
|
||||||
|
NavPanel.Content = HacksPage;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue