diff --git a/Ryujinx.sln b/Ryujinx.sln index 87c1021c1..373572178 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -61,8 +61,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.FFmp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "src\Ryujinx\Ryujinx.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.UI.Common", "src\Ryujinx.UI.Common\Ryujinx.UI.Common.csproj", "{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Generators", "src\Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj", "{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vulkan", "src\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj", "{D4D09B08-D580-4D69-B886-C35D2853F6C8}" @@ -219,10 +217,6 @@ Global {7C1B2721-13DA-4B62-B046-C626605ECCE6}.Debug|Any CPU.Build.0 = Debug|Any CPU {7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|Any CPU.ActiveCfg = Release|Any CPU {7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|Any CPU.Build.0 = Release|Any CPU - {BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Release|Any CPU.Build.0 = Release|Any CPU {6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|Any CPU.Build.0 = Debug|Any CPU {6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/distribution/linux/appimage/build-appimage.sh b/distribution/linux/appimage/build-appimage.sh index 5c32d78a8..9b52928f8 100755 --- a/distribution/linux/appimage/build-appimage.sh +++ b/distribution/linux/appimage/build-appimage.sh @@ -13,7 +13,7 @@ mkdir -p AppDir/usr/bin cp distribution/linux/Ryujinx.desktop AppDir/Ryujinx.desktop cp distribution/linux/appimage/AppRun AppDir/AppRun -cp src/Ryujinx.UI.Common/Resources/Logo_Ryujinx.png AppDir/Ryujinx.svg +cp distribution/misc/Logo.svg AppDir/Ryujinx.svg cp -r "$BUILDDIR"/* AppDir/usr/bin/ diff --git a/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs b/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs index 3a3c1d1b1..5991b816f 100644 --- a/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs @@ -9,20 +9,12 @@ namespace Ryujinx.Audio.Backends.Dummy { public class DummyHardwareDeviceDriver : IHardwareDeviceDriver { - private readonly ManualResetEvent _updateRequiredEvent; - private readonly ManualResetEvent _pauseEvent; + private readonly ManualResetEvent _updateRequiredEvent = new(false); + private readonly ManualResetEvent _pauseEvent = new(true); public static bool IsSupported => true; - public float Volume { get; set; } - - public DummyHardwareDeviceDriver() - { - _updateRequiredEvent = new ManualResetEvent(false); - _pauseEvent = new ManualResetEvent(true); - - Volume = 1f; - } + public float Volume { get; set; } = 1f; public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount) { @@ -60,7 +52,7 @@ namespace Ryujinx.Audio.Backends.Dummy Dispose(true); } - protected virtual void Dispose(bool disposing) + private void Dispose(bool disposing) { if (disposing) { diff --git a/src/Ryujinx.Common/BitTricks.cs b/src/Ryujinx.Common/BitTricks.cs new file mode 100644 index 000000000..d0c689291 --- /dev/null +++ b/src/Ryujinx.Common/BitTricks.cs @@ -0,0 +1,35 @@ +namespace Ryujinx.Common +{ + public class BitTricks + { + // Never actually written bit packing logic before, so I looked it up. + // This code is from https://gist.github.com/Alan-FGR/04938e93e2bffdf5802ceb218a37c195 + + public static ulong PackBitFields(uint[] values, byte[] bitFields) + { + ulong retVal = values[0]; //we set the first value right away + for (int f = 1; f < values.Length; f++) + { + retVal <<= bitFields[f]; // we shift the previous value + retVal += values[f];// and add our current value + } + return retVal; + } + + public static uint[] UnpackBitFields(ulong packed, byte[] bitFields) + { + int fields = bitFields.Length - 1; // number of fields to unpack + uint[] retArr = new uint[fields + 1]; // init return array + int curPos = 0; // current field bit position (start) + int lastEnd; // position where last field ended + for (int f = fields; f >= 0; f--) // loop from last + { + lastEnd = curPos; // we store where the last value ended + curPos += bitFields[f]; // we get where the current value starts + int leftShift = 64 - curPos; // we figure how much left shift we gotta apply for the other numbers to overflow into oblivion + retArr[f] = (uint)((packed << leftShift) >> leftShift + lastEnd); // we do magic + } + return retArr; + } + } +} diff --git a/src/Ryujinx.Common/Configuration/DirtyHacks.cs b/src/Ryujinx.Common/Configuration/DirtyHacks.cs index 6a6d4949c..1015e95d1 100644 --- a/src/Ryujinx.Common/Configuration/DirtyHacks.cs +++ b/src/Ryujinx.Common/Configuration/DirtyHacks.cs @@ -1,11 +1,61 @@ using System; +using System.Collections.Generic; +using System.Linq; namespace Ryujinx.Common.Configuration { [Flags] - public enum DirtyHacks + public enum DirtyHacks : byte { - None = 0, - Xc2MenuSoftlockFix = 1 << 10 + Xc2MenuSoftlockFix = 1, + ShaderCompilationThreadSleep = 2 + } + + public record EnabledDirtyHack(DirtyHacks Hack, int Value) + { + public static readonly byte[] PackedFormat = [8, 32]; + + public ulong Pack() => BitTricks.PackBitFields([(uint)Hack, (uint)Value], PackedFormat); + + public static EnabledDirtyHack Unpack(ulong packedHack) + { + var unpackedFields = BitTricks.UnpackBitFields(packedHack, PackedFormat); + if (unpackedFields is not [var hack, var value]) + throw new ArgumentException(nameof(packedHack)); + + return new EnabledDirtyHack((DirtyHacks)hack, (int)value); + } + } + + public class DirtyHackCollection : Dictionary + { + public DirtyHackCollection(EnabledDirtyHack[] hacks) + { + foreach ((DirtyHacks dirtyHacks, int value) in hacks) + { + Add(dirtyHacks, value); + } + } + + public DirtyHackCollection(ulong[] packedHacks) + { + foreach ((DirtyHacks dirtyHacks, int value) in packedHacks.Select(EnabledDirtyHack.Unpack)) + { + Add(dirtyHacks, value); + } + } + + public ulong[] PackEntries() => + this + .Select(it => + BitTricks.PackBitFields([(uint)it.Key, (uint)it.Value], EnabledDirtyHack.PackedFormat)) + .ToArray(); + + public static implicit operator DirtyHackCollection(EnabledDirtyHack[] hacks) => new(hacks); + public static implicit operator DirtyHackCollection(ulong[] packedHacks) => new(packedHacks); + + public new int this[DirtyHacks hack] => TryGetValue(hack, out var value) ? value : -1; + + public bool IsEnabled(DirtyHacks hack) => ContainsKey(hack); } } diff --git a/src/Ryujinx.UI.Common/Helper/ConsoleHelper.cs b/src/Ryujinx.Common/Helpers/ConsoleHelper.cs similarity index 98% rename from src/Ryujinx.UI.Common/Helper/ConsoleHelper.cs rename to src/Ryujinx.Common/Helpers/ConsoleHelper.cs index 99b209c6e..105c9881e 100644 --- a/src/Ryujinx.UI.Common/Helper/ConsoleHelper.cs +++ b/src/Ryujinx.Common/Helpers/ConsoleHelper.cs @@ -3,7 +3,7 @@ using System; using System.Runtime.InteropServices; using System.Runtime.Versioning; -namespace Ryujinx.UI.Common.Helper +namespace Ryujinx.Common.Helper { public static partial class ConsoleHelper { diff --git a/src/Ryujinx.UI.Common/Helper/FileAssociationHelper.cs b/src/Ryujinx.Common/Helpers/FileAssociationHelper.cs similarity index 99% rename from src/Ryujinx.UI.Common/Helper/FileAssociationHelper.cs rename to src/Ryujinx.Common/Helpers/FileAssociationHelper.cs index b1463989d..476aee228 100644 --- a/src/Ryujinx.UI.Common/Helper/FileAssociationHelper.cs +++ b/src/Ryujinx.Common/Helpers/FileAssociationHelper.cs @@ -8,7 +8,7 @@ using System.Linq; using System.Runtime.InteropServices; using System.Runtime.Versioning; -namespace Ryujinx.UI.Common.Helper +namespace Ryujinx.Common.Helper { public static partial class FileAssociationHelper { diff --git a/src/Ryujinx.UI.Common/Helper/LinuxHelper.cs b/src/Ryujinx.Common/Helpers/LinuxHelper.cs similarity index 98% rename from src/Ryujinx.UI.Common/Helper/LinuxHelper.cs rename to src/Ryujinx.Common/Helpers/LinuxHelper.cs index b57793791..2adfd20f8 100644 --- a/src/Ryujinx.UI.Common/Helper/LinuxHelper.cs +++ b/src/Ryujinx.Common/Helpers/LinuxHelper.cs @@ -3,7 +3,7 @@ using System.Diagnostics; using System.IO; using System.Runtime.Versioning; -namespace Ryujinx.UI.Common.Helper +namespace Ryujinx.Common.Helper { [SupportedOSPlatform("linux")] public static class LinuxHelper diff --git a/src/Ryujinx.UI.Common/Helper/ObjectiveC.cs b/src/Ryujinx.Common/Helpers/ObjectiveC.cs similarity index 99% rename from src/Ryujinx.UI.Common/Helper/ObjectiveC.cs rename to src/Ryujinx.Common/Helpers/ObjectiveC.cs index f8f972098..d8e02f54d 100644 --- a/src/Ryujinx.UI.Common/Helper/ObjectiveC.cs +++ b/src/Ryujinx.Common/Helpers/ObjectiveC.cs @@ -2,7 +2,7 @@ using System; using System.Runtime.InteropServices; using System.Runtime.Versioning; -namespace Ryujinx.UI.Common.Helper +namespace Ryujinx.Common.Helper { [SupportedOSPlatform("macos")] public static partial class ObjectiveC diff --git a/src/Ryujinx.UI.Common/Helper/OpenHelper.cs b/src/Ryujinx.Common/Helpers/OpenHelper.cs similarity index 99% rename from src/Ryujinx.UI.Common/Helper/OpenHelper.cs rename to src/Ryujinx.Common/Helpers/OpenHelper.cs index bf398a355..6a54b69f3 100644 --- a/src/Ryujinx.UI.Common/Helper/OpenHelper.cs +++ b/src/Ryujinx.Common/Helpers/OpenHelper.cs @@ -5,7 +5,7 @@ using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; -namespace Ryujinx.UI.Common.Helper +namespace Ryujinx.Common.Helper { public static partial class OpenHelper { diff --git a/src/Ryujinx.UI.Common/UserError.cs b/src/Ryujinx.Common/UI/UserError.cs similarity index 96% rename from src/Ryujinx.UI.Common/UserError.cs rename to src/Ryujinx.Common/UI/UserError.cs index 706971efa..cdcc42eac 100644 --- a/src/Ryujinx.UI.Common/UserError.cs +++ b/src/Ryujinx.Common/UI/UserError.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.UI.Common +namespace Ryujinx.Common.UI { /// /// Represent a common error that could be reported to the user by the emulator. diff --git a/src/Ryujinx.Graphics.Gpu/GpuContext.cs b/src/Ryujinx.Graphics.Gpu/GpuContext.cs index fb529e914..84bc0b9a9 100644 --- a/src/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/src/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -1,4 +1,5 @@ using Ryujinx.Common; +using Ryujinx.Common.Configuration; using Ryujinx.Graphics.Device; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Engine.GPFifo; @@ -90,6 +91,9 @@ namespace Ryujinx.Graphics.Gpu /// Support buffer updater. /// internal SupportBufferUpdater SupportBufferUpdater { get; } + + internal DirtyHackCollection DirtyHacks { get; } + /// /// Host hardware capabilities. @@ -113,7 +117,7 @@ namespace Ryujinx.Graphics.Gpu /// Creates a new instance of the GPU emulation context. /// /// Host renderer - public GpuContext(IRenderer renderer) + public GpuContext(IRenderer renderer, DirtyHackCollection hackCollection) { Renderer = renderer; @@ -136,6 +140,8 @@ namespace Ryujinx.Graphics.Gpu SupportBufferUpdater = new SupportBufferUpdater(renderer); + DirtyHacks = hackCollection; + _firstTimestamp = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds); } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs index 74922d1e3..9aa96a76f 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; @@ -366,6 +367,9 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache { try { + if (_context.DirtyHacks.IsEnabled(DirtyHacks.ShaderCompilationThreadSleep)) + Thread.Sleep(_context.DirtyHacks[DirtyHacks.ShaderCompilationThreadSleep]); + AsyncProgramTranslation asyncTranslation = new(guestShaders, specState, programIndex, isCompute); _asyncTranslationQueue.Add(asyncTranslation, _cancellationToken); } diff --git a/src/Ryujinx.HLE/HLEConfiguration.cs b/src/Ryujinx.HLE/HLEConfiguration.cs index b44a09b22..8ac76508f 100644 --- a/src/Ryujinx.HLE/HLEConfiguration.cs +++ b/src/Ryujinx.HLE/HLEConfiguration.cs @@ -192,7 +192,7 @@ namespace Ryujinx.HLE /// /// The desired hacky workarounds. /// - public DirtyHacks Hacks { internal get; set; } + public EnabledDirtyHack[] Hacks { internal get; set; } public HLEConfiguration(VirtualFileSystem virtualFileSystem, LibHacHorizonManager libHacHorizonManager, @@ -224,7 +224,7 @@ namespace Ryujinx.HLE string multiplayerLdnPassphrase, string multiplayerLdnServer, int customVSyncInterval, - DirtyHacks dirtyHacks = DirtyHacks.None) + EnabledDirtyHack[] dirtyHacks = null) { VirtualFileSystem = virtualFileSystem; LibHacHorizonManager = libHacHorizonManager; @@ -256,7 +256,7 @@ namespace Ryujinx.HLE MultiplayerDisableP2p = multiplayerDisableP2p; MultiplayerLdnPassphrase = multiplayerLdnPassphrase; MultiplayerLdnServer = multiplayerLdnServer; - Hacks = dirtyHacks; + Hacks = dirtyHacks ?? []; } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs b/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs index 07ab8b386..ac5dc04e9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs @@ -39,7 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy 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.Value == Xc2TitleId) + if (context.Device.DirtyHacks.IsEnabled(DirtyHacks.Xc2MenuSoftlockFix) && TitleIDs.CurrentApplication.Value == Xc2TitleId) { // Add a load-bearing sleep to avoid XC2 softlock // https://web.archive.org/web/20240728045136/https://github.com/Ryujinx/Ryujinx/issues/2357 diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboBinReader.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboBinReader.cs index 03063cb53..dffcf9c3f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboBinReader.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboBinReader.cs @@ -114,10 +114,10 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption } } - string usedCharacterStr = BitConverter.ToString(usedCharacter).Replace("-", ""); - string variationStr = BitConverter.ToString(variation).Replace("-", ""); - string amiiboIDStr = BitConverter.ToString(amiiboID).Replace("-", ""); - string setIDStr = BitConverter.ToString(setID).Replace("-", ""); + string usedCharacterStr = Convert.ToHexString(usedCharacter); + string variationStr = Convert.ToHexString(variation); + string amiiboIDStr = Convert.ToHexString(amiiboID); + string setIDStr = Convert.ToHexString(setID); string head = usedCharacterStr + variationStr; string tail = amiiboIDStr + setIDStr + "02"; string finalID = head + tail; @@ -289,8 +289,8 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption private static void LogFinalData(byte[] titleId, byte[] appId, string head, string tail, string finalID, string nickName, DateTime initDateTime, DateTime writeDateTime, ushort settingsValue, ushort writeCounterValue, byte[] applicationAreas) { - Logger.Debug?.Print(LogClass.ServiceNfp, $"Title ID: 0x{BitConverter.ToString(titleId).Replace("-", "")}"); - Logger.Debug?.Print(LogClass.ServiceNfp, $"Application Program ID: 0x{BitConverter.ToString(appId).Replace("-", "")}"); + Logger.Debug?.Print(LogClass.ServiceNfp, $"Title ID: 0x{Convert.ToHexString(titleId)}"); + Logger.Debug?.Print(LogClass.ServiceNfp, $"Application Program ID: 0x{Convert.ToHexString(appId)}"); Logger.Debug?.Print(LogClass.ServiceNfp, $"Head: {head}"); Logger.Debug?.Print(LogClass.ServiceNfp, $"Tail: {tail}"); Logger.Debug?.Print(LogClass.ServiceNfp, $"Final ID: {finalID}"); diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs index c630c71c7..127e532e2 100644 --- a/src/Ryujinx.HLE/Switch.cs +++ b/src/Ryujinx.HLE/Switch.cs @@ -40,7 +40,7 @@ namespace Ryujinx.HLE public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable; - public DirtyHacks DirtyHacks { get; } + public DirtyHackCollection DirtyHacks { get; } public Switch(HLEConfiguration configuration) { @@ -57,9 +57,10 @@ namespace Ryujinx.HLE : MemoryAllocationFlags.Reserve | MemoryAllocationFlags.Mirrorable; #pragma warning disable IDE0055 // Disable formatting + DirtyHacks = new DirtyHackCollection(Configuration.Hacks); AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(Configuration.AudioDeviceDriver); Memory = new MemoryBlock(Configuration.MemoryConfiguration.ToDramSize(), memoryAllocationFlags); - Gpu = new GpuContext(Configuration.GpuRenderer); + Gpu = new GpuContext(Configuration.GpuRenderer, DirtyHacks); System = new HOS.Horizon(this); Statistics = new PerformanceStatistics(); Hid = new Hid(this, System.HidStorage); @@ -77,7 +78,7 @@ namespace Ryujinx.HLE System.EnablePtc = Configuration.EnablePtc; System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel; System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode; - DirtyHacks = Configuration.Hacks; + UpdateVSyncInterval(); #pragma warning restore IDE0055 diff --git a/src/Ryujinx.UI.Common/Configuration/FileTypes.cs b/src/Ryujinx.UI.Common/Configuration/FileTypes.cs deleted file mode 100644 index 1974207b6..000000000 --- a/src/Ryujinx.UI.Common/Configuration/FileTypes.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Ryujinx.UI.Common -{ - public enum FileTypes - { - NSP, - PFS0, - XCI, - NCA, - NRO, - NSO - } -} diff --git a/src/Ryujinx.UI.Common/Helper/AppletMetadata.cs b/src/Ryujinx.UI.Common/Helper/AppletMetadata.cs deleted file mode 100644 index 644b7fe74..000000000 --- a/src/Ryujinx.UI.Common/Helper/AppletMetadata.cs +++ /dev/null @@ -1,64 +0,0 @@ -using LibHac.Common; -using LibHac.Ncm; -using LibHac.Ns; -using LibHac.Tools.FsSystem.NcaUtils; -using Ryujinx.HLE; -using Ryujinx.HLE.FileSystem; -using Ryujinx.UI.App.Common; - -namespace Ryujinx.UI.Common.Helper -{ - public readonly struct AppletMetadata - { - private readonly ContentManager _contentManager; - - public string Name { get; } - public ulong ProgramId { get; } - - public string Version { get; } - - public AppletMetadata(ContentManager contentManager, string name, ulong programId, string version = "1.0.0") - : this(name, programId, version) - { - _contentManager = contentManager; - } - - public AppletMetadata(string name, ulong programId, string version = "1.0.0") - { - Name = name; - ProgramId = programId; - Version = version; - } - - public string GetContentPath(ContentManager contentManager) - => (contentManager ?? _contentManager) - .GetInstalledContentPath(ProgramId, StorageId.BuiltInSystem, NcaContentType.Program); - - public bool CanStart(ContentManager contentManager, out ApplicationData appData, out BlitStruct appControl) - { - contentManager ??= _contentManager; - if (contentManager == null) - { - appData = null; - appControl = new BlitStruct(0); - return false; - } - - appData = new() - { - Name = Name, - Id = ProgramId, - Path = GetContentPath(contentManager) - }; - - if (string.IsNullOrEmpty(appData.Path)) - { - appControl = new BlitStruct(0); - return false; - } - - appControl = StructHelpers.CreateCustomNacpData(Name, Version); - return true; - } - } -} diff --git a/src/Ryujinx.UI.Common/Resources/Controller_JoyConLeft.svg b/src/Ryujinx.UI.Common/Resources/Controller_JoyConLeft.svg deleted file mode 100644 index 03585e656..000000000 --- a/src/Ryujinx.UI.Common/Resources/Controller_JoyConLeft.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/Ryujinx.UI.Common/Resources/Controller_JoyConPair.svg b/src/Ryujinx.UI.Common/Resources/Controller_JoyConPair.svg deleted file mode 100644 index c073c9c0c..000000000 --- a/src/Ryujinx.UI.Common/Resources/Controller_JoyConPair.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/Ryujinx.UI.Common/Resources/Controller_JoyConRight.svg b/src/Ryujinx.UI.Common/Resources/Controller_JoyConRight.svg deleted file mode 100644 index f4f125148..000000000 --- a/src/Ryujinx.UI.Common/Resources/Controller_JoyConRight.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/Ryujinx.UI.Common/Resources/Controller_ProCon.svg b/src/Ryujinx.UI.Common/Resources/Controller_ProCon.svg deleted file mode 100644 index f5380f3ad..000000000 --- a/src/Ryujinx.UI.Common/Resources/Controller_ProCon.svg +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - A - - - - X - - - - Y - - - - B - - - - - - - - - - ZL - - - - - ZR - - - - R - - - - - L - - - diff --git a/src/Ryujinx.UI.Common/Ryujinx.UI.Common.csproj b/src/Ryujinx.UI.Common/Ryujinx.UI.Common.csproj index 1ee9a4aa0..01efe04ba 100644 --- a/src/Ryujinx.UI.Common/Ryujinx.UI.Common.csproj +++ b/src/Ryujinx.UI.Common/Ryujinx.UI.Common.csproj @@ -21,34 +21,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index 1f538868b..b90f8b801 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -20,11 +20,15 @@ using Ryujinx.Ava.UI.Models; 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.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Multiplayer; using Ryujinx.Common.Logging; using Ryujinx.Common.SystemInterop; +using Ryujinx.Common.UI; using Ryujinx.Common.Utilities; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL.Multithreading; @@ -39,10 +43,6 @@ using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.Input; using Ryujinx.Input.HLE; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Helper; using Silk.NET.Vulkan; using SkiaSharp; using SPB.Graphics.Vulkan; @@ -952,7 +952,7 @@ namespace Ryujinx.Ava ConfigurationState.Instance.Multiplayer.LdnPassphrase, ConfigurationState.Instance.Multiplayer.LdnServer, ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value, - ConfigurationState.Instance.Hacks.ShowDirtyHacks ? ConfigurationState.Instance.Hacks.EnabledHacks : DirtyHacks.None)); + ConfigurationState.Instance.Hacks.ShowDirtyHacks ? ConfigurationState.Instance.Hacks.EnabledHacks : null)); } private static IHardwareDeviceDriver InitializeAudio() diff --git a/src/Ryujinx.UI.Common/Resources/Icon_Blank.png b/src/Ryujinx/Assets/UIImages/Icon_Blank.png similarity index 100% rename from src/Ryujinx.UI.Common/Resources/Icon_Blank.png rename to src/Ryujinx/Assets/UIImages/Icon_Blank.png diff --git a/src/Ryujinx.UI.Common/Resources/Icon_NCA.png b/src/Ryujinx/Assets/UIImages/Icon_NCA.png similarity index 100% rename from src/Ryujinx.UI.Common/Resources/Icon_NCA.png rename to src/Ryujinx/Assets/UIImages/Icon_NCA.png diff --git a/src/Ryujinx.UI.Common/Resources/Icon_NRO.png b/src/Ryujinx/Assets/UIImages/Icon_NRO.png similarity index 100% rename from src/Ryujinx.UI.Common/Resources/Icon_NRO.png rename to src/Ryujinx/Assets/UIImages/Icon_NRO.png diff --git a/src/Ryujinx.UI.Common/Resources/Icon_NSO.png b/src/Ryujinx/Assets/UIImages/Icon_NSO.png similarity index 100% rename from src/Ryujinx.UI.Common/Resources/Icon_NSO.png rename to src/Ryujinx/Assets/UIImages/Icon_NSO.png diff --git a/src/Ryujinx.UI.Common/Resources/Icon_NSP.png b/src/Ryujinx/Assets/UIImages/Icon_NSP.png similarity index 100% rename from src/Ryujinx.UI.Common/Resources/Icon_NSP.png rename to src/Ryujinx/Assets/UIImages/Icon_NSP.png diff --git a/src/Ryujinx.UI.Common/Resources/Icon_XCI.png b/src/Ryujinx/Assets/UIImages/Icon_XCI.png similarity index 100% rename from src/Ryujinx.UI.Common/Resources/Icon_XCI.png rename to src/Ryujinx/Assets/UIImages/Icon_XCI.png diff --git a/src/Ryujinx.UI.Common/Resources/Logo_Amiibo.png b/src/Ryujinx/Assets/UIImages/Logo_Amiibo.png similarity index 100% rename from src/Ryujinx.UI.Common/Resources/Logo_Amiibo.png rename to src/Ryujinx/Assets/UIImages/Logo_Amiibo.png diff --git a/src/Ryujinx.UI.Common/Resources/Logo_Discord_Dark.png b/src/Ryujinx/Assets/UIImages/Logo_Discord_Dark.png similarity index 100% rename from src/Ryujinx.UI.Common/Resources/Logo_Discord_Dark.png rename to src/Ryujinx/Assets/UIImages/Logo_Discord_Dark.png diff --git a/src/Ryujinx.UI.Common/Resources/Logo_Discord_Light.png b/src/Ryujinx/Assets/UIImages/Logo_Discord_Light.png similarity index 100% rename from src/Ryujinx.UI.Common/Resources/Logo_Discord_Light.png rename to src/Ryujinx/Assets/UIImages/Logo_Discord_Light.png diff --git a/src/Ryujinx.UI.Common/Resources/Logo_GitHub_Dark.png b/src/Ryujinx/Assets/UIImages/Logo_GitHub_Dark.png similarity index 100% rename from src/Ryujinx.UI.Common/Resources/Logo_GitHub_Dark.png rename to src/Ryujinx/Assets/UIImages/Logo_GitHub_Dark.png diff --git a/src/Ryujinx.UI.Common/Resources/Logo_GitHub_Light.png b/src/Ryujinx/Assets/UIImages/Logo_GitHub_Light.png similarity index 100% rename from src/Ryujinx.UI.Common/Resources/Logo_GitHub_Light.png rename to src/Ryujinx/Assets/UIImages/Logo_GitHub_Light.png diff --git a/src/Ryujinx.UI.Common/Resources/Logo_Ryujinx.png b/src/Ryujinx/Assets/UIImages/Logo_Ryujinx.png similarity index 100% rename from src/Ryujinx.UI.Common/Resources/Logo_Ryujinx.png rename to src/Ryujinx/Assets/UIImages/Logo_Ryujinx.png diff --git a/src/Ryujinx.UI.Common/Resources/Logo_Ryujinx_AntiAlias.png b/src/Ryujinx/Assets/UIImages/Logo_Ryujinx_AntiAlias.png similarity index 100% rename from src/Ryujinx.UI.Common/Resources/Logo_Ryujinx_AntiAlias.png rename to src/Ryujinx/Assets/UIImages/Logo_Ryujinx_AntiAlias.png diff --git a/src/Ryujinx/Common/ApplicationHelper.cs b/src/Ryujinx/Common/ApplicationHelper.cs index 1c6b53dee..7db933ed6 100644 --- a/src/Ryujinx/Common/ApplicationHelper.cs +++ b/src/Ryujinx/Common/ApplicationHelper.cs @@ -15,12 +15,12 @@ using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Common.Helper; using Ryujinx.Common.Logging; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.Loaders.Processes.Extensions; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Helper; using System; using System.Buffers; using System.IO; diff --git a/src/Ryujinx/Common/LocaleManager.cs b/src/Ryujinx/Common/LocaleManager.cs index 70b04ec95..9422cf7fb 100644 --- a/src/Ryujinx/Common/LocaleManager.cs +++ b/src/Ryujinx/Common/LocaleManager.cs @@ -1,8 +1,8 @@ using Gommon; using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Common; using Ryujinx.Common.Utilities; -using Ryujinx.UI.Common.Configuration; using System; using System.Collections.Concurrent; using System.Collections.Generic; diff --git a/src/Ryujinx.UI.Common/Models/Amiibo/AmiiboApi.cs b/src/Ryujinx/Common/Models/Amiibo/AmiiboApi.cs similarity index 97% rename from src/Ryujinx.UI.Common/Models/Amiibo/AmiiboApi.cs rename to src/Ryujinx/Common/Models/Amiibo/AmiiboApi.cs index 7989f0f1a..826e98d4f 100644 --- a/src/Ryujinx.UI.Common/Models/Amiibo/AmiiboApi.cs +++ b/src/Ryujinx/Common/Models/Amiibo/AmiiboApi.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Ryujinx.UI.Common.Models.Amiibo +namespace Ryujinx.Ava.Common.Models.Amiibo { public struct AmiiboApi : IEquatable { diff --git a/src/Ryujinx.UI.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs b/src/Ryujinx/Common/Models/Amiibo/AmiiboApiGamesSwitch.cs similarity index 90% rename from src/Ryujinx.UI.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs rename to src/Ryujinx/Common/Models/Amiibo/AmiiboApiGamesSwitch.cs index 40e635bf0..7d43506e4 100644 --- a/src/Ryujinx.UI.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs +++ b/src/Ryujinx/Common/Models/Amiibo/AmiiboApiGamesSwitch.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Ryujinx.UI.Common.Models.Amiibo +namespace Ryujinx.Ava.Common.Models.Amiibo { public class AmiiboApiGamesSwitch { diff --git a/src/Ryujinx.UI.Common/Models/Amiibo/AmiiboApiUsage.cs b/src/Ryujinx/Common/Models/Amiibo/AmiiboApiUsage.cs similarity index 84% rename from src/Ryujinx.UI.Common/Models/Amiibo/AmiiboApiUsage.cs rename to src/Ryujinx/Common/Models/Amiibo/AmiiboApiUsage.cs index 4f8d292b1..911323182 100644 --- a/src/Ryujinx.UI.Common/Models/Amiibo/AmiiboApiUsage.cs +++ b/src/Ryujinx/Common/Models/Amiibo/AmiiboApiUsage.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Ryujinx.UI.Common.Models.Amiibo +namespace Ryujinx.Ava.Common.Models.Amiibo { public class AmiiboApiUsage { diff --git a/src/Ryujinx.UI.Common/Models/Amiibo/AmiiboJson.cs b/src/Ryujinx/Common/Models/Amiibo/AmiiboJson.cs similarity index 87% rename from src/Ryujinx.UI.Common/Models/Amiibo/AmiiboJson.cs rename to src/Ryujinx/Common/Models/Amiibo/AmiiboJson.cs index 15083f505..39ca94bb6 100644 --- a/src/Ryujinx.UI.Common/Models/Amiibo/AmiiboJson.cs +++ b/src/Ryujinx/Common/Models/Amiibo/AmiiboJson.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Ryujinx.UI.Common.Models.Amiibo +namespace Ryujinx.Ava.Common.Models.Amiibo { public struct AmiiboJson { diff --git a/src/Ryujinx.UI.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs b/src/Ryujinx/Common/Models/Amiibo/AmiiboJsonSerializerContext.cs similarity index 69% rename from src/Ryujinx.UI.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs rename to src/Ryujinx/Common/Models/Amiibo/AmiiboJsonSerializerContext.cs index bc3f1303c..e0fe11bc5 100644 --- a/src/Ryujinx.UI.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs +++ b/src/Ryujinx/Common/Models/Amiibo/AmiiboJsonSerializerContext.cs @@ -1,9 +1,7 @@ using System.Text.Json.Serialization; -namespace Ryujinx.UI.Common.Models.Amiibo +namespace Ryujinx.Ava.Common.Models.Amiibo { [JsonSerializable(typeof(AmiiboJson))] - public partial class AmiiboJsonSerializerContext : JsonSerializerContext - { - } + public partial class AmiiboJsonSerializerContext : JsonSerializerContext; } diff --git a/src/Ryujinx.UI.Common/Models/DownloadableContentModel.cs b/src/Ryujinx/Common/Models/DownloadableContentModel.cs similarity index 93% rename from src/Ryujinx.UI.Common/Models/DownloadableContentModel.cs rename to src/Ryujinx/Common/Models/DownloadableContentModel.cs index 95c64f078..ad9934bd2 100644 --- a/src/Ryujinx.UI.Common/Models/DownloadableContentModel.cs +++ b/src/Ryujinx/Common/Models/DownloadableContentModel.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.UI.Common.Models +namespace Ryujinx.Ava.Common.Models { // NOTE: most consuming code relies on this model being value-comparable public record DownloadableContentModel(ulong TitleId, string ContainerPath, string FullPath) diff --git a/src/Ryujinx.UI.Common/Models/Github/GithubReleaseAssetJsonResponse.cs b/src/Ryujinx/Common/Models/Github/GithubReleaseAssetJsonResponse.cs similarity index 82% rename from src/Ryujinx.UI.Common/Models/Github/GithubReleaseAssetJsonResponse.cs rename to src/Ryujinx/Common/Models/Github/GithubReleaseAssetJsonResponse.cs index 8f528dc0b..633e16596 100644 --- a/src/Ryujinx.UI.Common/Models/Github/GithubReleaseAssetJsonResponse.cs +++ b/src/Ryujinx/Common/Models/Github/GithubReleaseAssetJsonResponse.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.UI.Common.Models.Github +namespace Ryujinx.Ava.Common.Models.Github { public class GithubReleaseAssetJsonResponse { diff --git a/src/Ryujinx.UI.Common/Models/Github/GithubReleasesJsonResponse.cs b/src/Ryujinx/Common/Models/Github/GithubReleasesJsonResponse.cs similarity index 85% rename from src/Ryujinx.UI.Common/Models/Github/GithubReleasesJsonResponse.cs rename to src/Ryujinx/Common/Models/Github/GithubReleasesJsonResponse.cs index 7bec1bcdc..dee912e1c 100644 --- a/src/Ryujinx.UI.Common/Models/Github/GithubReleasesJsonResponse.cs +++ b/src/Ryujinx/Common/Models/Github/GithubReleasesJsonResponse.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Ryujinx.UI.Common.Models.Github +namespace Ryujinx.Ava.Common.Models.Github { public class GithubReleasesJsonResponse { diff --git a/src/Ryujinx.UI.Common/Models/Github/GithubReleasesJsonSerializerContext.cs b/src/Ryujinx/Common/Models/Github/GithubReleasesJsonSerializerContext.cs similarity index 74% rename from src/Ryujinx.UI.Common/Models/Github/GithubReleasesJsonSerializerContext.cs rename to src/Ryujinx/Common/Models/Github/GithubReleasesJsonSerializerContext.cs index 71864257c..a450ab22d 100644 --- a/src/Ryujinx.UI.Common/Models/Github/GithubReleasesJsonSerializerContext.cs +++ b/src/Ryujinx/Common/Models/Github/GithubReleasesJsonSerializerContext.cs @@ -1,9 +1,7 @@ using System.Text.Json.Serialization; -namespace Ryujinx.UI.Common.Models.Github +namespace Ryujinx.Ava.Common.Models.Github { [JsonSerializable(typeof(GithubReleasesJsonResponse), GenerationMode = JsonSourceGenerationMode.Metadata)] - public partial class GithubReleasesJsonSerializerContext : JsonSerializerContext - { - } + public partial class GithubReleasesJsonSerializerContext : JsonSerializerContext; } diff --git a/src/Ryujinx.UI.Common/Models/TitleUpdateModel.cs b/src/Ryujinx/Common/Models/TitleUpdateModel.cs similarity index 91% rename from src/Ryujinx.UI.Common/Models/TitleUpdateModel.cs rename to src/Ryujinx/Common/Models/TitleUpdateModel.cs index 5422e1303..fedecddbe 100644 --- a/src/Ryujinx.UI.Common/Models/TitleUpdateModel.cs +++ b/src/Ryujinx/Common/Models/TitleUpdateModel.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.UI.Common.Models +namespace Ryujinx.Ava.Common.Models { // NOTE: most consuming code relies on this model being value-comparable public record TitleUpdateModel(ulong TitleId, ulong Version, string DisplayVersion, string Path) diff --git a/src/Ryujinx.UI.Common/Models/XCITrimmerFileModel.cs b/src/Ryujinx/Common/Models/XCITrimmerFileModel.cs similarity index 95% rename from src/Ryujinx.UI.Common/Models/XCITrimmerFileModel.cs rename to src/Ryujinx/Common/Models/XCITrimmerFileModel.cs index 95fb3985b..526bf230c 100644 --- a/src/Ryujinx.UI.Common/Models/XCITrimmerFileModel.cs +++ b/src/Ryujinx/Common/Models/XCITrimmerFileModel.cs @@ -1,8 +1,8 @@ +using Ryujinx.Ava.Utilities.AppLibrary; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; -using Ryujinx.UI.App.Common; -namespace Ryujinx.UI.Common.Models +namespace Ryujinx.Ava.Common.Models { public record XCITrimmerFileModel( string Name, diff --git a/src/Ryujinx.UI.Common/DiscordIntegrationModule.cs b/src/Ryujinx/DiscordIntegrationModule.cs similarity index 97% rename from src/Ryujinx.UI.Common/DiscordIntegrationModule.cs rename to src/Ryujinx/DiscordIntegrationModule.cs index efeeb2586..ee00f2c0d 100644 --- a/src/Ryujinx.UI.Common/DiscordIntegrationModule.cs +++ b/src/Ryujinx/DiscordIntegrationModule.cs @@ -1,14 +1,14 @@ using DiscordRPC; using Humanizer; using Humanizer.Localisation; +using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Common; using Ryujinx.HLE; using Ryujinx.HLE.Loaders.Processes; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common.Configuration; using System.Text; -namespace Ryujinx.UI.Common +namespace Ryujinx.Ava { public static class DiscordIntegrationModule { diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs index ba84e53a5..19d2fb94e 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs @@ -2,6 +2,7 @@ using LibHac.Tools.FsSystem; using Ryujinx.Audio.Backends.SDL2; using Ryujinx.Ava; +using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; @@ -16,8 +17,6 @@ using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.Vulkan; using Ryujinx.HLE; using Ryujinx.Input; -using Ryujinx.UI.Common; -using Ryujinx.UI.Common.Configuration; using Silk.NET.Vulkan; using System; using System.IO; diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs index eabe72cbe..3cb0afca3 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -1,6 +1,7 @@ using CommandLine; using Gommon; using Ryujinx.Ava; +using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; @@ -13,9 +14,6 @@ using Ryujinx.Cpu; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu.Shader; -using Ryujinx.Graphics.Metal; -using Ryujinx.Graphics.OpenGL; -using Ryujinx.Graphics.Vulkan; using Ryujinx.Graphics.Vulkan.MoltenVK; using Ryujinx.HLE; using Ryujinx.HLE.FileSystem; @@ -25,7 +23,6 @@ using Ryujinx.Input; using Ryujinx.Input.HLE; using Ryujinx.Input.SDL2; using Ryujinx.SDL2.Common; -using Ryujinx.UI.Common.Configuration; using System; using System.Collections.Generic; using System.IO; diff --git a/src/Ryujinx/Headless/Options.cs b/src/Ryujinx/Headless/Options.cs index b0a349a2b..c0def95c1 100644 --- a/src/Ryujinx/Headless/Options.cs +++ b/src/Ryujinx/Headless/Options.cs @@ -1,11 +1,11 @@ using CommandLine; using Gommon; +using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; using Ryujinx.HLE; using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.SystemState; -using Ryujinx.UI.Common.Configuration; using System; using System.Collections.Generic; using System.Linq; diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index bde08a372..6f0f3e12e 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -8,6 +8,10 @@ using Projektanker.Icons.Avalonia.MaterialDesign; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Windows; +using Ryujinx.Ava.Utilities; +using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Ava.Utilities.SystemInfo; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.GraphicsDriver; @@ -16,11 +20,6 @@ using Ryujinx.Common.SystemInterop; using Ryujinx.Graphics.Vulkan.MoltenVK; using Ryujinx.Headless; using Ryujinx.SDL2.Common; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Helper; -using Ryujinx.UI.Common.SystemInfo; using System; using System.IO; using System.Linq; @@ -117,9 +116,6 @@ namespace Ryujinx.Ava // Setup base data directory. AppDataManager.Initialize(CommandLineState.BaseDirPathArg); - // Set the delegate for localizing the word "never" in the UI - ApplicationData.LocalizedNever = () => LocaleManager.Instance[LocaleKeys.Never]; - // Initialize the configuration. ConfigurationState.Initialize(); diff --git a/src/Ryujinx/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj index 5e5adf2a0..9d23b0909 100644 --- a/src/Ryujinx/Ryujinx.csproj +++ b/src/Ryujinx/Ryujinx.csproj @@ -48,6 +48,7 @@ + @@ -57,6 +58,7 @@ + @@ -77,7 +79,6 @@ - @@ -128,21 +129,36 @@ + + Assets\ShortcutFiles\shortcut-template.desktop + + + Assets\ShortcutFiles\shortcut-launch-script.sh + + + Assets\ShortcutFiles\shortcut-template.plist + + + + + + + + + + + + + - - - SettingsHacksView.axaml - Code - - \ No newline at end of file diff --git a/src/Ryujinx/RyujinxApp.axaml.cs b/src/Ryujinx/RyujinxApp.axaml.cs index bbef20aa0..d950af3a9 100644 --- a/src/Ryujinx/RyujinxApp.axaml.cs +++ b/src/Ryujinx/RyujinxApp.axaml.cs @@ -11,10 +11,10 @@ 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.Common; using Ryujinx.Common.Logging; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Helper; using System; using System.Diagnostics; diff --git a/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs b/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs index 893ea95ac..65f4c7795 100644 --- a/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs +++ b/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs @@ -5,12 +5,12 @@ using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Windows; +using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.HLE; using Ryujinx.HLE.HOS.Applets; using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard; using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types; using Ryujinx.HLE.UI; -using Ryujinx.UI.Common.Configuration; using System; using System.Threading; diff --git a/src/Ryujinx/UI/Applet/ErrorAppletWindow.axaml b/src/Ryujinx/UI/Applet/ErrorAppletWindow.axaml index c7aa56fb8..22c2851e1 100644 --- a/src/Ryujinx/UI/Applet/ErrorAppletWindow.axaml +++ b/src/Ryujinx/UI/Applet/ErrorAppletWindow.axaml @@ -25,7 +25,7 @@ Height="80" MinWidth="50" Margin="5,10,20,10" - Source="resm:Ryujinx.UI.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.UI.Common" /> + Source="resm:Ryujinx.Assets.UIImages.Logo_Ryujinx.png?assembly=Ryujinx" /> + Source="resm:Ryujinx.Assets.UIImages.Logo_Ryujinx.png?assembly=Ryujinx" /> + Source="resm:Ryujinx.Assets.UIImages.Logo_Ryujinx.png?assembly=Ryujinx" /> new(Avalonia.Platform.AssetLoader.Open(new Uri(uri))); diff --git a/src/Ryujinx/UI/ViewModels/AmiiboWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/AmiiboWindowViewModel.cs index ab08ce385..01a5dadd3 100644 --- a/src/Ryujinx/UI/ViewModels/AmiiboWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/AmiiboWindowViewModel.cs @@ -3,13 +3,13 @@ using Avalonia.Collections; using Avalonia.Media.Imaging; using Avalonia.Threading; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.Common.Models.Amiibo; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; -using Ryujinx.UI.Common.Models.Amiibo; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -68,7 +68,7 @@ namespace Ryujinx.Ava.UI.ViewModels _amiiboSeries = new ObservableCollection(); _amiibos = new AvaloniaList(); - _amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.UI.Common/Resources/Logo_Amiibo.png"); + _amiiboLogoBytes = EmbeddedResources.Read("Ryujinx/Assets/UIImages/Logo_Amiibo.png"); _ = LoadContentAsync(); } diff --git a/src/Ryujinx/UI/ViewModels/AppListFavoriteComparable.cs b/src/Ryujinx/UI/ViewModels/AppListFavoriteComparable.cs index e80984508..9c37368de 100644 --- a/src/Ryujinx/UI/ViewModels/AppListFavoriteComparable.cs +++ b/src/Ryujinx/UI/ViewModels/AppListFavoriteComparable.cs @@ -1,4 +1,4 @@ -using Ryujinx.UI.App.Common; +using Ryujinx.Ava.Utilities.AppLibrary; using System; namespace Ryujinx.Ava.UI.ViewModels diff --git a/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs b/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs index 3abaae3ae..4e9660a65 100644 --- a/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs @@ -5,10 +5,10 @@ using Avalonia.Threading; using DynamicData; 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.HLE.FileSystem; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common.Models; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs index 493e6659d..74b8681d5 100644 --- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs @@ -10,6 +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.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; @@ -19,7 +20,6 @@ using Ryujinx.Common.Configuration.Hid.Keyboard; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; using Ryujinx.Input; -using Ryujinx.UI.Common.Configuration; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -35,10 +35,10 @@ namespace Ryujinx.Ava.UI.ViewModels.Input public class InputViewModel : BaseModel, IDisposable { private const string Disabled = "disabled"; - private const string ProControllerResource = "Ryujinx.UI.Common/Resources/Controller_ProCon.svg"; - private const string JoyConPairResource = "Ryujinx.UI.Common/Resources/Controller_JoyConPair.svg"; - private const string JoyConLeftResource = "Ryujinx.UI.Common/Resources/Controller_JoyConLeft.svg"; - private const string JoyConRightResource = "Ryujinx.UI.Common/Resources/Controller_JoyConRight.svg"; + private const string ProControllerResource = "Ryujinx/Assets/Icons/Controller_ProCon.svg"; + private const string JoyConPairResource = "Ryujinx/Assets/Icons/Controller_JoyConPair.svg"; + private const string JoyConLeftResource = "Ryujinx/Assets/Icons/Controller_JoyConLeft.svg"; + private const string JoyConRightResource = "Ryujinx/Assets/Icons/Controller_JoyConRight.svg"; private const string KeyboardString = "keyboard"; private const string ControllerString = "controller"; private readonly MainWindow _mainWindow; diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index 2f1800290..332009149 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -21,9 +21,13 @@ 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.Common; using Ryujinx.Common.Configuration; +using Ryujinx.Common.Helper; using Ryujinx.Common.Logging; +using Ryujinx.Common.UI; using Ryujinx.Common.Utilities; using Ryujinx.Cpu; using Ryujinx.HLE; @@ -33,10 +37,6 @@ using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption; using Ryujinx.HLE.UI; using Ryujinx.Input.HLE; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Helper; using Silk.NET.Vulkan; using SkiaSharp; using System; @@ -133,7 +133,7 @@ namespace Ryujinx.Ava.UI.ViewModels // 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(ConfigurationState))!.GetManifestResourceStream("Ryujinx.UI.Common.Resources.Logo_Ryujinx_AntiAlias.png")!); + new(Assembly.GetAssembly(typeof(MainWindowViewModel))!.GetManifestResourceStream("Ryujinx.Assets.UIImages.Logo_Ryujinx_AntiAlias.png")!); public MainWindow Window { get; init; } diff --git a/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs new file mode 100644 index 000000000..b93cdd6dc --- /dev/null +++ b/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs @@ -0,0 +1,77 @@ +using Gommon; +using Ryujinx.Ava.Utilities.Configuration; + +namespace Ryujinx.Ava.UI.ViewModels +{ + public class SettingsHacksViewModel : BaseModel + { + private readonly SettingsViewModel _baseViewModel; + + public SettingsHacksViewModel() {} + + public SettingsHacksViewModel(SettingsViewModel settingsVm) + { + _baseViewModel = settingsVm; + } + + private bool _xc2MenuSoftlockFix = ConfigurationState.Instance.Hacks.Xc2MenuSoftlockFix; + private bool _shaderTranslationThreadSleep = ConfigurationState.Instance.Hacks.EnableShaderTranslationDelay; + private int _shaderTranslationSleepDelay = ConfigurationState.Instance.Hacks.ShaderTranslationDelay; + + public bool Xc2MenuSoftlockFixEnabled + { + get => _xc2MenuSoftlockFix; + set + { + _xc2MenuSoftlockFix = value; + + OnPropertyChanged(); + } + } + + public bool ShaderTranslationDelayEnabled + { + get => _shaderTranslationThreadSleep; + set + { + _shaderTranslationThreadSleep = value; + + OnPropertyChanged(); + } + } + + public string ShaderTranslationDelayTooltipText => $"Current value: {ShaderTranslationDelay}"; + + public int ShaderTranslationDelay + { + get => _shaderTranslationSleepDelay; + set + { + _shaderTranslationSleepDelay = value; + + OnPropertiesChanged(nameof(ShaderTranslationDelay), nameof(ShaderTranslationDelayTooltipText)); + } + } + + 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.") + .AppendLine(); + + sb.AppendLine("From the issue on GitHub:").AppendLine(); + sb.Append( + "When 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."); + }); + + public static string ShaderTranslationDelayTooltip { get; } = Lambda.String(sb => + { + sb.AppendLine("This hack applies the delay you specify every time shaders are attempted to be translated.") + .AppendLine(); + + sb.Append("Configurable via slider, only when this option is enabled."); + }); + } +} diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index ecd2d40dd..a5bdd2f88 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -10,6 +10,8 @@ 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.Common.Configuration; using Ryujinx.Common.Configuration.Multiplayer; using Ryujinx.Common.GraphicsDriver; @@ -18,8 +20,6 @@ using Ryujinx.Graphics.Vulkan; using Ryujinx.HLE; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Services.Time.TimeZone; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Configuration.System; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -65,7 +65,7 @@ namespace Ryujinx.Ava.UI.ViewModels private string _ldnPassphrase; private string _ldnServer; - private bool _xc2MenuSoftlockFix = ConfigurationState.Instance.Hacks.Xc2MenuSoftlockFix; + public SettingsHacksViewModel DirtyHacks { get; } public int ResolutionScale { @@ -277,17 +277,6 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public bool Xc2MenuSoftlockFixEnabled - { - get => _xc2MenuSoftlockFix; - set - { - _xc2MenuSoftlockFix = value; - - OnPropertyChanged(); - } - } - public int Language { get; set; } public int Region { get; set; } public int FsGlobalAccessLogMode { get; set; } @@ -400,9 +389,12 @@ namespace Ryujinx.Ava.UI.ViewModels { _virtualFileSystem = virtualFileSystem; _contentManager = contentManager; + if (Program.PreviewerDetached) { Task.Run(LoadTimeZones); + + DirtyHacks = new SettingsHacksViewModel(this); } } @@ -422,6 +414,8 @@ namespace Ryujinx.Ava.UI.ViewModels { Task.Run(LoadAvailableGpus); LoadCurrentConfiguration(); + + DirtyHacks = new SettingsHacksViewModel(this); } } @@ -636,9 +630,9 @@ namespace Ryujinx.Ava.UI.ViewModels OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value; MultiplayerModeIndex = (int)config.Multiplayer.Mode.Value; - DisableP2P = config.Multiplayer.DisableP2p.Value; - LdnPassphrase = config.Multiplayer.LdnPassphrase.Value; - LdnServer = config.Multiplayer.LdnServer.Value; + DisableP2P = config.Multiplayer.DisableP2p; + LdnPassphrase = config.Multiplayer.LdnPassphrase; + LdnServer = config.Multiplayer.LdnServer; } public void SaveSettings() @@ -762,7 +756,9 @@ namespace Ryujinx.Ava.UI.ViewModels config.Multiplayer.LdnServer.Value = LdnServer; // Dirty Hacks - config.Hacks.Xc2MenuSoftlockFix.Value = Xc2MenuSoftlockFixEnabled; + config.Hacks.Xc2MenuSoftlockFix.Value = DirtyHacks.Xc2MenuSoftlockFixEnabled; + config.Hacks.EnableShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelayEnabled; + config.Hacks.ShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelay; config.ToFileFormat().SaveConfig(Program.ConfigurationPath); @@ -796,18 +792,5 @@ namespace Ryujinx.Ava.UI.ViewModels RevertIfNotSaved(); CloseWindow?.Invoke(); } - - 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.") - .AppendLine(); - - sb.AppendLine("From the issue on GitHub:").AppendLine(); - sb.Append( - "When 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."); - }); } } diff --git a/src/Ryujinx/UI/ViewModels/TitleUpdateViewModel.cs b/src/Ryujinx/UI/ViewModels/TitleUpdateViewModel.cs index dacdc3056..a179218af 100644 --- a/src/Ryujinx/UI/ViewModels/TitleUpdateViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/TitleUpdateViewModel.cs @@ -4,10 +4,10 @@ using Avalonia.Platform.Storage; using Avalonia.Threading; 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.HLE.FileSystem; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common.Models; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/src/Ryujinx/UI/ViewModels/XCITrimmerViewModel.cs b/src/Ryujinx/UI/ViewModels/XCITrimmerViewModel.cs index 402b182af..64965cd96 100644 --- a/src/Ryujinx/UI/ViewModels/XCITrimmerViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/XCITrimmerViewModel.cs @@ -4,10 +4,10 @@ using Gommon; using Avalonia.Threading; using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.Common.Models; using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.Utilities.AppLibrary; using Ryujinx.Common.Utilities; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common.Models; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -371,6 +371,16 @@ namespace Ryujinx.Ava.UI.ViewModels } } + public XCITrimmerFileModel NullableProcessingApplication + { + get => _processingApplication.OrDefault(); + set + { + _processingApplication = value; + OnPropertyChanged(); + } + } + public bool Processing { get => _cancellationTokenSource != null; diff --git a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs index 181773b2e..be444faa4 100644 --- a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs @@ -7,12 +7,12 @@ using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; +using Ryujinx.Ava.Utilities; +using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Common; +using Ryujinx.Common.Helper; using Ryujinx.Common.Utilities; using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption; -using Ryujinx.UI.Common; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Helper; using System; using System.Collections.Generic; using System.Linq; diff --git a/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml.cs b/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml.cs index 297e86c67..b234f7859 100644 --- a/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml.cs +++ b/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml.cs @@ -5,10 +5,10 @@ using Avalonia.Interactivity; using Avalonia.Threading; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Windows; +using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; -using Ryujinx.UI.Common.Configuration; using System; namespace Ryujinx.Ava.UI.Views.Main diff --git a/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml index b7817f064..087112368 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml @@ -29,19 +29,47 @@ + Text="Game-specific hacks & tricks to alleviate performance issues or crashing. Will cause issues." /> + ToolTip.Tip="{Binding DirtyHacks.Xc2MenuFixTooltip}"> + IsChecked="{Binding DirtyHacks.Xc2MenuSoftlockFixEnabled}"/> + + + + + + + diff --git a/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml.cs b/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml.cs index f9e0958ca..0017b5583 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml.cs +++ b/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml.cs @@ -1,14 +1,9 @@ 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(); diff --git a/src/Ryujinx/UI/Windows/AboutWindow.axaml b/src/Ryujinx/UI/Windows/AboutWindow.axaml index c5abb0241..1b00ad23c 100644 --- a/src/Ryujinx/UI/Windows/AboutWindow.axaml +++ b/src/Ryujinx/UI/Windows/AboutWindow.axaml @@ -38,7 +38,7 @@ it.HasControlHolder)) { ref var controlHolder = ref application.ControlHolder.Value; diff --git a/src/Ryujinx/UI/Windows/ModManagerWindow.axaml.cs b/src/Ryujinx/UI/Windows/ModManagerWindow.axaml.cs index 774446cf1..449aab554 100644 --- a/src/Ryujinx/UI/Windows/ModManagerWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/ModManagerWindow.axaml.cs @@ -6,7 +6,7 @@ using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.ViewModels; -using Ryujinx.UI.Common.Helper; +using Ryujinx.Common.Helper; using System.Threading.Tasks; using Button = Avalonia.Controls.Button; diff --git a/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs b/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs index 4808bcbd5..8caa4454a 100644 --- a/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs @@ -87,7 +87,7 @@ namespace Ryujinx.Ava.UI.Windows NavPanel.Content = LoggingPage; break; case nameof(HacksPage): - HacksPage.ViewModel = ViewModel; + HacksPage.DataContext = ViewModel; NavPanel.Content = HacksPage; break; default: diff --git a/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml b/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml index 6e22cfed7..0ba9bc7d8 100644 --- a/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml +++ b/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml @@ -6,9 +6,9 @@ xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" - xmlns:models="clr-namespace:Ryujinx.UI.Common.Models;assembly=Ryujinx.UI.Common" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" + xmlns:models="clr-namespace:Ryujinx.Ava.Common.Models" Width="500" Height="300" mc:Ignorable="d" diff --git a/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml.cs b/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml.cs index a13ad4012..b7c421c96 100644 --- a/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml.cs @@ -3,10 +3,10 @@ using Avalonia.Interactivity; using Avalonia.Styling; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.Common.Models; using Ryujinx.Ava.UI.ViewModels; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common.Helper; -using Ryujinx.UI.Common.Models; +using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Common.Helper; using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Windows diff --git a/src/Ryujinx/UI/Windows/XCITrimmerWindow.axaml b/src/Ryujinx/UI/Windows/XCITrimmerWindow.axaml index d726f8099..2d56931b5 100644 --- a/src/Ryujinx/UI/Windows/XCITrimmerWindow.axaml +++ b/src/Ryujinx/UI/Windows/XCITrimmerWindow.axaml @@ -6,9 +6,8 @@ xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" - xmlns:models="clr-namespace:Ryujinx.UI.Common.Models;assembly=Ryujinx.UI.Common" - xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" + xmlns:models="clr-namespace:Ryujinx.Ava.Common.Models" Width="700" Height="600" x:DataType="viewModels:XCITrimmerViewModel" @@ -140,7 +139,7 @@ Padding="2.5"> LocalizedNever { get; set; } = () => "Never"; - public bool Favorite { get; set; } public byte[] Icon { get; set; } public string Name { get; set; } = "Unknown"; @@ -40,7 +37,7 @@ namespace Ryujinx.UI.App.Common public string TimePlayedString => ValueFormatUtils.FormatTimeSpan(TimePlayed); - public string LastPlayedString => ValueFormatUtils.FormatDateTime(LastPlayed)?.Replace(" ", "\n") ?? LocalizedNever(); + public string LastPlayedString => ValueFormatUtils.FormatDateTime(LastPlayed)?.Replace(" ", "\n"); public string FileSizeString => ValueFormatUtils.FormatFileSize(FileSize); diff --git a/src/Ryujinx.UI.Common/App/ApplicationJsonSerializerContext.cs b/src/Ryujinx/Utilities/AppLibrary/ApplicationJsonSerializerContext.cs similarity index 85% rename from src/Ryujinx.UI.Common/App/ApplicationJsonSerializerContext.cs rename to src/Ryujinx/Utilities/AppLibrary/ApplicationJsonSerializerContext.cs index ada7cc346..d443ab66e 100644 --- a/src/Ryujinx.UI.Common/App/ApplicationJsonSerializerContext.cs +++ b/src/Ryujinx/Utilities/AppLibrary/ApplicationJsonSerializerContext.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Ryujinx.UI.App.Common +namespace Ryujinx.Ava.Utilities.AppLibrary { [JsonSourceGenerationOptions(WriteIndented = true)] [JsonSerializable(typeof(ApplicationMetadata))] diff --git a/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs b/src/Ryujinx/Utilities/AppLibrary/ApplicationLibrary.cs similarity index 98% rename from src/Ryujinx.UI.Common/App/ApplicationLibrary.cs rename to src/Ryujinx/Utilities/AppLibrary/ApplicationLibrary.cs index e78af3121..28b4262f1 100644 --- a/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs +++ b/src/Ryujinx/Utilities/AppLibrary/ApplicationLibrary.cs @@ -10,6 +10,10 @@ using LibHac.Ns; 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.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Multiplayer; using Ryujinx.Common.Logging; @@ -19,16 +23,11 @@ using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Loaders.Npdm; using Ryujinx.HLE.Loaders.Processes.Extensions; using Ryujinx.HLE.Utilities; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Configuration.System; -using Ryujinx.UI.Common.Helper; -using Ryujinx.UI.Common.Models; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; -using System.Reflection; using System.Text; using System.Text.Json; using System.Threading; @@ -38,7 +37,7 @@ using MissingKeyException = LibHac.Common.Keys.MissingKeyException; using Path = System.IO.Path; using TimeSpan = System.TimeSpan; -namespace Ryujinx.UI.App.Common +namespace Ryujinx.Ava.Utilities.AppLibrary { public class ApplicationLibrary { @@ -76,21 +75,11 @@ namespace Ryujinx.UI.App.Common TitleUpdates = _titleUpdates.AsObservableCache(); DownloadableContents = _downloadableContents.AsObservableCache(); - _nspIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_NSP.png"); - _xciIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_XCI.png"); - _ncaIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_NCA.png"); - _nroIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_NRO.png"); - _nsoIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_NSO.png"); - } - - private static byte[] GetResourceBytes(string resourceName) - { - Stream resourceStream = Assembly.GetCallingAssembly().GetManifestResourceStream(resourceName)!; - byte[] resourceByteArray = new byte[resourceStream.Length]; - - resourceStream.ReadExactly(resourceByteArray); - - return resourceByteArray; + _nspIcon = EmbeddedResources.Read("Ryujinx/Assets.UIImages.Icon_NSP.png"); + _xciIcon = EmbeddedResources.Read("Ryujinx/Assets.UIImages.Icon_XCI.png"); + _ncaIcon = EmbeddedResources.Read("Ryujinx/Assets.UIImages.Icon_NCA.png"); + _nroIcon = EmbeddedResources.Read("Ryujinx/Assets.UIImages.Icon_NRO.png"); + _nsoIcon = EmbeddedResources.Read("Ryujinx/Assets.UIImages.Icon_NSO.png"); } /// The npdm file doesn't contain valid data. diff --git a/src/Ryujinx.UI.Common/App/ApplicationMetadata.cs b/src/Ryujinx/Utilities/AppLibrary/ApplicationMetadata.cs similarity index 97% rename from src/Ryujinx.UI.Common/App/ApplicationMetadata.cs rename to src/Ryujinx/Utilities/AppLibrary/ApplicationMetadata.cs index 81193c5b3..d823c7482 100644 --- a/src/Ryujinx.UI.Common/App/ApplicationMetadata.cs +++ b/src/Ryujinx/Utilities/AppLibrary/ApplicationMetadata.cs @@ -1,7 +1,7 @@ using System; using System.Text.Json.Serialization; -namespace Ryujinx.UI.App.Common +namespace Ryujinx.Ava.Utilities.AppLibrary { public class ApplicationMetadata { diff --git a/src/Ryujinx.UI.Common/App/LdnGameData.cs b/src/Ryujinx/Utilities/AppLibrary/LdnGameData.cs similarity index 96% rename from src/Ryujinx.UI.Common/App/LdnGameData.cs rename to src/Ryujinx/Utilities/AppLibrary/LdnGameData.cs index f7a98e136..c1653df97 100644 --- a/src/Ryujinx.UI.Common/App/LdnGameData.cs +++ b/src/Ryujinx/Utilities/AppLibrary/LdnGameData.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; -namespace Ryujinx.UI.App.Common +namespace Ryujinx.Ava.Utilities.AppLibrary { public struct LdnGameData { diff --git a/src/Ryujinx.UI.Common/App/LdnGameDataReceivedEventArgs.cs b/src/Ryujinx/Utilities/AppLibrary/LdnGameDataReceivedEventArgs.cs similarity index 81% rename from src/Ryujinx.UI.Common/App/LdnGameDataReceivedEventArgs.cs rename to src/Ryujinx/Utilities/AppLibrary/LdnGameDataReceivedEventArgs.cs index 7c7454411..97299c9e8 100644 --- a/src/Ryujinx.UI.Common/App/LdnGameDataReceivedEventArgs.cs +++ b/src/Ryujinx/Utilities/AppLibrary/LdnGameDataReceivedEventArgs.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace Ryujinx.UI.App.Common +namespace Ryujinx.Ava.Utilities.AppLibrary { public class LdnGameDataReceivedEventArgs : EventArgs { diff --git a/src/Ryujinx.UI.Common/App/LdnGameDataSerializerContext.cs b/src/Ryujinx/Utilities/AppLibrary/LdnGameDataSerializerContext.cs similarity index 76% rename from src/Ryujinx.UI.Common/App/LdnGameDataSerializerContext.cs rename to src/Ryujinx/Utilities/AppLibrary/LdnGameDataSerializerContext.cs index ce8edcdb6..90d1894c7 100644 --- a/src/Ryujinx.UI.Common/App/LdnGameDataSerializerContext.cs +++ b/src/Ryujinx/Utilities/AppLibrary/LdnGameDataSerializerContext.cs @@ -1,11 +1,8 @@ using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Ryujinx.UI.App.Common +namespace Ryujinx.Ava.Utilities.AppLibrary { [JsonSerializable(typeof(IEnumerable))] - internal partial class LdnGameDataSerializerContext : JsonSerializerContext - { - - } + internal partial class LdnGameDataSerializerContext : JsonSerializerContext; } diff --git a/src/Ryujinx/Utilities/AppletMetadata.cs b/src/Ryujinx/Utilities/AppletMetadata.cs new file mode 100644 index 000000000..82baed7d3 --- /dev/null +++ b/src/Ryujinx/Utilities/AppletMetadata.cs @@ -0,0 +1,60 @@ +using LibHac.Common; +using LibHac.Ncm; +using LibHac.Ns; +using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.HLE; +using Ryujinx.HLE.FileSystem; + +namespace Ryujinx.Ava.Utilities +{ + public readonly struct AppletMetadata + { + private readonly ContentManager _contentManager; + + public string Name { get; } + public ulong ProgramId { get; } + + public string Version { get; } + + public AppletMetadata(ContentManager contentManager, string name, ulong programId, string version = "1.0.0") + : this(name, programId, version) + { + _contentManager = contentManager; + } + + public AppletMetadata(string name, ulong programId, string version = "1.0.0") + { + Name = name; + ProgramId = programId; + Version = version; + } + + public string GetContentPath(ContentManager contentManager) + => (contentManager ?? _contentManager) + .GetInstalledContentPath(ProgramId, StorageId.BuiltInSystem, NcaContentType.Program); + + public bool CanStart(ContentManager contentManager, out ApplicationData appData, + out BlitStruct appControl) + { + contentManager ??= _contentManager; + if (contentManager == null) + { + appData = null; + appControl = new BlitStruct(0); + return false; + } + + appData = new() { Name = Name, Id = ProgramId, Path = GetContentPath(contentManager) }; + + if (string.IsNullOrEmpty(appData.Path)) + { + appControl = new BlitStruct(0); + return false; + } + + appControl = StructHelpers.CreateCustomNacpData(Name, Version); + return true; + } + } +} diff --git a/src/Ryujinx.UI.Common/Helper/CommandLineState.cs b/src/Ryujinx/Utilities/CommandLineState.cs similarity index 99% rename from src/Ryujinx.UI.Common/Helper/CommandLineState.cs rename to src/Ryujinx/Utilities/CommandLineState.cs index 3a96a55c8..6fb8e92d6 100644 --- a/src/Ryujinx.UI.Common/Helper/CommandLineState.cs +++ b/src/Ryujinx/Utilities/CommandLineState.cs @@ -1,7 +1,7 @@ using Ryujinx.Common.Logging; using System.Collections.Generic; -namespace Ryujinx.UI.Common.Helper +namespace Ryujinx.Ava.Utilities { public static class CommandLineState { diff --git a/src/Ryujinx.UI.Common/Configuration/AudioBackend.cs b/src/Ryujinx/Utilities/Configuration/AudioBackend.cs similarity index 84% rename from src/Ryujinx.UI.Common/Configuration/AudioBackend.cs rename to src/Ryujinx/Utilities/Configuration/AudioBackend.cs index a952e7ac0..8394bb282 100644 --- a/src/Ryujinx.UI.Common/Configuration/AudioBackend.cs +++ b/src/Ryujinx/Utilities/Configuration/AudioBackend.cs @@ -1,7 +1,7 @@ using Ryujinx.Common.Utilities; using System.Text.Json.Serialization; -namespace Ryujinx.UI.Common.Configuration +namespace Ryujinx.Ava.Utilities.Configuration { [JsonConverter(typeof(TypedStringEnumConverter))] public enum AudioBackend diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs similarity index 98% rename from src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs rename to src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs index 8b123be01..540024cbd 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs @@ -1,3 +1,5 @@ +using Ryujinx.Ava.Utilities.Configuration.System; +using Ryujinx.Ava.Utilities.Configuration.UI; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; @@ -5,19 +7,17 @@ using Ryujinx.Common.Configuration.Multiplayer; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; using Ryujinx.HLE; -using Ryujinx.UI.Common.Configuration.System; -using Ryujinx.UI.Common.Configuration.UI; using System.Collections.Generic; using System.Text.Json.Nodes; -namespace Ryujinx.UI.Common.Configuration +namespace Ryujinx.Ava.Utilities.Configuration { public class ConfigurationFileFormat { /// /// The current version of the file format /// - public const int CurrentVersion = 58; + public const int CurrentVersion = 59; /// /// Version of the configuration file format @@ -436,9 +436,9 @@ namespace Ryujinx.UI.Common.Configuration public bool ShowDirtyHacks { get; set; } /// - /// The packed value of the enabled dirty hacks. + /// The packed values of the enabled dirty hacks. /// - public int EnabledDirtyHacks { get; set; } + public ulong[] DirtyHacks { get; set; } /// /// Loads a configuration file from disk diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormatSettings.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormatSettings.cs similarity index 83% rename from src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormatSettings.cs rename to src/Ryujinx/Utilities/Configuration/ConfigurationFileFormatSettings.cs index 9861ebf1f..175d4dee8 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormatSettings.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormatSettings.cs @@ -1,6 +1,6 @@ using Ryujinx.Common.Utilities; -namespace Ryujinx.UI.Common.Configuration +namespace Ryujinx.Ava.Utilities.Configuration { internal static class ConfigurationFileFormatSettings { diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationJsonSerializerContext.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationJsonSerializerContext.cs similarity index 74% rename from src/Ryujinx.UI.Common/Configuration/ConfigurationJsonSerializerContext.cs rename to src/Ryujinx/Utilities/Configuration/ConfigurationJsonSerializerContext.cs index 3c3e3f20d..a81e00f4a 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationJsonSerializerContext.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationJsonSerializerContext.cs @@ -1,10 +1,8 @@ using System.Text.Json.Serialization; -namespace Ryujinx.UI.Common.Configuration +namespace Ryujinx.Ava.Utilities.Configuration { [JsonSourceGenerationOptions(WriteIndented = true)] [JsonSerializable(typeof(ConfigurationFileFormat))] - internal partial class ConfigurationJsonSerializerContext : JsonSerializerContext - { - } + internal partial class ConfigurationJsonSerializerContext : JsonSerializerContext; } diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs similarity index 96% rename from src/Ryujinx.UI.Common/Configuration/ConfigurationState.Migration.cs rename to src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs index 8652b4331..210132117 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.Migration.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs @@ -1,3 +1,5 @@ +using Ryujinx.Ava.Utilities.Configuration.System; +using Ryujinx.Ava.Utilities.Configuration.UI; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; @@ -5,12 +7,11 @@ using Ryujinx.Common.Configuration.Hid.Keyboard; using Ryujinx.Common.Configuration.Multiplayer; using Ryujinx.Common.Logging; using Ryujinx.HLE; -using Ryujinx.UI.Common.Configuration.System; -using Ryujinx.UI.Common.Configuration.UI; using System; using System.Collections.Generic; +using System.Linq; -namespace Ryujinx.UI.Common.Configuration +namespace Ryujinx.Ava.Utilities.Configuration { public partial class ConfigurationState { @@ -637,6 +638,18 @@ namespace Ryujinx.UI.Common.Configuration configurationFileUpdated = true; } + + // 58 migration accidentally got skipped but it worked with no issues somehow lol + + if (configurationFileFormat.Version < 59) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 59."); + + configurationFileFormat.ShowDirtyHacks = false; + configurationFileFormat.DirtyHacks = []; + + configurationFileUpdated = true; + } Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Graphics.ResScale.Value = configurationFileFormat.ResScale; @@ -737,7 +750,17 @@ namespace Ryujinx.UI.Common.Configuration Multiplayer.LdnServer.Value = configurationFileFormat.LdnServer; Hacks.ShowDirtyHacks.Value = configurationFileFormat.ShowDirtyHacks; - Hacks.Xc2MenuSoftlockFix.Value = ((DirtyHacks)configurationFileFormat.EnabledDirtyHacks).HasFlag(DirtyHacks.Xc2MenuSoftlockFix); + + { + EnabledDirtyHack[] hacks = (configurationFileFormat.DirtyHacks ?? []).Select(EnabledDirtyHack.Unpack).ToArray(); + + Hacks.Xc2MenuSoftlockFix.Value = hacks.Any(it => it.Hack == DirtyHacks.Xc2MenuSoftlockFix); + + var shaderCompilationThreadSleep = hacks.FirstOrDefault(it => + it.Hack == DirtyHacks.ShaderCompilationThreadSleep); + Hacks.EnableShaderTranslationDelay.Value = shaderCompilationThreadSleep != null; + Hacks.ShaderTranslationDelay.Value = shaderCompilationThreadSleep?.Value ?? 0; + } if (configurationFileUpdated) { diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.Model.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs similarity index 95% rename from src/Ryujinx.UI.Common/Configuration/ConfigurationState.Model.cs rename to src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs index 2ae56d50a..2a91bf65b 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.Model.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs @@ -1,16 +1,18 @@ using ARMeilleure; using Gommon; +using Ryujinx.Ava.Utilities.Configuration.System; 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.HLE; -using Ryujinx.UI.Common.Configuration.System; -using Ryujinx.UI.Common.Helper; using System.Collections.Generic; +using System.Linq; +using RyuLogger = Ryujinx.Common.Logging.Logger; -namespace Ryujinx.UI.Common.Configuration +namespace Ryujinx.Ava.Utilities.Configuration { public partial class ConfigurationState { @@ -626,36 +628,54 @@ namespace Ryujinx.UI.Common.Configuration public ReactiveObject ShowDirtyHacks { get; private set; } public ReactiveObject Xc2MenuSoftlockFix { get; private set; } + + public ReactiveObject EnableShaderTranslationDelay { get; private set; } + + public ReactiveObject ShaderTranslationDelay { get; private set; } public HacksSection() { ShowDirtyHacks = new ReactiveObject(); Xc2MenuSoftlockFix = new ReactiveObject(); Xc2MenuSoftlockFix.Event += HackChanged; + EnableShaderTranslationDelay = new ReactiveObject(); + EnableShaderTranslationDelay.Event += HackChanged; + ShaderTranslationDelay = new ReactiveObject(); } private void HackChanged(object sender, ReactiveEventArgs rxe) { - Ryujinx.Common.Logging.Logger.Info?.Print(LogClass.Configuration, $"EnabledDirtyHacks set to: {EnabledHacks}", "LogValueChange"); + var newHacks = EnabledHacks.Select(x => x.Hack) + .JoinToString(", "); + + if (newHacks != _lastHackCollection) + { + RyuLogger.Info?.Print(LogClass.Configuration, + $"EnabledDirtyHacks set to: [{newHacks}]", "LogValueChange"); + + _lastHackCollection = newHacks; + } } - public DirtyHacks EnabledHacks + private static string _lastHackCollection; + + public EnabledDirtyHack[] EnabledHacks { get { - DirtyHacks dirtyHacks = DirtyHacks.None; + List enabledHacks = []; if (Xc2MenuSoftlockFix) Apply(DirtyHacks.Xc2MenuSoftlockFix); - return dirtyHacks; + if (EnableShaderTranslationDelay) + Apply(DirtyHacks.ShaderCompilationThreadSleep, ShaderTranslationDelay); + + return enabledHacks.ToArray(); - void Apply(DirtyHacks hack) + void Apply(DirtyHacks hack, int value = 0) { - if (dirtyHacks is not DirtyHacks.None) - dirtyHacks |= hack; - else - dirtyHacks = hack; + enabledHacks.Add(new EnabledDirtyHack(hack, value)); } } } diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs similarity index 98% rename from src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs rename to src/Ryujinx/Utilities/Configuration/ConfigurationState.cs index 8ae76ecc5..95ec62e83 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs @@ -1,14 +1,15 @@ +using Ryujinx.Ava.Utilities.Configuration.System; +using Ryujinx.Ava.Utilities.Configuration.UI; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Keyboard; using Ryujinx.Common.Configuration.Multiplayer; using Ryujinx.Graphics.Vulkan; using Ryujinx.HLE; -using Ryujinx.UI.Common.Configuration.System; -using Ryujinx.UI.Common.Configuration.UI; using System; +using System.Linq; -namespace Ryujinx.UI.Common.Configuration +namespace Ryujinx.Ava.Utilities.Configuration { public partial class ConfigurationState { @@ -139,7 +140,7 @@ namespace Ryujinx.UI.Common.Configuration MultiplayerLdnPassphrase = Multiplayer.LdnPassphrase, LdnServer = Multiplayer.LdnServer, ShowDirtyHacks = Hacks.ShowDirtyHacks, - EnabledDirtyHacks = (int)Hacks.EnabledHacks, + DirtyHacks = Hacks.EnabledHacks.Select(it => it.Pack()).ToArray(), }; return configurationFile; diff --git a/src/Ryujinx.UI.Common/Extensions/FileTypeExtensions.cs b/src/Ryujinx/Utilities/Configuration/FileTypes.cs similarity index 73% rename from src/Ryujinx.UI.Common/Extensions/FileTypeExtensions.cs rename to src/Ryujinx/Utilities/Configuration/FileTypes.cs index 7e71ba7a4..c4550b5a6 100644 --- a/src/Ryujinx.UI.Common/Extensions/FileTypeExtensions.cs +++ b/src/Ryujinx/Utilities/Configuration/FileTypes.cs @@ -1,8 +1,19 @@ using System; -using static Ryujinx.UI.Common.Configuration.ConfigurationState.UISection; -namespace Ryujinx.UI.Common +using static Ryujinx.Ava.Utilities.Configuration.ConfigurationState.UISection; + +namespace Ryujinx.Ava.Utilities.Configuration { + public enum FileTypes + { + NSP, + PFS0, + XCI, + NCA, + NRO, + NSO + } + public static class FileTypesExtensions { /// @@ -10,7 +21,7 @@ namespace Ryujinx.UI.Common /// /// The name of the parameter to get the value of. /// The config instance to get the value from. - /// The current value of the setting. Value is if the file type is the be shown on the games list, otherwise. + /// The current value of the setting. Value is if the file type is to be shown on the games list, otherwise. public static bool GetConfigValue(this FileTypes type, ShownFileTypeSettings config) => type switch { FileTypes.NSP => config.NSP.Value, diff --git a/src/Ryujinx.UI.Common/Configuration/LoggerModule.cs b/src/Ryujinx/Utilities/Configuration/LoggerModule.cs similarity index 98% rename from src/Ryujinx.UI.Common/Configuration/LoggerModule.cs rename to src/Ryujinx/Utilities/Configuration/LoggerModule.cs index a7913f142..663ad607f 100644 --- a/src/Ryujinx.UI.Common/Configuration/LoggerModule.cs +++ b/src/Ryujinx/Utilities/Configuration/LoggerModule.cs @@ -4,7 +4,7 @@ using Ryujinx.Common.Logging.Targets; using System; using System.IO; -namespace Ryujinx.UI.Common.Configuration +namespace Ryujinx.Ava.Utilities.Configuration { public static class LoggerModule { diff --git a/src/Ryujinx.UI.Common/Configuration/System/Language.cs b/src/Ryujinx/Utilities/Configuration/System/Language.cs similarity index 86% rename from src/Ryujinx.UI.Common/Configuration/System/Language.cs rename to src/Ryujinx/Utilities/Configuration/System/Language.cs index 8ca4e542b..81a9bd192 100644 --- a/src/Ryujinx.UI.Common/Configuration/System/Language.cs +++ b/src/Ryujinx/Utilities/Configuration/System/Language.cs @@ -1,8 +1,7 @@ using Ryujinx.Common.Utilities; -using Ryujinx.HLE.HOS.SystemState; using System.Text.Json.Serialization; -namespace Ryujinx.UI.Common.Configuration.System +namespace Ryujinx.Ava.Utilities.Configuration.System { [JsonConverter(typeof(TypedStringEnumConverter))] public enum Language diff --git a/src/Ryujinx.UI.Common/Configuration/System/Region.cs b/src/Ryujinx/Utilities/Configuration/System/Region.cs similarity index 84% rename from src/Ryujinx.UI.Common/Configuration/System/Region.cs rename to src/Ryujinx/Utilities/Configuration/System/Region.cs index 6087c70e5..ff3352e6a 100644 --- a/src/Ryujinx.UI.Common/Configuration/System/Region.cs +++ b/src/Ryujinx/Utilities/Configuration/System/Region.cs @@ -1,7 +1,7 @@ using Ryujinx.Common.Utilities; using System.Text.Json.Serialization; -namespace Ryujinx.UI.Common.Configuration.System +namespace Ryujinx.Ava.Utilities.Configuration.System { [JsonConverter(typeof(TypedStringEnumConverter))] public enum Region diff --git a/src/Ryujinx.UI.Common/Configuration/UI/ColumnSort.cs b/src/Ryujinx/Utilities/Configuration/UI/ColumnSort.cs similarity index 73% rename from src/Ryujinx.UI.Common/Configuration/UI/ColumnSort.cs rename to src/Ryujinx/Utilities/Configuration/UI/ColumnSort.cs index 44e98c407..e74ca0ec5 100644 --- a/src/Ryujinx.UI.Common/Configuration/UI/ColumnSort.cs +++ b/src/Ryujinx/Utilities/Configuration/UI/ColumnSort.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.UI.Common.Configuration.UI +namespace Ryujinx.Ava.Utilities.Configuration.UI { public struct ColumnSort { diff --git a/src/Ryujinx.UI.Common/Configuration/UI/GuiColumns.cs b/src/Ryujinx/Utilities/Configuration/UI/GuiColumns.cs similarity index 91% rename from src/Ryujinx.UI.Common/Configuration/UI/GuiColumns.cs rename to src/Ryujinx/Utilities/Configuration/UI/GuiColumns.cs index c486492e0..0ab9885fe 100644 --- a/src/Ryujinx.UI.Common/Configuration/UI/GuiColumns.cs +++ b/src/Ryujinx/Utilities/Configuration/UI/GuiColumns.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.UI.Common.Configuration.UI +namespace Ryujinx.Ava.Utilities.Configuration.UI { public struct GuiColumns { diff --git a/src/Ryujinx.UI.Common/Configuration/UI/ShownFileTypes.cs b/src/Ryujinx/Utilities/Configuration/UI/ShownFileTypes.cs similarity index 85% rename from src/Ryujinx.UI.Common/Configuration/UI/ShownFileTypes.cs rename to src/Ryujinx/Utilities/Configuration/UI/ShownFileTypes.cs index 6c72a6930..9541b4885 100644 --- a/src/Ryujinx.UI.Common/Configuration/UI/ShownFileTypes.cs +++ b/src/Ryujinx/Utilities/Configuration/UI/ShownFileTypes.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.UI.Common.Configuration.UI +namespace Ryujinx.Ava.Utilities.Configuration.UI { public struct ShownFileTypes { diff --git a/src/Ryujinx.UI.Common/Configuration/UI/WindowStartup.cs b/src/Ryujinx/Utilities/Configuration/UI/WindowStartup.cs similarity index 85% rename from src/Ryujinx.UI.Common/Configuration/UI/WindowStartup.cs rename to src/Ryujinx/Utilities/Configuration/UI/WindowStartup.cs index 0df459134..6c5e36879 100644 --- a/src/Ryujinx.UI.Common/Configuration/UI/WindowStartup.cs +++ b/src/Ryujinx/Utilities/Configuration/UI/WindowStartup.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.UI.Common.Configuration.UI +namespace Ryujinx.Ava.Utilities.Configuration.UI { public struct WindowStartup { diff --git a/src/Ryujinx.UI.Common/Helper/DownloadableContentsHelper.cs b/src/Ryujinx/Utilities/DownloadableContentsHelper.cs similarity index 98% rename from src/Ryujinx.UI.Common/Helper/DownloadableContentsHelper.cs rename to src/Ryujinx/Utilities/DownloadableContentsHelper.cs index 020529b55..b6d2420a3 100644 --- a/src/Ryujinx.UI.Common/Helper/DownloadableContentsHelper.cs +++ b/src/Ryujinx/Utilities/DownloadableContentsHelper.cs @@ -3,18 +3,18 @@ using LibHac.Fs; using LibHac.Fs.Fsa; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Ava.Common.Models; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.Utilities; -using Ryujinx.UI.Common.Models; using System; using System.Collections.Generic; using System.IO; using Path = System.IO.Path; -namespace Ryujinx.UI.Common.Helper +namespace Ryujinx.Ava.Utilities { public static class DownloadableContentsHelper { diff --git a/src/Ryujinx.UI.Common/Helper/SetupValidator.cs b/src/Ryujinx/Utilities/SetupValidator.cs similarity index 98% rename from src/Ryujinx.UI.Common/Helper/SetupValidator.cs rename to src/Ryujinx/Utilities/SetupValidator.cs index 45d9f8f0d..0bd3a348d 100644 --- a/src/Ryujinx.UI.Common/Helper/SetupValidator.cs +++ b/src/Ryujinx/Utilities/SetupValidator.cs @@ -1,9 +1,10 @@ using Ryujinx.Common.Logging; +using Ryujinx.Common.UI; using Ryujinx.HLE.FileSystem; using System; using System.IO; -namespace Ryujinx.UI.Common.Helper +namespace Ryujinx.Ava.Utilities { /// /// Ensure installation validity diff --git a/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs b/src/Ryujinx/Utilities/ShortcutHelper.cs similarity index 97% rename from src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs rename to src/Ryujinx/Utilities/ShortcutHelper.cs index 8c006a227..fed6a5c46 100644 --- a/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs +++ b/src/Ryujinx/Utilities/ShortcutHelper.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.IO; using System.Runtime.Versioning; -namespace Ryujinx.UI.Common.Helper +namespace Ryujinx.Ava.Utilities { public static class ShortcutHelper { @@ -31,7 +31,7 @@ namespace Ryujinx.UI.Common.Helper private static void CreateShortcutLinux(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string desktopPath, string cleanedAppName) { string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx.sh"); - var desktopFile = EmbeddedResources.ReadAllText("Ryujinx.UI.Common/shortcut-template.desktop"); + var desktopFile = EmbeddedResources.ReadAllText("Ryujinx/Assets/ShortcutFiles/shortcut-template.desktop"); iconPath += ".png"; var image = SKBitmap.Decode(iconData); @@ -47,8 +47,8 @@ namespace Ryujinx.UI.Common.Helper private static void CreateShortcutMacos(string appFilePath, string applicationId, byte[] iconData, string desktopPath, string cleanedAppName) { string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx"); - var plistFile = EmbeddedResources.ReadAllText("Ryujinx.UI.Common/shortcut-template.plist"); - var shortcutScript = EmbeddedResources.ReadAllText("Ryujinx.UI.Common/shortcut-launch-script.sh"); + var plistFile = EmbeddedResources.ReadAllText("Ryujinx/Assets/ShortcutFiles/shortcut-template.plist"); + var shortcutScript = EmbeddedResources.ReadAllText("Ryujinx/Assets/ShortcutFiles/shortcut-launch-script.sh"); // Macos .App folder string contentFolderPath = Path.Combine("/Applications", cleanedAppName + ".app", "Contents"); string scriptFolderPath = Path.Combine(contentFolderPath, "MacOS"); diff --git a/src/Ryujinx.UI.Common/SystemInfo/LinuxSystemInfo.cs b/src/Ryujinx/Utilities/SystemInfo/LinuxSystemInfo.cs similarity index 98% rename from src/Ryujinx.UI.Common/SystemInfo/LinuxSystemInfo.cs rename to src/Ryujinx/Utilities/SystemInfo/LinuxSystemInfo.cs index c7fe05a09..6ca38aa36 100644 --- a/src/Ryujinx.UI.Common/SystemInfo/LinuxSystemInfo.cs +++ b/src/Ryujinx/Utilities/SystemInfo/LinuxSystemInfo.cs @@ -5,7 +5,7 @@ using System.Globalization; using System.IO; using System.Runtime.Versioning; -namespace Ryujinx.UI.Common.SystemInfo +namespace Ryujinx.Ava.Utilities.SystemInfo { [SupportedOSPlatform("linux")] class LinuxSystemInfo : SystemInfo diff --git a/src/Ryujinx.UI.Common/SystemInfo/MacOSSystemInfo.cs b/src/Ryujinx/Utilities/SystemInfo/MacOSSystemInfo.cs similarity index 99% rename from src/Ryujinx.UI.Common/SystemInfo/MacOSSystemInfo.cs rename to src/Ryujinx/Utilities/SystemInfo/MacOSSystemInfo.cs index 894c3cadc..6b0beacf8 100644 --- a/src/Ryujinx.UI.Common/SystemInfo/MacOSSystemInfo.cs +++ b/src/Ryujinx/Utilities/SystemInfo/MacOSSystemInfo.cs @@ -5,7 +5,7 @@ using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; -namespace Ryujinx.UI.Common.SystemInfo +namespace Ryujinx.Ava.Utilities.SystemInfo { [SupportedOSPlatform("macos")] partial class MacOSSystemInfo : SystemInfo diff --git a/src/Ryujinx.UI.Common/SystemInfo/SystemInfo.cs b/src/Ryujinx/Utilities/SystemInfo/SystemInfo.cs similarity index 97% rename from src/Ryujinx.UI.Common/SystemInfo/SystemInfo.cs rename to src/Ryujinx/Utilities/SystemInfo/SystemInfo.cs index 2dfa9160d..0d45c52f2 100644 --- a/src/Ryujinx.UI.Common/SystemInfo/SystemInfo.cs +++ b/src/Ryujinx/Utilities/SystemInfo/SystemInfo.cs @@ -1,11 +1,10 @@ using Ryujinx.Common.Logging; -using Ryujinx.UI.Common.Helper; using System; using System.Runtime.InteropServices; using System.Runtime.Intrinsics.X86; using System.Text; -namespace Ryujinx.UI.Common.SystemInfo +namespace Ryujinx.Ava.Utilities.SystemInfo { public class SystemInfo { diff --git a/src/Ryujinx.UI.Common/SystemInfo/WindowsSystemInfo.cs b/src/Ryujinx/Utilities/SystemInfo/WindowsSystemInfo.cs similarity index 98% rename from src/Ryujinx.UI.Common/SystemInfo/WindowsSystemInfo.cs rename to src/Ryujinx/Utilities/SystemInfo/WindowsSystemInfo.cs index 4a2c8795d..73845be11 100644 --- a/src/Ryujinx.UI.Common/SystemInfo/WindowsSystemInfo.cs +++ b/src/Ryujinx/Utilities/SystemInfo/WindowsSystemInfo.cs @@ -4,7 +4,7 @@ using System.Management; using System.Runtime.InteropServices; using System.Runtime.Versioning; -namespace Ryujinx.UI.Common.SystemInfo +namespace Ryujinx.Ava.Utilities.SystemInfo { [SupportedOSPlatform("windows")] partial class WindowsSystemInfo : SystemInfo diff --git a/src/Ryujinx.UI.Common/Helper/TitleHelper.cs b/src/Ryujinx/Utilities/TitleHelper.cs similarity index 95% rename from src/Ryujinx.UI.Common/Helper/TitleHelper.cs rename to src/Ryujinx/Utilities/TitleHelper.cs index 9d73aea75..be7a87f82 100644 --- a/src/Ryujinx.UI.Common/Helper/TitleHelper.cs +++ b/src/Ryujinx/Utilities/TitleHelper.cs @@ -1,7 +1,6 @@ using Ryujinx.HLE.Loaders.Processes; -using System; -namespace Ryujinx.UI.Common.Helper +namespace Ryujinx.Ava.Utilities { public static class TitleHelper { diff --git a/src/Ryujinx.UI.Common/Helper/TitleUpdatesHelper.cs b/src/Ryujinx/Utilities/TitleUpdatesHelper.cs similarity index 98% rename from src/Ryujinx.UI.Common/Helper/TitleUpdatesHelper.cs rename to src/Ryujinx/Utilities/TitleUpdatesHelper.cs index 36de8b31a..9fc9bbf6b 100644 --- a/src/Ryujinx.UI.Common/Helper/TitleUpdatesHelper.cs +++ b/src/Ryujinx/Utilities/TitleUpdatesHelper.cs @@ -6,14 +6,14 @@ using LibHac.Ncm; using LibHac.Ns; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Ava.Common.Models; +using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.Loaders.Processes.Extensions; using Ryujinx.HLE.Utilities; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Models; using System; using System.Collections.Generic; using System.IO; @@ -22,7 +22,7 @@ using Path = System.IO.Path; using SpanHelpers = LibHac.Common.SpanHelpers; using TitleUpdateMetadata = Ryujinx.Common.Configuration.TitleUpdateMetadata; -namespace Ryujinx.UI.Common.Helper +namespace Ryujinx.Ava.Utilities { public static class TitleUpdatesHelper { diff --git a/src/Ryujinx.UI.Common/Helper/ValueFormatUtils.cs b/src/Ryujinx/Utilities/ValueFormatUtils.cs similarity index 97% rename from src/Ryujinx.UI.Common/Helper/ValueFormatUtils.cs rename to src/Ryujinx/Utilities/ValueFormatUtils.cs index c203834f5..944cfbf8a 100644 --- a/src/Ryujinx.UI.Common/Helper/ValueFormatUtils.cs +++ b/src/Ryujinx/Utilities/ValueFormatUtils.cs @@ -1,8 +1,9 @@ +using Ryujinx.Ava.Common.Locale; using System; using System.Globalization; using System.Linq; -namespace Ryujinx.UI.Common.Helper +namespace Ryujinx.Ava.Utilities { public static class ValueFormatUtils { @@ -75,7 +76,7 @@ namespace Ryujinx.UI.Common.Helper { culture ??= CultureInfo.CurrentCulture; - return utcDateTime?.ToLocalTime().ToString(culture); + return utcDateTime?.ToLocalTime().ToString(culture) ?? LocaleManager.Instance[LocaleKeys.Never]; } ///