Skip to content

Commit

Permalink
misc: Dirty Hacks
Browse files Browse the repository at this point in the history
Enable this settings screen via a boolean in Config.json
First one is the xb2 menu softlock fix
  • Loading branch information
GreemDev committed Dec 29, 2024
1 parent 09107b6 commit 8b3a945
Show file tree
Hide file tree
Showing 21 changed files with 222 additions and 22 deletions.
11 changes: 11 additions & 0 deletions src/Ryujinx.Common/Configuration/DirtyHacks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace Ryujinx.Common.Configuration
{
[Flags]
public enum DirtyHacks
{
None = 0,
Xc2MenuSoftlockFix = 1 << 10
}
}
2 changes: 2 additions & 0 deletions src/Ryujinx.Common/TitleIDs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ namespace Ryujinx.Common
{
public static class TitleIDs
{
public static Optional<string> CurrentApplication;

public static GraphicsBackend SelectGraphicsBackend(string titleId, GraphicsBackend currentBackend)
{
switch (currentBackend)
Expand Down
6 changes: 0 additions & 6 deletions src/Ryujinx.Graphics.Gpu/GraphicsConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,6 @@ public static class GraphicsConfig
/// </summary>
public static bool EnableMacroHLE = true;

/// <summary>
/// Title id of the current running game.
/// Used by the shader cache.
/// </summary>
public static string TitleId;

/// <summary>
/// Enables or disables the shader cache.
/// </summary>
Expand Down
5 changes: 3 additions & 2 deletions src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
Expand Down Expand Up @@ -116,8 +117,8 @@ public ShaderCache(GpuContext context)
/// </summary>
private static string GetDiskCachePath()
{
return GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null
? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId, "cache", "shader")
return GraphicsConfig.EnableShaderCache && TitleIDs.CurrentApplication.HasValue
? Path.Combine(AppDataManager.GamesDirPath, TitleIDs.CurrentApplication, "cache", "shader")
: null;
}

Expand Down
9 changes: 8 additions & 1 deletion src/Ryujinx.HLE/HLEConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,11 @@ public class HLEConfiguration
/// An action called when HLE force a refresh of output after docked mode changed.
/// </summary>
public Action RefreshInputConfig { internal get; set; }

/**
* The desired hacky workarounds.
*/
public DirtyHacks Hacks { internal get; set; }

public HLEConfiguration(VirtualFileSystem virtualFileSystem,
LibHacHorizonManager libHacHorizonManager,
Expand Down Expand Up @@ -218,7 +223,8 @@ public HLEConfiguration(VirtualFileSystem virtualFileSystem,
bool multiplayerDisableP2p,
string multiplayerLdnPassphrase,
string multiplayerLdnServer,
int customVSyncInterval)
int customVSyncInterval,
DirtyHacks dirtyHacks = DirtyHacks.None)
{
VirtualFileSystem = virtualFileSystem;
LibHacHorizonManager = libHacHorizonManager;
Expand Down Expand Up @@ -250,6 +256,7 @@ public HLEConfiguration(VirtualFileSystem virtualFileSystem,
MultiplayerDisableP2p = multiplayerDisableP2p;
MultiplayerLdnPassphrase = multiplayerLdnPassphrase;
MultiplayerLdnServer = multiplayerLdnServer;
Hacks = dirtyHacks;
}
}
}
12 changes: 12 additions & 0 deletions src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using LibHac;
using LibHac.Common;
using LibHac.Sf;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using System.Threading;

namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
{
Expand All @@ -13,6 +16,8 @@ public IStorage(ref SharedRef<LibHac.FsSrv.Sf.IStorage> baseStorage)
_baseStorage = SharedRef<LibHac.FsSrv.Sf.IStorage>.CreateMove(ref baseStorage);
}

private const string Xc2TitleId = "0100e95004038000";

[CommandCmif(0)]
// Read(u64 offset, u64 length) -> buffer<u8, 0x46, 0> buffer
public ResultCode Read(ServiceCtx context)
Expand All @@ -33,6 +38,13 @@ public ResultCode Read(ServiceCtx context)

using var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true);
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using LibHac.Loader;
using LibHac.Ns;
using LibHac.Tools.FsSystem;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Loaders.Executables;
Expand Down Expand Up @@ -102,7 +103,7 @@ public static ProcessResult Load(this IFileSystem exeFs, Switch device, BlitStru
}

// Initialize GPU.
Graphics.Gpu.GraphicsConfig.TitleId = programId.ToString("X16");
TitleIDs.CurrentApplication = programId.ToString("X16");
device.Gpu.HostInitalized.Set();

if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible))
Expand Down
3 changes: 2 additions & 1 deletion src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.HLE.Loaders.Processes.Extensions;
Expand Down Expand Up @@ -204,7 +205,7 @@ public bool LoadNxo(string path)
}

// Explicitly null TitleId to disable the shader cache.
Graphics.Gpu.GraphicsConfig.TitleId = null;
TitleIDs.CurrentApplication = default;
_device.Gpu.HostInitalized.Set();

ProcessResult processResult = ProcessLoaderHelper.LoadNsos(_device,
Expand Down
3 changes: 3 additions & 0 deletions src/Ryujinx.HLE/Switch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public class Switch : IDisposable

public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable;

public DirtyHacks DirtyHacks { get; }

public Switch(HLEConfiguration configuration)
{
ArgumentNullException.ThrowIfNull(configuration.GpuRenderer);
Expand Down Expand Up @@ -72,6 +74,7 @@ public Switch(HLEConfiguration configuration)
System.EnablePtc = Configuration.EnablePtc;
System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel;
System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode;
DirtyHacks = Configuration.Hacks;
UpdateVSyncInterval();
#pragma warning restore IDE0055
}
Expand Down
14 changes: 12 additions & 2 deletions src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class ConfigurationFileFormat
/// <summary>
/// The current version of the file format
/// </summary>
public const int CurrentVersion = 57;
public const int CurrentVersion = 58;

/// <summary>
/// Version of the configuration file format
Expand Down Expand Up @@ -429,7 +429,17 @@ public class ConfigurationFileFormat
/// Uses Hypervisor over JIT if available
/// </summary>
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>
/// Loads a configuration file from disk
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,9 @@ public void Load(ConfigurationFileFormat configurationFileFormat, string configu
Multiplayer.DisableP2p.Value = configurationFileFormat.MultiplayerDisableP2p;
Multiplayer.LdnPassphrase.Value = configurationFileFormat.MultiplayerLdnPassphrase;
Multiplayer.LdnServer.Value = configurationFileFormat.LdnServer;

Hacks.ShowDirtyHacks.Value = configurationFileFormat.ShowDirtyHacks;
Hacks.Xc2MenuSoftlockFix.Value = ((DirtyHacks)configurationFileFormat.EnabledDirtyHacks).HasFlag(DirtyHacks.Xc2MenuSoftlockFix);

if (configurationFileUpdated)
{
Expand Down
50 changes: 50 additions & 0 deletions src/Ryujinx.UI.Common/Configuration/ConfigurationState.Model.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using ARMeilleure;
using Gommon;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
Expand Down Expand Up @@ -617,6 +618,49 @@ public MultiplayerSection()
}
}

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>
/// The default configuration instance
/// </summary>
Expand Down Expand Up @@ -651,6 +695,11 @@ public MultiplayerSection()
/// The Multiplayer section
/// </summary>
public MultiplayerSection Multiplayer { get; private set; }

/**
* The Dirty Hacks section
*/
public HacksSection Hacks { get; private set; }

/// <summary>
/// Enables or disables Discord Rich Presence
Expand Down Expand Up @@ -700,6 +749,7 @@ private ConfigurationState()
Graphics = new GraphicsSection();
Hid = new HidSection();
Multiplayer = new MultiplayerSection();
Hacks = new HacksSection();
EnableDiscordIntegration = new ReactiveObject<bool>();
CheckUpdatesOnStart = new ReactiveObject<bool>();
ShowConfirmExit = new ReactiveObject<bool>();
Expand Down
2 changes: 2 additions & 0 deletions src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ public ConfigurationFileFormat ToFileFormat()
MultiplayerDisableP2p = Multiplayer.DisableP2p,
MultiplayerLdnPassphrase = Multiplayer.LdnPassphrase,
LdnServer = Multiplayer.LdnServer,
ShowDirtyHacks = Hacks.ShowDirtyHacks,
EnabledDirtyHacks = (int)Hacks.EnabledHacks,
};

return configurationFile;
Expand Down
6 changes: 4 additions & 2 deletions src/Ryujinx/AppHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ public void UpdateVSyncMode(object sender, ReactiveEventArgs<VSyncMode> e)
Device.VSyncMode = e.NewValue;
Device.UpdateVSyncInterval();
}

_renderer.Window?.ChangeVSyncMode(e.NewValue);

_viewModel.ShowCustomVSyncIntervalPicker = (e.NewValue == VSyncMode.Custom);
Expand Down Expand Up @@ -923,7 +924,7 @@ private void InitializeSwitchInstance()
// Initialize Configuration.
var memoryConfiguration = ConfigurationState.Instance.System.DramSize.Value;

Device = new HLE.Switch(new HLEConfiguration(
Device = new Switch(new HLEConfiguration(
VirtualFileSystem,
_viewModel.LibHacHorizonManager,
ContentManager,
Expand Down Expand Up @@ -953,7 +954,8 @@ private void InitializeSwitchInstance()
ConfigurationState.Instance.Multiplayer.DisableP2p,
ConfigurationState.Instance.Multiplayer.LdnPassphrase,
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()
Expand Down
6 changes: 6 additions & 0 deletions src/Ryujinx/Ryujinx.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,10 @@
<ItemGroup>
<AdditionalFiles Include="Assets\locales.json" />
</ItemGroup>
<ItemGroup>
<Compile Update="UI\Views\Settings\SettingsHacksView.axaml.cs">
<DependentUpon>SettingsHacksView.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
</Project>
3 changes: 2 additions & 1 deletion src/Ryujinx/UI/ViewModels/BaseModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
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)
{
OnPropertyChanged(propertyName);
Expand Down
Loading

0 comments on commit 8b3a945

Please sign in to comment.