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 new file mode 100644 index 000000000..1015e95d1 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/DirtyHacks.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Ryujinx.Common.Configuration +{ + [Flags] + public enum DirtyHacks : byte + { + 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.Common/Extensions/StreamExtensions.cs b/src/Ryujinx.Common/Extensions/StreamExtensions.cs index 4b02781c9..59ff44edc 100644 --- a/src/Ryujinx.Common/Extensions/StreamExtensions.cs +++ b/src/Ryujinx.Common/Extensions/StreamExtensions.cs @@ -8,10 +8,10 @@ namespace Ryujinx.Common public static class StreamExtensions { /// - /// Writes a to this stream. + /// Writes an int span to this stream. /// /// This default implementation converts each buffer value to a stack-allocated - /// byte array, then writes it to the Stream using . + /// byte array, then writes it to the Stream using . /// /// The stream to be written to /// The buffer of values to be written 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.Common/TitleIDs.cs b/src/Ryujinx.Common/TitleIDs.cs index b75ee1299..31d895051 100644 --- a/src/Ryujinx.Common/TitleIDs.cs +++ b/src/Ryujinx.Common/TitleIDs.cs @@ -8,6 +8,8 @@ namespace Ryujinx.Common { public static class TitleIDs { + public static ReactiveObject> CurrentApplication { get; set; } = new(); + public static GraphicsBackend SelectGraphicsBackend(string titleId, GraphicsBackend currentBackend) { switch (currentBackend) @@ -33,6 +35,7 @@ namespace Ryujinx.Common "010028600EBDA000", // Mario 3D World "0100152000022000", // Mario Kart 8 Deluxe "01005CA01580E000", // Persona 5 + "01001f5010dfa000", // Pokemon Legends Arceus "01008C0016544000", // Sea of Stars "01006A800016E000", // Smash Ultimate "0100000000010000", // Super Mario Odyessy 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/GraphicsConfig.cs b/src/Ryujinx.Graphics.Gpu/GraphicsConfig.cs index fbb7399ca..066ac28f7 100644 --- a/src/Ryujinx.Graphics.Gpu/GraphicsConfig.cs +++ b/src/Ryujinx.Graphics.Gpu/GraphicsConfig.cs @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Gpu /// Enables or disables high-level emulation of common GPU Macro code. /// public static bool EnableMacroHLE = true; - + /// /// Title id of the current running game. /// Used by the shader cache. 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.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 0924c60f8..3a02eb615 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; diff --git a/src/Ryujinx.Graphics.Metal/Window.cs b/src/Ryujinx.Graphics.Metal/Window.cs index 7d9a51a9e..1823c0b9a 100644 --- a/src/Ryujinx.Graphics.Metal/Window.cs +++ b/src/Ryujinx.Graphics.Metal/Window.cs @@ -22,13 +22,15 @@ namespace Ryujinx.Graphics.Metal private int _requestedWidth; private int _requestedHeight; - - // private bool _vsyncEnabled; + private AntiAliasing _currentAntiAliasing; private bool _updateEffect; private IPostProcessingEffect _effect; private IScalingFilter _scalingFilter; private bool _isLinear; + + public bool IsVSyncEnabled => _metalLayer.DisplaySyncEnabled; + // private float _scalingFilterLevel; private bool _updateScalingFilter; private ScalingFilter _currentScalingFilter; @@ -40,7 +42,7 @@ namespace Ryujinx.Graphics.Metal _metalLayer = metalLayer; } - private unsafe void ResizeIfNeeded() + private void ResizeIfNeeded() { if (_requestedWidth != 0 && _requestedHeight != 0) { @@ -54,7 +56,7 @@ namespace Ryujinx.Graphics.Metal } } - public unsafe void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) + public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) { if (_renderer.Pipeline is Pipeline pipeline && texture is Texture tex) { @@ -141,15 +143,7 @@ namespace Ryujinx.Graphics.Metal public void ChangeVSyncMode(VSyncMode vSyncMode) { - switch (vSyncMode) - { - case VSyncMode.Unbounded: - _metalLayer.DisplaySyncEnabled = false; - break; - case VSyncMode.Switch: - _metalLayer.DisplaySyncEnabled = true; - break; - } + _metalLayer.DisplaySyncEnabled = vSyncMode is VSyncMode.Switch; } public void SetAntiAliasing(AntiAliasing effect) diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/BitDepth.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/BitDepth.cs index a43c83580..fdf0aff9c 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/BitDepth.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/BitDepth.cs @@ -2,8 +2,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { internal enum BitDepth { - Bits8 = 8, /**< 8 bits */ - Bits10 = 10, /**< 10 bits */ - Bits12 = 12, /**< 12 bits */ + Bits8 = 8, // < 8 bits + Bits10 = 10, // < 10 bits + Bits12 = 12, // < 12 bits } } diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index ad4b18e50..a4fcf5353 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -1,3 +1,4 @@ +using Gommon; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; @@ -890,7 +891,12 @@ namespace Ryujinx.Graphics.Vulkan private void PrintGpuInformation() { - Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})"); + string gpuInfoMessage = $"{GpuRenderer} ({GpuVersion})"; + if (!GpuRenderer.StartsWithIgnoreCase(GpuVendor)) + gpuInfoMessage = gpuInfoMessage.Prepend(GpuVendor); + + Logger.Notice.Print(LogClass.Gpu, gpuInfoMessage); + Logger.Notice.Print(LogClass.Gpu, $"GPU Memory: {GetTotalGPUMemory() / (1024 * 1024)} MiB"); } diff --git a/src/Ryujinx.HLE/HLEConfiguration.cs b/src/Ryujinx.HLE/HLEConfiguration.cs index 52c2b3da4..8ac76508f 100644 --- a/src/Ryujinx.HLE/HLEConfiguration.cs +++ b/src/Ryujinx.HLE/HLEConfiguration.cs @@ -188,6 +188,11 @@ namespace Ryujinx.HLE /// An action called when HLE force a refresh of output after docked mode changed. /// public Action RefreshInputConfig { internal get; set; } + + /// + /// The desired hacky workarounds. + /// + public EnabledDirtyHack[] Hacks { internal get; set; } public HLEConfiguration(VirtualFileSystem virtualFileSystem, LibHacHorizonManager libHacHorizonManager, @@ -218,7 +223,8 @@ namespace Ryujinx.HLE bool multiplayerDisableP2p, string multiplayerLdnPassphrase, string multiplayerLdnServer, - int customVSyncInterval) + int customVSyncInterval, + EnabledDirtyHack[] dirtyHacks = null) { VirtualFileSystem = virtualFileSystem; LibHacHorizonManager = libHacHorizonManager; @@ -250,6 +256,7 @@ namespace Ryujinx.HLE MultiplayerDisableP2p = multiplayerDisableP2p; MultiplayerLdnPassphrase = multiplayerLdnPassphrase; MultiplayerLdnServer = multiplayerLdnServer; + 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 4299a6c74..ac5dc04e9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs @@ -1,6 +1,9 @@ using LibHac; using LibHac.Common; using LibHac.Sf; +using Ryujinx.Common; +using Ryujinx.Common.Configuration; +using System.Threading; namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy { @@ -13,6 +16,8 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy _baseStorage = SharedRef.CreateMove(ref baseStorage); } + private const string Xc2TitleId = "0100e95004038000"; + [CommandCmif(0)] // Read(u64 offset, u64 length) -> buffer buffer public ResultCode Read(ServiceCtx context) @@ -33,6 +38,13 @@ 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.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 + Thread.Sleep(2); + } return (ResultCode)result.Value; } 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/Loaders/Processes/Extensions/FileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs index 97284f3bb..d1d13b00f 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs @@ -4,8 +4,10 @@ using LibHac.Fs.Fsa; using LibHac.Loader; using LibHac.Ns; using LibHac.Tools.FsSystem; +using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; +using Ryujinx.Graphics.Gpu; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.Memory; using System; @@ -102,7 +104,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions } // Initialize GPU. - Graphics.Gpu.GraphicsConfig.TitleId = programId.ToString("X16"); + GraphicsConfig.TitleId = programId.ToString("X16"); device.Gpu.HostInitalized.Set(); if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible)) diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs index fe8360f04..ebbeb1398 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs @@ -6,7 +6,9 @@ using LibHac.Ns; using LibHac.Tools.Fs; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Common; using Ryujinx.Common.Logging; +using Ryujinx.Graphics.Gpu; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Loaders.Processes.Extensions; using System; @@ -59,6 +61,8 @@ namespace Ryujinx.HLE.Loaders.Processes { _latestPid = processResult.ProcessId; + TitleIDs.CurrentApplication.Value = processResult.ProgramIdText; + return true; } } @@ -86,6 +90,8 @@ namespace Ryujinx.HLE.Loaders.Processes { _latestPid = processResult.ProcessId; + TitleIDs.CurrentApplication.Value = processResult.ProgramIdText; + return true; } } @@ -113,6 +119,8 @@ namespace Ryujinx.HLE.Loaders.Processes if (processResult.ProgramId > 0x01000000000007FF) { _latestPid = processResult.ProcessId; + + TitleIDs.CurrentApplication.Value = processResult.ProgramIdText; } return true; @@ -132,6 +140,8 @@ namespace Ryujinx.HLE.Loaders.Processes { _latestPid = processResult.ProcessId; + TitleIDs.CurrentApplication.Value = processResult.ProgramIdText; + return true; } } @@ -183,14 +193,17 @@ namespace Ryujinx.HLE.Loaders.Processes if (nacpData.Value.PresenceGroupId != 0) { programId = nacpData.Value.PresenceGroupId; + TitleIDs.CurrentApplication.Value = programId.ToString("X16"); } else if (nacpData.Value.SaveDataOwnerId != 0) { programId = nacpData.Value.SaveDataOwnerId; + TitleIDs.CurrentApplication.Value = programId.ToString("X16"); } else if (nacpData.Value.AddOnContentBaseId != 0) { programId = nacpData.Value.AddOnContentBaseId - 0x1000; + TitleIDs.CurrentApplication.Value = programId.ToString("X16"); } } @@ -204,7 +217,7 @@ namespace Ryujinx.HLE.Loaders.Processes } // Explicitly null TitleId to disable the shader cache. - Graphics.Gpu.GraphicsConfig.TitleId = null; + GraphicsConfig.TitleId = null; _device.Gpu.HostInitalized.Set(); ProcessResult processResult = ProcessLoaderHelper.LoadNsos(_device, diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs index d0afdf173..127e532e2 100644 --- a/src/Ryujinx.HLE/Switch.cs +++ b/src/Ryujinx.HLE/Switch.cs @@ -2,6 +2,7 @@ using LibHac.Common; using LibHac.Ns; using Ryujinx.Audio.Backends.CompatLayer; using Ryujinx.Audio.Integration; +using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Graphics.Gpu; using Ryujinx.HLE.FileSystem; @@ -17,6 +18,8 @@ namespace Ryujinx.HLE { public class Switch : IDisposable { + public static Switch Shared { get; private set; } + public HLEConfiguration Configuration { get; } public IHardwareDeviceDriver AudioDeviceDriver { get; } public MemoryBlock Memory { get; } @@ -37,6 +40,8 @@ namespace Ryujinx.HLE public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable; + public DirtyHackCollection DirtyHacks { get; } + public Switch(HLEConfiguration configuration) { ArgumentNullException.ThrowIfNull(configuration.GpuRenderer); @@ -52,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); @@ -72,8 +78,11 @@ namespace Ryujinx.HLE System.EnablePtc = Configuration.EnablePtc; System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel; System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode; + UpdateVSyncInterval(); #pragma warning restore IDE0055 + + Shared = this; } public void ProcessFrame() @@ -142,6 +151,9 @@ namespace Ryujinx.HLE AudioDeviceDriver.Dispose(); FileSystem.Dispose(); Memory.Dispose(); + + TitleIDs.CurrentApplication.Value = null; + Shared = null; } } } 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 909eb05d5..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; @@ -311,6 +311,7 @@ namespace Ryujinx.Ava Device.VSyncMode = e.NewValue; Device.UpdateVSyncInterval(); } + _renderer.Window?.ChangeVSyncMode(e.NewValue); _viewModel.ShowCustomVSyncIntervalPicker = (e.NewValue == VSyncMode.Custom); @@ -577,7 +578,6 @@ namespace Ryujinx.Ava public void Stop() { _isActive = false; - DiscordIntegrationModule.SwitchToMainState(); } private void Exit() @@ -861,13 +861,11 @@ namespace Ryujinx.Ava return false; } - - ApplicationMetadata appMeta = ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, + + ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, appMetadata => appMetadata.UpdatePreGame() ); - DiscordIntegrationModule.SwitchToPlayingState(appMeta, Device.Processes.ActiveApplication); - return true; } @@ -923,7 +921,7 @@ namespace Ryujinx.Ava // Initialize Configuration. var memoryConfiguration = ConfigurationState.Instance.System.DramSize.Value; - Device = new HLE.Switch(new HLEConfiguration( + Device = new Switch(new HLEConfiguration( VirtualFileSystem, _viewModel.LibHacHorizonManager, ContentManager, @@ -953,7 +951,8 @@ namespace Ryujinx.Ava ConfigurationState.Instance.Multiplayer.DisableP2p, ConfigurationState.Instance.Multiplayer.LdnPassphrase, ConfigurationState.Instance.Multiplayer.LdnServer, - ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value)); + ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value, + ConfigurationState.Instance.Hacks.ShowDirtyHacks ? ConfigurationState.Instance.Hacks.EnabledHacks : 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/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 907a7d7bd..6bede7999 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -17426,25 +17426,25 @@ "ID": "TitleUpdateVersionLabel", "Translations": { "ar_SA": "الإصدار: {0}", - "de_DE": "Version {0} - {1}", - "el_GR": "Version {0} - {1}", - "en_US": "Version {0} - {1}", - "es_ES": "Versión {0} - {1}", + "de_DE": "", + "el_GR": "", + "en_US": "Version {0}", + "es_ES": "Versión {0}", "fr_FR": "", - "he_IL": "גרסה {0} - {1}", - "it_IT": "Versione {0} - {1}", - "ja_JP": "バージョン {0} - {1}", - "ko_KR": "버전 {0} - {1}", - "no_NO": "Versjon {0} - {1}", - "pl_PL": "Wersja {0} - {1}", - "pt_BR": "Versão {0} - {1}", - "ru_RU": "Версия {0} - {1}", - "sv_SE": "Version {0} - {1}", - "th_TH": "เวอร์ชั่น {0} - {1}", - "tr_TR": "Sürüm {0} - {1}", - "uk_UA": "Версія {0} - {1}", - "zh_CN": "游戏更新的版本 {0} - {1}", - "zh_TW": "版本 {0} - {1}" + "he_IL": "גרסה: {0}", + "it_IT": "Versione {0}", + "ja_JP": "バージョン {0}", + "ko_KR": "버전 {0}", + "no_NO": "Versjon {0}", + "pl_PL": "Wersja {0}", + "pt_BR": "Versão {0}", + "ru_RU": "Версия {0}", + "sv_SE": "Version {0}", + "th_TH": "เวอร์ชั่น {0}", + "tr_TR": "Sürüm {0}", + "uk_UA": "Версія {0}", + "zh_CN": "游戏更新的版本 {0}", + "zh_TW": "版本 {0}" } }, { @@ -22598,4 +22598,4 @@ } } ] -} \ No newline at end of file +} 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 85% rename from src/Ryujinx.UI.Common/DiscordIntegrationModule.cs rename to src/Ryujinx/DiscordIntegrationModule.cs index 574aaff87..ee00f2c0d 100644 --- a/src/Ryujinx.UI.Common/DiscordIntegrationModule.cs +++ b/src/Ryujinx/DiscordIntegrationModule.cs @@ -1,13 +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 { @@ -44,6 +45,16 @@ namespace Ryujinx.UI.Common }; ConfigurationState.Instance.EnableDiscordIntegration.Event += Update; + TitleIDs.CurrentApplication.Event += (_, e) => + { + if (e.NewValue) + SwitchToPlayingState( + ApplicationLibrary.LoadAndSaveMetaData(e.NewValue), + Switch.Shared.Processes.ActiveApplication + ); + else + SwitchToMainState(); + }; } private static void Update(object sender, ReactiveEventArgs evnt) @@ -69,7 +80,7 @@ namespace Ryujinx.UI.Common } } - public static void SwitchToPlayingState(ApplicationMetadata appMeta, ProcessResult procRes) + private static void SwitchToPlayingState(ApplicationMetadata appMeta, ProcessResult procRes) { _discordClient?.SetPresence(new RichPresence { @@ -88,7 +99,7 @@ namespace Ryujinx.UI.Common }); } - public static void SwitchToMainState() => _discordClient?.SetPresence(_discordPresenceMain); + private static void SwitchToMainState() => _discordClient?.SetPresence(_discordPresenceMain); private static string TruncateToByteLength(string input) { 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 d98e499c2..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,12 +129,33 @@ + + Assets\ShortcutFiles\shortcut-template.desktop + + + Assets\ShortcutFiles\shortcut-launch-script.sh + + + Assets\ShortcutFiles\shortcut-template.plist + + + + + + + + + + + + + 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/BaseModel.cs b/src/Ryujinx/UI/ViewModels/BaseModel.cs index d8f2e9096..e27c52867 100644 --- a/src/Ryujinx/UI/ViewModels/BaseModel.cs +++ b/src/Ryujinx/UI/ViewModels/BaseModel.cs @@ -13,8 +13,9 @@ namespace Ryujinx.Ava.UI.ViewModels PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - protected void OnPropertiesChanged(params ReadOnlySpan propertyNames) + protected void OnPropertiesChanged(string firstPropertyName, params ReadOnlySpan propertyNames) { + OnPropertyChanged(firstPropertyName); foreach (var propertyName in propertyNames) { OnPropertyChanged(propertyName); 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 9feaaba9b..a5bdd2f88 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -1,6 +1,7 @@ using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Threading; +using Gommon; using LibHac.Tools.FsSystem; using Ryujinx.Audio.Backends.OpenAL; using Ryujinx.Audio.Backends.SDL2; @@ -9,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; @@ -17,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; @@ -62,7 +63,9 @@ namespace Ryujinx.Ava.UI.ViewModels private int _networkInterfaceIndex; private int _multiplayerModeIndex; private string _ldnPassphrase; - private string _LdnServer; + private string _ldnServer; + + public SettingsHacksViewModel DirtyHacks { get; } public int ResolutionScale { @@ -162,9 +165,7 @@ namespace Ryujinx.Ava.UI.ViewModels get => _vSyncMode; set { - if (value == VSyncMode.Custom || - value == VSyncMode.Switch || - value == VSyncMode.Unbounded) + if (value is VSyncMode.Custom or VSyncMode.Switch or VSyncMode.Unbounded) { _vSyncMode = value; OnPropertyChanged(); @@ -258,6 +259,8 @@ namespace Ryujinx.Ava.UI.ViewModels public bool UseHypervisor { get; set; } public bool DisableP2P { get; set; } + public bool ShowDirtyHacks => ConfigurationState.Instance.Hacks.ShowDirtyHacks; + public string TimeZone { get; set; } public string ShaderDumpPath { get; set; } @@ -374,10 +377,10 @@ namespace Ryujinx.Ava.UI.ViewModels public string LdnServer { - get => _LdnServer; + get => _ldnServer; set { - _LdnServer = value; + _ldnServer = value; OnPropertyChanged(); } } @@ -386,9 +389,12 @@ namespace Ryujinx.Ava.UI.ViewModels { _virtualFileSystem = virtualFileSystem; _contentManager = contentManager; + if (Program.PreviewerDetached) { Task.Run(LoadTimeZones); + + DirtyHacks = new SettingsHacksViewModel(this); } } @@ -408,6 +414,8 @@ namespace Ryujinx.Ava.UI.ViewModels { Task.Run(LoadAvailableGpus); LoadCurrentConfiguration(); + + DirtyHacks = new SettingsHacksViewModel(this); } } @@ -622,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() @@ -746,6 +754,11 @@ namespace Ryujinx.Ava.UI.ViewModels config.Multiplayer.DisableP2p.Value = DisableP2P; config.Multiplayer.LdnPassphrase.Value = LdnPassphrase; config.Multiplayer.LdnServer.Value = LdnServer; + + // Dirty Hacks + config.Hacks.Xc2MenuSoftlockFix.Value = DirtyHacks.Xc2MenuSoftlockFixEnabled; + config.Hacks.EnableShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelayEnabled; + config.Hacks.ShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelay; config.ToFileFormat().SaveConfig(Program.ConfigurationPath); 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 new file mode 100644 index 000000000..087112368 --- /dev/null +++ b/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml.cs b/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml.cs new file mode 100644 index 000000000..0017b5583 --- /dev/null +++ b/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace Ryujinx.Ava.UI.Views.Settings +{ + public partial class SettingsHacksView : UserControl + { + 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 b/src/Ryujinx/UI/Windows/SettingsWindow.axaml index 2bf5b55e7..59302b6fc 100644 --- a/src/Ryujinx/UI/Windows/SettingsWindow.axaml +++ b/src/Ryujinx/UI/Windows/SettingsWindow.axaml @@ -37,6 +37,7 @@ + +