From 4a545a00c6765ac3b0c4de23627372e9ced67d2c Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Sun, 8 Dec 2024 19:01:53 -0600 Subject: [PATCH 01/12] Headless in Avalonia, v1 It will get more advanced than this, this is for proof of concept. --- src/Ryujinx.Headless.SDL2/Program.cs | 4 ++-- src/Ryujinx.UI.Common/Helper/FileAssociationHelper.cs | 2 +- src/Ryujinx/Program.cs | 6 ++++++ src/Ryujinx/Ryujinx.csproj | 1 + 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index 12158176a..46743b4eb 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -43,7 +43,7 @@ using Key = Ryujinx.Common.Configuration.Hid.Key; namespace Ryujinx.Headless.SDL2 { - class Program + public class Program { public static string Version { get; private set; } @@ -62,7 +62,7 @@ namespace Ryujinx.Headless.SDL2 private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - static void Main(string[] args) + public static void Main(string[] args) { Version = ReleaseInformation.Version; diff --git a/src/Ryujinx.UI.Common/Helper/FileAssociationHelper.cs b/src/Ryujinx.UI.Common/Helper/FileAssociationHelper.cs index 9333a1b76..5dc871063 100644 --- a/src/Ryujinx.UI.Common/Helper/FileAssociationHelper.cs +++ b/src/Ryujinx.UI.Common/Helper/FileAssociationHelper.cs @@ -132,7 +132,7 @@ namespace Ryujinx.UI.Common.Helper if (uninstall) { - // If the types don't already exist, there's nothing to do and we can call this operation successful. + // If the types don't already exist, there's nothing to do, and we can call this operation successful. if (!AreMimeTypesRegisteredWindows()) { return true; diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 05fd66b90..0f6d1bfae 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -55,6 +55,12 @@ namespace Ryujinx.Ava Initialize(args); + if (args[0] is "--no-gui" or "nogui") + { + Headless.SDL2.Program.Main(args.Skip(1).ToArray()); + return 0; + } + LoggerAdapter.Register(); IconProvider.Current diff --git a/src/Ryujinx/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj index 989a3a5bd..74f4e479a 100644 --- a/src/Ryujinx/Ryujinx.csproj +++ b/src/Ryujinx/Ryujinx.csproj @@ -67,6 +67,7 @@ + From 90db9a962d6115030c7f87e93942939f4a3921f6 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Mon, 9 Dec 2024 01:36:00 -0600 Subject: [PATCH 02/12] Iteration 2: Drop Headless-specific project, drop headless releases. Also prevent duplicate log targets by checking name equality. --- .github/workflows/build.yml | 23 ------ .github/workflows/canary.yml | 20 +---- .github/workflows/release.yml | 17 +--- Ryujinx.sln | 6 -- src/Ryujinx.Common/Logging/Logger.cs | 20 ++--- .../Ryujinx.Headless.SDL2.csproj | 15 ---- .../HeadlessDynamicTextInputHandler.cs | 2 +- .../Headless}/HeadlessHostUiTheme.cs | 2 +- .../Headless/HeadlessRyujinx.cs} | 73 +++++++++++++++--- .../Headless}/OpenGL/OpenGLWindow.cs | 2 +- .../Headless}/Options.cs | 11 ++- .../Headless}/Ryujinx.bmp | Bin .../Headless}/StatusUpdatedEventArgs.cs | 2 +- .../Headless}/Vulkan/VulkanWindow.cs | 2 +- .../Headless}/WindowBase.cs | 5 +- src/Ryujinx/Program.cs | 9 ++- src/Ryujinx/Ryujinx.csproj | 3 +- 17 files changed, 100 insertions(+), 112 deletions(-) rename src/{Ryujinx.Headless.SDL2 => Ryujinx/Headless}/HeadlessDynamicTextInputHandler.cs (97%) rename src/{Ryujinx.Headless.SDL2 => Ryujinx/Headless}/HeadlessHostUiTheme.cs (93%) rename src/{Ryujinx.Headless.SDL2/Program.cs => Ryujinx/Headless/HeadlessRyujinx.cs} (91%) rename src/{Ryujinx.Headless.SDL2 => Ryujinx/Headless}/OpenGL/OpenGLWindow.cs (99%) rename src/{Ryujinx.Headless.SDL2 => Ryujinx/Headless}/Options.cs (97%) rename src/{Ryujinx.Headless.SDL2 => Ryujinx/Headless}/Ryujinx.bmp (100%) rename src/{Ryujinx.Headless.SDL2 => Ryujinx/Headless}/StatusUpdatedEventArgs.cs (94%) rename src/{Ryujinx.Headless.SDL2 => Ryujinx/Headless}/Vulkan/VulkanWindow.cs (98%) rename src/{Ryujinx.Headless.SDL2 => Ryujinx/Headless}/WindowBase.cs (99%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 21dc3eb0b..aeb12a575 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,14 +64,9 @@ jobs: run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13' - - name: Publish Ryujinx.Headless.SDL2 - run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained - if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13' - - name: Set executable bit run: | chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh - chmod +x ./publish_sdl2_headless/Ryujinx.Headless.SDL2 ./publish_sdl2_headless/Ryujinx.sh if: github.event_name == 'pull_request' && matrix.platform.os == 'ubuntu-latest' - name: Build AppImage @@ -119,13 +114,6 @@ jobs: name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}-AppImage path: publish_appimage - - name: Upload Ryujinx.Headless.SDL2 artifact - uses: actions/upload-artifact@v4 - with: - name: nogui-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }} - path: publish_sdl2_headless - if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13' - build_macos: name: macOS Universal (${{ matrix.configuration }}) runs-on: ubuntu-latest @@ -171,20 +159,9 @@ jobs: run: | ./distribution/macos/create_macos_build_ava.sh . publish_tmp publish ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER" - - name: Publish macOS Ryujinx.Headless.SDL2 - run: | - ./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER" - - name: Upload Ryujinx artifact uses: actions/upload-artifact@v4 with: name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal path: "publish/*.tar.gz" if: github.event_name == 'pull_request' - - - name: Upload Ryujinx.Headless.SDL2 artifact - uses: actions/upload-artifact@v4 - with: - name: nogui-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal - path: "publish_headless/*.tar.gz" - if: github.event_name == 'pull_request' diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml index df28e4784..58b659117 100644 --- a/.github/workflows/canary.yml +++ b/.github/workflows/canary.yml @@ -104,7 +104,6 @@ jobs: - name: Publish run: | dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained - dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained - name: Packing Windows builds if: matrix.platform.os == 'windows-latest' @@ -113,11 +112,6 @@ jobs: rm publish/libarmeilleure-jitsupport.dylib 7z a ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish popd - - pushd publish_sdl2_headless - rm publish/libarmeilleure-jitsupport.dylib - 7z a ../release_output/nogui-ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish - popd shell: bash - name: Packing Linux builds @@ -128,12 +122,6 @@ jobs: chmod +x publish/Ryujinx.sh publish/Ryujinx tar -czvf ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish popd - - pushd publish_sdl2_headless - rm publish/libarmeilleure-jitsupport.dylib - chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2 - tar -czvf ../release_output/nogui-ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish - popd shell: bash #- name: Build AppImage (Linux) @@ -179,7 +167,7 @@ jobs: with: name: ${{ steps.version_info.outputs.build_version }} artifacts: "release_output/*.tar.gz,release_output/*.zip" - #artifacts: "release_output/*.tar.gz,release_output/*.zip/*AppImage*" + #artifacts: "release_output/*.tar.gz,release_output/*.zip,release_output/*AppImage*" tag: ${{ steps.version_info.outputs.build_version }} body: "**Full Changelog**: https://github.com/${{ github.repository }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }}" omitBodyDuringUpdate: true @@ -238,15 +226,11 @@ jobs: run: | ./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 1 - - name: Publish macOS Ryujinx.Headless.SDL2 - run: | - ./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 1 - - name: Pushing new release uses: ncipollo/release-action@v1 with: name: "Canary ${{ steps.version_info.outputs.build_version }}" - artifacts: "publish_ava/*.tar.gz, publish_headless/*.tar.gz" + artifacts: "publish_ava/*.tar.gz" tag: ${{ steps.version_info.outputs.build_version }} body: "**Full Changelog**: https://github.com/${{ github.repository }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }}" omitBodyDuringUpdate: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fbf715756..d4bdd2430 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -103,7 +103,6 @@ jobs: - name: Publish run: | dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained - dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained - name: Packing Windows builds if: matrix.platform.os == 'windows-latest' @@ -112,11 +111,6 @@ jobs: rm libarmeilleure-jitsupport.dylib 7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish popd - - pushd publish_sdl2_headless - rm libarmeilleure-jitsupport.dylib - 7z a ../release_output/nogui-ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish - popd shell: bash - name: Build AppImage (Linux) @@ -163,11 +157,6 @@ jobs: chmod +x Ryujinx.sh Ryujinx tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish popd - - pushd publish_sdl2_headless - chmod +x Ryujinx.sh Ryujinx.Headless.SDL2 - tar -czvf ../release_output/nogui-ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish - popd shell: bash - name: Pushing new release @@ -233,15 +222,11 @@ jobs: run: | ./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 0 - - name: Publish macOS Ryujinx.Headless.SDL2 - run: | - ./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 0 - - name: Pushing new release uses: ncipollo/release-action@v1 with: name: ${{ steps.version_info.outputs.build_version }} - artifacts: "publish/*.tar.gz, publish_headless/*.tar.gz" + artifacts: "publish/*.tar.gz" tag: ${{ steps.version_info.outputs.build_version }} body: "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}" omitBodyDuringUpdate: true diff --git a/Ryujinx.sln b/Ryujinx.sln index d661b903c..58c556efc 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -57,8 +57,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.SDL2.Common", "src\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SDL2", "src\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj", "{D99A395A-8569-4DB0-B336-900647890052}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Headless.SDL2", "src\Ryujinx.Headless.SDL2\Ryujinx.Headless.SDL2.csproj", "{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.FFmpeg", "src\Ryujinx.Graphics.Nvdec.FFmpeg\Ryujinx.Graphics.Nvdec.FFmpeg.csproj", "{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "src\Ryujinx\Ryujinx.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}" @@ -204,10 +202,6 @@ Global {D99A395A-8569-4DB0-B336-900647890052}.Debug|Any CPU.Build.0 = Debug|Any CPU {D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.ActiveCfg = Release|Any CPU {D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.Build.0 = Release|Any CPU - {390DC343-5CB4-4C79-A5DD-E3ED235E4C49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {390DC343-5CB4-4C79-A5DD-E3ED235E4C49}.Debug|Any CPU.Build.0 = Debug|Any CPU - {390DC343-5CB4-4C79-A5DD-E3ED235E4C49}.Release|Any CPU.ActiveCfg = Release|Any CPU - {390DC343-5CB4-4C79-A5DD-E3ED235E4C49}.Release|Any CPU.Build.0 = Release|Any CPU {BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Debug|Any CPU.Build.0 = Debug|Any CPU {BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/src/Ryujinx.Common/Logging/Logger.cs b/src/Ryujinx.Common/Logging/Logger.cs index 26d343969..6ea6b7ac3 100644 --- a/src/Ryujinx.Common/Logging/Logger.cs +++ b/src/Ryujinx.Common/Logging/Logger.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Runtime.CompilerServices; using System.Threading; @@ -157,21 +158,16 @@ namespace Ryujinx.Common.Logging _time.Restart(); } - private static ILogTarget GetTarget(string targetName) - { - foreach (var target in _logTargets) - { - if (target.Name.Equals(targetName)) - { - return target; - } - } - - return null; - } + private static ILogTarget GetTarget(string targetName) + => _logTargets.FirstOrDefault(target => target.Name.Equals(targetName)); public static void AddTarget(ILogTarget target) { + if (_logTargets.Any(t => t.Name == target.Name)) + { + return; + } + _logTargets.Add(target); Updated += target.Log; diff --git a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj index 8fbf9be1e..6cb8d66ae 100644 --- a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj +++ b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj @@ -38,27 +38,12 @@ - - - Always - THIRDPARTY.md - - - Always - LICENSE.txt - - - Always - - - - false diff --git a/src/Ryujinx.Headless.SDL2/HeadlessDynamicTextInputHandler.cs b/src/Ryujinx/Headless/HeadlessDynamicTextInputHandler.cs similarity index 97% rename from src/Ryujinx.Headless.SDL2/HeadlessDynamicTextInputHandler.cs rename to src/Ryujinx/Headless/HeadlessDynamicTextInputHandler.cs index 40eb5ba98..2a507d2b7 100644 --- a/src/Ryujinx.Headless.SDL2/HeadlessDynamicTextInputHandler.cs +++ b/src/Ryujinx/Headless/HeadlessDynamicTextInputHandler.cs @@ -2,7 +2,7 @@ using Ryujinx.HLE.UI; using System.Threading; using System.Threading.Tasks; -namespace Ryujinx.Headless.SDL2 +namespace Ryujinx.Headless { /// /// Headless text processing class, right now there is no way to forward the input to it. diff --git a/src/Ryujinx.Headless.SDL2/HeadlessHostUiTheme.cs b/src/Ryujinx/Headless/HeadlessHostUiTheme.cs similarity index 93% rename from src/Ryujinx.Headless.SDL2/HeadlessHostUiTheme.cs rename to src/Ryujinx/Headless/HeadlessHostUiTheme.cs index 78cd43ae5..b5e1ce526 100644 --- a/src/Ryujinx.Headless.SDL2/HeadlessHostUiTheme.cs +++ b/src/Ryujinx/Headless/HeadlessHostUiTheme.cs @@ -1,6 +1,6 @@ using Ryujinx.HLE.UI; -namespace Ryujinx.Headless.SDL2 +namespace Ryujinx.Headless { internal class HeadlessHostUiTheme : IHostUITheme { diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs similarity index 91% rename from src/Ryujinx.Headless.SDL2/Program.cs rename to src/Ryujinx/Headless/HeadlessRyujinx.cs index 46743b4eb..2918e130b 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -1,7 +1,12 @@ +using Avalonia.Threading; using CommandLine; +using DiscordRPC; using Gommon; using LibHac.Tools.FsSystem; using Ryujinx.Audio.Backends.SDL2; +using Ryujinx.Ava; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; @@ -21,8 +26,6 @@ using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.Vulkan; using Ryujinx.Graphics.Vulkan.MoltenVK; -using Ryujinx.Headless.SDL2.OpenGL; -using Ryujinx.Headless.SDL2.Vulkan; using Ryujinx.HLE; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; @@ -31,22 +34,25 @@ using Ryujinx.Input; using Ryujinx.Input.HLE; using Ryujinx.Input.SDL2; using Ryujinx.SDL2.Common; +using Ryujinx.UI.App.Common; +using Ryujinx.UI.Common; +using Ryujinx.UI.Common.Configuration; +using Ryujinx.UI.Common.Helper; using Silk.NET.Vulkan; using System; using System.Collections.Generic; using System.IO; using System.Text.Json; using System.Threading; +using System.Threading.Tasks; using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId; using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; using Key = Ryujinx.Common.Configuration.Hid.Key; -namespace Ryujinx.Headless.SDL2 +namespace Ryujinx.Headless { - public class Program + public class HeadlessRyujinx { - public static string Version { get; private set; } - private static VirtualFileSystem _virtualFileSystem; private static ContentManager _contentManager; private static AccountManager _accountManager; @@ -62,14 +68,63 @@ namespace Ryujinx.Headless.SDL2 private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - public static void Main(string[] args) + public static void Initialize(string[] args) { - Version = ReleaseInformation.Version; + // Ensure Discord presence timestamp begins at the absolute start of when Ryujinx is launched + DiscordIntegrationModule.StartedAt = Timestamps.Now; + // Delete backup files after updating. + Task.Run(Updater.CleanupUpdate); + + // Hook unhandled exception and process exit events. + AppDomain.CurrentDomain.UnhandledException += (sender, e) + => Program.ProcessUnhandledException(sender, e.ExceptionObject as Exception, e.IsTerminating); + AppDomain.CurrentDomain.ProcessExit += (_, _) => Program.Exit(); + + // 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(); + + // Initialize the logger system. + LoggerModule.Initialize(); + + // Initialize Discord integration. + DiscordIntegrationModule.Initialize(); + + // Initialize SDL2 driver + SDL2Driver.MainThreadDispatcher = action => Dispatcher.UIThread.InvokeAsync(action, DispatcherPriority.Input); + + Program.ReloadConfig(); + + // Logging system information. + Program.PrintSystemInfo(); + + // Enable OGL multithreading on the driver, and some other flags. + DriverUtilities.InitDriverConfig(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off); + + // Check if keys exists. + if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys"))) + { + if (!(AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile && File.Exists(Path.Combine(AppDataManager.KeysDirPathUser, "prod.keys")))) + { + Logger.Error?.Print(LogClass.Application, "Keys not found"); + } + } + + Entrypoint(args); + } + + public static void Entrypoint(string[] args) + { // Make process DPI aware for proper window sizing on high-res screens. ForceDpiAware.Windows(); - Console.Title = $"Ryujinx Console {Version} (Headless SDL2)"; + Console.Title = $"Ryujinx Console {Program.Version} (Headless SDL2)"; if (OperatingSystem.IsMacOS() || OperatingSystem.IsLinux()) { diff --git a/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs b/src/Ryujinx/Headless/OpenGL/OpenGLWindow.cs similarity index 99% rename from src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs rename to src/Ryujinx/Headless/OpenGL/OpenGLWindow.cs index 8c4854a11..c00a0648f 100644 --- a/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs +++ b/src/Ryujinx/Headless/OpenGL/OpenGLWindow.cs @@ -7,7 +7,7 @@ using Ryujinx.Input.HLE; using System; using static SDL2.SDL; -namespace Ryujinx.Headless.SDL2.OpenGL +namespace Ryujinx.Headless { class OpenGLWindow : WindowBase { diff --git a/src/Ryujinx.Headless.SDL2/Options.cs b/src/Ryujinx/Headless/Options.cs similarity index 97% rename from src/Ryujinx.Headless.SDL2/Options.cs rename to src/Ryujinx/Headless/Options.cs index 4e2ad5b58..db873b921 100644 --- a/src/Ryujinx.Headless.SDL2/Options.cs +++ b/src/Ryujinx/Headless/Options.cs @@ -2,13 +2,22 @@ using CommandLine; using Ryujinx.Common.Configuration; using Ryujinx.HLE; using Ryujinx.HLE.HOS.SystemState; +using Ryujinx.UI.Common.Configuration; -namespace Ryujinx.Headless.SDL2 +namespace Ryujinx.Headless { public class Options { // General + public void InheritMainConfig(ConfigurationState configurationState) + { + + } + + [Option("use-main-config", Required = false, Default = false, HelpText = "Use the settings from what was configured via the UI.")] + public bool InheritConfig { get; set; } + [Option("root-data-dir", Required = false, HelpText = "Set the custom folder path for Ryujinx data.")] public string BaseDataDir { get; set; } diff --git a/src/Ryujinx.Headless.SDL2/Ryujinx.bmp b/src/Ryujinx/Headless/Ryujinx.bmp similarity index 100% rename from src/Ryujinx.Headless.SDL2/Ryujinx.bmp rename to src/Ryujinx/Headless/Ryujinx.bmp diff --git a/src/Ryujinx.Headless.SDL2/StatusUpdatedEventArgs.cs b/src/Ryujinx/Headless/StatusUpdatedEventArgs.cs similarity index 94% rename from src/Ryujinx.Headless.SDL2/StatusUpdatedEventArgs.cs rename to src/Ryujinx/Headless/StatusUpdatedEventArgs.cs index c1dd3805f..6c76a43a1 100644 --- a/src/Ryujinx.Headless.SDL2/StatusUpdatedEventArgs.cs +++ b/src/Ryujinx/Headless/StatusUpdatedEventArgs.cs @@ -1,6 +1,6 @@ using System; -namespace Ryujinx.Headless.SDL2 +namespace Ryujinx.Headless { class StatusUpdatedEventArgs( string vSyncMode, diff --git a/src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs b/src/Ryujinx/Headless/Vulkan/VulkanWindow.cs similarity index 98% rename from src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs rename to src/Ryujinx/Headless/Vulkan/VulkanWindow.cs index b88e0fe83..92caad34e 100644 --- a/src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs +++ b/src/Ryujinx/Headless/Vulkan/VulkanWindow.cs @@ -6,7 +6,7 @@ using System; using System.Runtime.InteropServices; using static SDL2.SDL; -namespace Ryujinx.Headless.SDL2.Vulkan +namespace Ryujinx.Headless { class VulkanWindow : WindowBase { diff --git a/src/Ryujinx.Headless.SDL2/WindowBase.cs b/src/Ryujinx/Headless/WindowBase.cs similarity index 99% rename from src/Ryujinx.Headless.SDL2/WindowBase.cs rename to src/Ryujinx/Headless/WindowBase.cs index fbe7cb49c..1862c2757 100644 --- a/src/Ryujinx.Headless.SDL2/WindowBase.cs +++ b/src/Ryujinx/Headless/WindowBase.cs @@ -1,5 +1,6 @@ using Humanizer; using LibHac.Tools.Fs; +using Ryujinx.Ava; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Logging; @@ -26,7 +27,7 @@ using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing; using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter; using Switch = Ryujinx.HLE.Switch; -namespace Ryujinx.Headless.SDL2 +namespace Ryujinx.Headless { abstract partial class WindowBase : IHostUIHandler, IDisposable { @@ -318,7 +319,7 @@ namespace Ryujinx.Headless.SDL2 Device.VSyncMode.ToString(), dockedMode, Device.Configuration.AspectRatio.ToText(), - $"Game: {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", + $"{Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", $"FIFO: {Device.Statistics.GetFifoPercent():0.00} %", $"GPU: {_gpuDriverName}")); diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 0f6d1bfae..35cdb6c0e 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -14,6 +14,7 @@ using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.Logging; 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; @@ -57,7 +58,7 @@ namespace Ryujinx.Ava if (args[0] is "--no-gui" or "nogui") { - Headless.SDL2.Program.Main(args.Skip(1).ToArray()); + HeadlessRyujinx.Initialize(args[1..]); return 0; } @@ -228,7 +229,7 @@ namespace Ryujinx.Ava UseHardwareAcceleration = CommandLineState.OverrideHardwareAcceleration.Value; } - private static void PrintSystemInfo() + internal static void PrintSystemInfo() { Logger.Notice.Print(LogClass.Application, $"{App.FullAppName} Version: {Version}"); SystemInfo.Gather().Print(); @@ -245,7 +246,7 @@ namespace Ryujinx.Ava : $"Launch Mode: {AppDataManager.Mode}"); } - private static void ProcessUnhandledException(object sender, Exception ex, bool isTerminating) + internal static void ProcessUnhandledException(object sender, Exception ex, bool isTerminating) { Logger.Log log = Logger.Error ?? Logger.Notice; string message = $"Unhandled exception caught: {ex}"; @@ -260,7 +261,7 @@ namespace Ryujinx.Ava Exit(); } - public static void Exit() + internal static void Exit() { DiscordIntegrationModule.Exit(); diff --git a/src/Ryujinx/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj index 74f4e479a..8412044c7 100644 --- a/src/Ryujinx/Ryujinx.csproj +++ b/src/Ryujinx/Ryujinx.csproj @@ -43,6 +43,7 @@ + @@ -67,7 +68,6 @@ - @@ -163,6 +163,7 @@ + From 338777055f11211a3eec13a99105b13c46e43c66 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Mon, 9 Dec 2024 01:38:13 -0600 Subject: [PATCH 03/12] I love that I clicked delete on this and it was literally like, still there, not deleted. lol --- .../Ryujinx.Headless.SDL2.csproj | 58 ------------------- 1 file changed, 58 deletions(-) delete mode 100644 src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj diff --git a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj deleted file mode 100644 index 6cb8d66ae..000000000 --- a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj +++ /dev/null @@ -1,58 +0,0 @@ - - - - net8.0 - win-x64;osx-x64;linux-x64 - Exe - true - 1.0.0-dirty - $(DefineConstants);$(ExtraDefineConstants) - - - true - $(DefaultItemExcludes);._* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Always - - - - - - false - ..\Ryujinx\Ryujinx.ico - - - - true - true - partial - - From a477658b85697d42bc709a5a8aef086b24739399 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Mon, 9 Dec 2024 03:21:07 -0600 Subject: [PATCH 04/12] Headless in Avalonia v2 --- .../Logging/Targets/AsyncLogTargetWrapper.cs | 6 +- .../Account/Acc/AccountSaveDataManager.cs | 16 +- .../Configuration/System/Language.cs | 1 + src/Ryujinx/Headless/HeadlessRyujinx.cs | 102 ++++++++---- src/Ryujinx/Headless/Options.cs | 154 +++++++++++++++++- src/Ryujinx/Program.cs | 17 +- 6 files changed, 247 insertions(+), 49 deletions(-) diff --git a/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs b/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs index a9dbe646a..1fcfea4da 100644 --- a/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs +++ b/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs @@ -27,11 +27,7 @@ namespace Ryujinx.Common.Logging.Targets private readonly int _overflowTimeout; - string ILogTarget.Name { get => _target.Name; } - - public AsyncLogTargetWrapper(ILogTarget target) - : this(target, -1) - { } + string ILogTarget.Name => _target.Name; public AsyncLogTargetWrapper(ILogTarget target, int queueLimit = -1, AsyncLogTargetOverflowAction overflowAction = AsyncLogTargetOverflowAction.Block) { diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs index b1ef0761c..aa57a0310 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs @@ -1,3 +1,4 @@ +using Gommon; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; @@ -6,12 +7,13 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; +using System.Linq; namespace Ryujinx.HLE.HOS.Services.Account.Acc { - class AccountSaveDataManager + public class AccountSaveDataManager { - private readonly string _profilesJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "Profiles.json"); + private static readonly string _profilesJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "Profiles.json"); private static readonly ProfilesJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); @@ -49,6 +51,16 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc } } + public static Optional GetLastUsedUser() + { + ProfilesJson profilesJson = JsonHelper.DeserializeFromFile(_profilesJsonPath, _serializerContext.ProfilesJson); + + return profilesJson.Profiles + .FindFirst(profile => profile.AccountState == AccountState.Open) + .Convert(profileJson => new UserProfile(new UserId(profileJson.UserId), profileJson.Name, + profileJson.Image, profileJson.LastModifiedTimestamp)); + } + public void Save(ConcurrentDictionary profiles) { ProfilesJson profilesJson = new() diff --git a/src/Ryujinx.UI.Common/Configuration/System/Language.cs b/src/Ryujinx.UI.Common/Configuration/System/Language.cs index d1d395b00..8ca4e542b 100644 --- a/src/Ryujinx.UI.Common/Configuration/System/Language.cs +++ b/src/Ryujinx.UI.Common/Configuration/System/Language.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Utilities; +using Ryujinx.HLE.HOS.SystemState; using System.Text.Json.Serialization; namespace Ryujinx.UI.Common.Configuration.System diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs index 2918e130b..74cf69e57 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -68,7 +68,7 @@ namespace Ryujinx.Headless private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - public static void Initialize(string[] args) + public static void Initialize() { // Ensure Discord presence timestamp begins at the absolute start of when Ryujinx is launched DiscordIntegrationModule.StartedAt = Timestamps.Now; @@ -81,32 +81,17 @@ namespace Ryujinx.Headless => Program.ProcessUnhandledException(sender, e.ExceptionObject as Exception, e.IsTerminating); AppDomain.CurrentDomain.ProcessExit += (_, _) => Program.Exit(); - // 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(); - // Initialize the logger system. - LoggerModule.Initialize(); - // Initialize Discord integration. DiscordIntegrationModule.Initialize(); - // Initialize SDL2 driver - SDL2Driver.MainThreadDispatcher = action => Dispatcher.UIThread.InvokeAsync(action, DispatcherPriority.Input); - - Program.ReloadConfig(); + ReloadConfig(); // Logging system information. Program.PrintSystemInfo(); - // Enable OGL multithreading on the driver, and some other flags. - DriverUtilities.InitDriverConfig(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off); - // Check if keys exists. if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys"))) { @@ -115,8 +100,6 @@ namespace Ryujinx.Headless Logger.Error?.Print(LogClass.Application, "Keys not found"); } } - - Entrypoint(args); } public static void Entrypoint(string[] args) @@ -124,7 +107,7 @@ namespace Ryujinx.Headless // Make process DPI aware for proper window sizing on high-res screens. ForceDpiAware.Windows(); - Console.Title = $"Ryujinx Console {Program.Version} (Headless SDL2)"; + Console.Title = $"Ryujinx Console {Program.Version} (Headless)"; if (OperatingSystem.IsMacOS() || OperatingSystem.IsLinux()) { @@ -152,7 +135,7 @@ namespace Ryujinx.Headless } Parser.Default.ParseArguments(args) - .WithParsed(Load) + .WithParsed(options => Load(args, options)) .WithNotParsed(errors => { Logger.Error?.PrintMsg(LogClass.Application, "Error parsing command-line arguments:"); @@ -160,6 +143,53 @@ namespace Ryujinx.Headless errors.ForEach(err => Logger.Error?.PrintMsg(LogClass.Application, $" - {err.Tag}")); }); } + + public static void ReloadConfig(string customConfigPath = null) + { + string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName); + string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName); + + string configurationPath = null; + + // Now load the configuration as the other subsystems are now registered + if (File.Exists(localConfigurationPath)) + { + configurationPath = localConfigurationPath; + } + else if (File.Exists(appDataConfigurationPath)) + { + configurationPath = appDataConfigurationPath; + } + else if (File.Exists(customConfigPath)) + { + configurationPath = customConfigPath; + } + + if (configurationPath == null) + { + // No configuration, we load the default values and save it to disk + configurationPath = appDataConfigurationPath; + Logger.Notice.Print(LogClass.Application, $"No configuration file found. Saving default configuration to: {configurationPath}"); + + ConfigurationState.Instance.LoadDefault(); + ConfigurationState.Instance.ToFileFormat().SaveConfig(configurationPath); + } + else + { + Logger.Notice.Print(LogClass.Application, $"Loading configuration from: {configurationPath}"); + + if (ConfigurationFileFormat.TryLoad(configurationPath, out ConfigurationFileFormat configurationFileFormat)) + { + ConfigurationState.Instance.Load(configurationFileFormat, configurationPath); + } + else + { + Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location: {configurationPath}"); + + ConfigurationState.Instance.LoadDefault(); + } + } + } private static InputConfig HandlePlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index) { @@ -389,8 +419,13 @@ namespace Ryujinx.Headless return config; } - static void Load(Options option) + static void Load(string[] originalArgs, Options option) { + Initialize(); + + if (option.InheritConfig) + option.InheritMainConfig(originalArgs, ConfigurationState.Instance, out _inputConfiguration); + AppDataManager.Initialize(option.BaseDataDir); _virtualFileSystem = VirtualFileSystem.CreateInstance(); @@ -466,15 +501,18 @@ namespace Ryujinx.Headless } } - LoadPlayerConfiguration(option.InputProfile1Name, option.InputId1, PlayerIndex.Player1); - LoadPlayerConfiguration(option.InputProfile2Name, option.InputId2, PlayerIndex.Player2); - LoadPlayerConfiguration(option.InputProfile3Name, option.InputId3, PlayerIndex.Player3); - LoadPlayerConfiguration(option.InputProfile4Name, option.InputId4, PlayerIndex.Player4); - LoadPlayerConfiguration(option.InputProfile5Name, option.InputId5, PlayerIndex.Player5); - LoadPlayerConfiguration(option.InputProfile6Name, option.InputId6, PlayerIndex.Player6); - LoadPlayerConfiguration(option.InputProfile7Name, option.InputId7, PlayerIndex.Player7); - LoadPlayerConfiguration(option.InputProfile8Name, option.InputId8, PlayerIndex.Player8); - LoadPlayerConfiguration(option.InputProfileHandheldName, option.InputIdHandheld, PlayerIndex.Handheld); + if (!option.InheritConfig) + { + LoadPlayerConfiguration(option.InputProfile1Name, option.InputId1, PlayerIndex.Player1); + LoadPlayerConfiguration(option.InputProfile2Name, option.InputId2, PlayerIndex.Player2); + LoadPlayerConfiguration(option.InputProfile3Name, option.InputId3, PlayerIndex.Player3); + LoadPlayerConfiguration(option.InputProfile4Name, option.InputId4, PlayerIndex.Player4); + LoadPlayerConfiguration(option.InputProfile5Name, option.InputId5, PlayerIndex.Player5); + LoadPlayerConfiguration(option.InputProfile6Name, option.InputId6, PlayerIndex.Player6); + LoadPlayerConfiguration(option.InputProfile7Name, option.InputId7, PlayerIndex.Player7); + LoadPlayerConfiguration(option.InputProfile8Name, option.InputId8, PlayerIndex.Player8); + LoadPlayerConfiguration(option.InputProfileHandheldName, option.InputIdHandheld, PlayerIndex.Handheld); + } if (_inputConfiguration.Count == 0) { @@ -486,7 +524,7 @@ namespace Ryujinx.Headless Logger.SetEnable(LogLevel.Stub, !option.LoggingDisableStub); Logger.SetEnable(LogLevel.Info, !option.LoggingDisableInfo); Logger.SetEnable(LogLevel.Warning, !option.LoggingDisableWarning); - Logger.SetEnable(LogLevel.Error, option.LoggingEnableError); + Logger.SetEnable(LogLevel.Error, !option.LoggingDisableError); Logger.SetEnable(LogLevel.Trace, option.LoggingEnableTrace); Logger.SetEnable(LogLevel.Guest, !option.LoggingDisableGuest); Logger.SetEnable(LogLevel.AccessLog, option.LoggingEnableFsAccessLog); diff --git a/src/Ryujinx/Headless/Options.cs b/src/Ryujinx/Headless/Options.cs index db873b921..3a807cb42 100644 --- a/src/Ryujinx/Headless/Options.cs +++ b/src/Ryujinx/Headless/Options.cs @@ -1,8 +1,15 @@ using CommandLine; +using Gommon; 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; +using System.Reflection; namespace Ryujinx.Headless { @@ -10,9 +17,152 @@ namespace Ryujinx.Headless { // General - public void InheritMainConfig(ConfigurationState configurationState) + public void InheritMainConfig(string[] originalArgs, ConfigurationState configurationState, out List inputConfigs) { + if (NeedsOverride(nameof(UserProfile)) && AccountSaveDataManager.GetLastUsedUser().TryGet(out var profile)) + UserProfile = profile.Name; + if (NeedsOverride(nameof(IsFullscreen))) + IsFullscreen = configurationState.UI.StartFullscreen; + + if (NeedsOverride(nameof(EnableKeyboard))) + EnableKeyboard = configurationState.Hid.EnableKeyboard; + + if (NeedsOverride(nameof(EnableMouse))) + EnableMouse = configurationState.Hid.EnableMouse; + + if (NeedsOverride(nameof(HideCursorMode))) + HideCursorMode = configurationState.HideCursor; + + if (NeedsOverride(nameof(DisablePTC))) + DisablePTC = !configurationState.System.EnablePtc; + + if (NeedsOverride(nameof(EnableInternetAccess))) + EnableInternetAccess = configurationState.System.EnableInternetAccess; + + if (NeedsOverride(nameof(DisableFsIntegrityChecks))) + DisableFsIntegrityChecks = configurationState.System.EnableFsIntegrityChecks; + + if (NeedsOverride(nameof(FsGlobalAccessLogMode))) + FsGlobalAccessLogMode = configurationState.System.FsGlobalAccessLogMode; + + if (NeedsOverride(nameof(VSyncMode))) + VSyncMode = configurationState.Graphics.VSyncMode; + + if (NeedsOverride(nameof(CustomVSyncInterval))) + CustomVSyncInterval = configurationState.Graphics.CustomVSyncInterval; + + if (NeedsOverride(nameof(DisableShaderCache))) + DisableShaderCache = !configurationState.Graphics.EnableShaderCache; + + if (NeedsOverride(nameof(EnableTextureRecompression))) + EnableTextureRecompression = configurationState.Graphics.EnableTextureRecompression; + + if (NeedsOverride(nameof(DisableDockedMode))) + DisableDockedMode = !configurationState.System.EnableDockedMode; + + if (NeedsOverride(nameof(SystemLanguage))) + SystemLanguage = (SystemLanguage)(int)configurationState.System.Language.Value; + + if (NeedsOverride(nameof(SystemRegion))) + SystemRegion = (RegionCode)(int)configurationState.System.Region.Value; + + if (NeedsOverride(nameof(SystemTimeZone))) + SystemTimeZone = configurationState.System.TimeZone; + + if (NeedsOverride(nameof(SystemTimeOffset))) + SystemTimeOffset = configurationState.System.SystemTimeOffset; + + if (NeedsOverride(nameof(MemoryManagerMode))) + MemoryManagerMode = configurationState.System.MemoryManagerMode; + + if (NeedsOverride(nameof(AudioVolume))) + AudioVolume = configurationState.System.AudioVolume; + + if (NeedsOverride(nameof(UseHypervisor)) && OperatingSystem.IsMacOS()) + UseHypervisor = configurationState.System.UseHypervisor; + + if (NeedsOverride(nameof(MultiplayerLanInterfaceId))) + MultiplayerLanInterfaceId = configurationState.Multiplayer.LanInterfaceId; + + if (NeedsOverride(nameof(DisableFileLog))) + DisableFileLog = !configurationState.Logger.EnableFileLog; + + if (NeedsOverride(nameof(LoggingEnableDebug))) + LoggingEnableDebug = configurationState.Logger.EnableDebug; + + if (NeedsOverride(nameof(LoggingDisableStub))) + LoggingDisableStub = !configurationState.Logger.EnableStub; + + if (NeedsOverride(nameof(LoggingDisableInfo))) + LoggingDisableInfo = !configurationState.Logger.EnableInfo; + + if (NeedsOverride(nameof(LoggingDisableWarning))) + LoggingDisableWarning = !configurationState.Logger.EnableWarn; + + if (NeedsOverride(nameof(LoggingDisableError))) + LoggingDisableError = !configurationState.Logger.EnableError; + + if (NeedsOverride(nameof(LoggingEnableTrace))) + LoggingEnableTrace = configurationState.Logger.EnableTrace; + + if (NeedsOverride(nameof(LoggingDisableGuest))) + LoggingDisableGuest = !configurationState.Logger.EnableGuest; + + if (NeedsOverride(nameof(LoggingEnableFsAccessLog))) + LoggingEnableFsAccessLog = configurationState.Logger.EnableFsAccessLog; + + if (NeedsOverride(nameof(LoggingGraphicsDebugLevel))) + LoggingGraphicsDebugLevel = configurationState.Logger.GraphicsDebugLevel; + + if (NeedsOverride(nameof(ResScale))) + ResScale = configurationState.Graphics.ResScale; + + if (NeedsOverride(nameof(MaxAnisotropy))) + MaxAnisotropy = configurationState.Graphics.MaxAnisotropy; + + if (NeedsOverride(nameof(AspectRatio))) + AspectRatio = configurationState.Graphics.AspectRatio; + + if (NeedsOverride(nameof(BackendThreading))) + BackendThreading = configurationState.Graphics.BackendThreading; + + if (NeedsOverride(nameof(DisableMacroHLE))) + DisableMacroHLE = !configurationState.Graphics.EnableMacroHLE; + + if (NeedsOverride(nameof(GraphicsShadersDumpPath))) + GraphicsShadersDumpPath = configurationState.Graphics.ShadersDumpPath; + + if (NeedsOverride(nameof(GraphicsBackend))) + GraphicsBackend = configurationState.Graphics.GraphicsBackend; + + if (NeedsOverride(nameof(AntiAliasing))) + AntiAliasing = configurationState.Graphics.AntiAliasing; + + if (NeedsOverride(nameof(ScalingFilter))) + ScalingFilter = configurationState.Graphics.ScalingFilter; + + if (NeedsOverride(nameof(ScalingFilterLevel))) + ScalingFilterLevel = configurationState.Graphics.ScalingFilterLevel; + + if (NeedsOverride(nameof(DramSize))) + DramSize = configurationState.System.DramSize; + + if (NeedsOverride(nameof(IgnoreMissingServices))) + IgnoreMissingServices = configurationState.System.IgnoreMissingServices; + + if (NeedsOverride(nameof(IgnoreControllerApplet))) + IgnoreControllerApplet = configurationState.IgnoreApplet; + + + inputConfigs = configurationState.Hid.InputConfig; + + return; + + bool NeedsOverride(string argKey) => originalArgs.None(arg => arg.TrimStart('-').EqualsIgnoreCase(OptionName(argKey))); + + string OptionName(string propertyName) => + typeof(Options)!.GetProperty(propertyName)!.GetCustomAttribute()!.LongName; } [Option("use-main-config", Required = false, Default = false, HelpText = "Use the settings from what was configured via the UI.")] @@ -181,7 +331,7 @@ namespace Ryujinx.Headless public bool LoggingDisableWarning { get; set; } [Option("disable-error-logs", Required = false, HelpText = "Disables printing error log messages.")] - public bool LoggingEnableError { get; set; } + public bool LoggingDisableError { get; set; } [Option("enable-trace-logs", Required = false, Default = false, HelpText = "Enables printing trace log messages.")] public bool LoggingEnableTrace { get; set; } diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 35cdb6c0e..997b3dcde 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -53,15 +53,15 @@ namespace Ryujinx.Ava } PreviewerDetached = true; - - Initialize(args); - + if (args[0] is "--no-gui" or "nogui") { - HeadlessRyujinx.Initialize(args[1..]); + HeadlessRyujinx.Entrypoint(args[1..]); return 0; } + Initialize(args); + LoggerAdapter.Register(); IconProvider.Current @@ -114,9 +114,6 @@ namespace Ryujinx.Ava => ProcessUnhandledException(sender, e.ExceptionObject as Exception, e.IsTerminating); AppDomain.CurrentDomain.ProcessExit += (_, _) => Exit(); - // 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]; @@ -157,7 +154,7 @@ namespace Ryujinx.Ava } } - public static void ReloadConfig() + public static void ReloadConfig(string customConfigPath = null) { string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName); string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName); @@ -171,6 +168,10 @@ namespace Ryujinx.Ava { ConfigurationPath = appDataConfigurationPath; } + else if (File.Exists(customConfigPath)) + { + ConfigurationPath = customConfigPath; + } if (ConfigurationPath == null) { From 75fa7c7d4dbc4b9835b36e9d73f11ef224e0e5b3 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Mon, 9 Dec 2024 03:28:16 -0600 Subject: [PATCH 05/12] fixups --- src/Ryujinx/Program.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 997b3dcde..fbeca3fc8 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -113,6 +113,9 @@ namespace Ryujinx.Ava AppDomain.CurrentDomain.UnhandledException += (sender, e) => ProcessUnhandledException(sender, e.ExceptionObject as Exception, e.IsTerminating); AppDomain.CurrentDomain.ProcessExit += (_, _) => Exit(); + + // 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]; @@ -154,7 +157,7 @@ namespace Ryujinx.Ava } } - public static void ReloadConfig(string customConfigPath = null) + public static void ReloadConfig() { string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName); string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName); @@ -168,10 +171,6 @@ namespace Ryujinx.Ava { ConfigurationPath = appDataConfigurationPath; } - else if (File.Exists(customConfigPath)) - { - ConfigurationPath = customConfigPath; - } if (ConfigurationPath == null) { From 2fc58d58a509e9a864ce6084d75165d22d783c60 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Wed, 11 Dec 2024 14:15:57 -0600 Subject: [PATCH 06/12] Fix accidental null passing --- src/Ryujinx/Headless/HeadlessRyujinx.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs index 74cf69e57..b2941b884 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -160,7 +160,7 @@ namespace Ryujinx.Headless { configurationPath = appDataConfigurationPath; } - else if (File.Exists(customConfigPath)) + else if (customConfigPath != null && File.Exists(customConfigPath)) { configurationPath = customConfigPath; } From 1398dad67ce65aaf32db9e62eb9a3ee06e03b0e0 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Wed, 11 Dec 2024 15:01:28 -0600 Subject: [PATCH 07/12] hia: fix 2 crashes --- src/Ryujinx/Headless/HeadlessRyujinx.cs | 10 +++++----- src/Ryujinx/Program.cs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs index b2941b884..4135a3e38 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -87,8 +87,6 @@ namespace Ryujinx.Headless // Initialize Discord integration. DiscordIntegrationModule.Initialize(); - ReloadConfig(); - // Logging system information. Program.PrintSystemInfo(); @@ -422,12 +420,14 @@ namespace Ryujinx.Headless static void Load(string[] originalArgs, Options option) { Initialize(); - + if (option.InheritConfig) option.InheritMainConfig(originalArgs, ConfigurationState.Instance, out _inputConfiguration); - + AppDataManager.Initialize(option.BaseDataDir); - + + ReloadConfig(); + _virtualFileSystem = VirtualFileSystem.CreateInstance(); _libHacHorizonManager = new LibHacHorizonManager(); diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index fbeca3fc8..944ac6078 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -54,7 +54,7 @@ namespace Ryujinx.Ava PreviewerDetached = true; - if (args[0] is "--no-gui" or "nogui") + if (args.Length > 0 && args[0] is "--no-gui" or "nogui") { HeadlessRyujinx.Entrypoint(args[1..]); return 0; From 6f56690c1c4d04a9c0152100ca2940d9885a2dc0 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Wed, 11 Dec 2024 15:23:31 -0600 Subject: [PATCH 08/12] hia: Only reference AppDataManager properties after they've been initialized --- src/Ryujinx/Headless/HeadlessRyujinx.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs index 4135a3e38..7f3f697f5 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -89,15 +89,6 @@ namespace Ryujinx.Headless // Logging system information. Program.PrintSystemInfo(); - - // Check if keys exists. - if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys"))) - { - if (!(AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile && File.Exists(Path.Combine(AppDataManager.KeysDirPathUser, "prod.keys")))) - { - Logger.Error?.Print(LogClass.Application, "Keys not found"); - } - } } public static void Entrypoint(string[] args) @@ -426,6 +417,15 @@ namespace Ryujinx.Headless AppDataManager.Initialize(option.BaseDataDir); + // Check if keys exists. + if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys"))) + { + if (!(AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile && File.Exists(Path.Combine(AppDataManager.KeysDirPathUser, "prod.keys")))) + { + Logger.Error?.Print(LogClass.Application, "Keys not found"); + } + } + ReloadConfig(); _virtualFileSystem = VirtualFileSystem.CreateInstance(); From 12a506a644a8b90afe66a57d0292431e1305aeb3 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Wed, 11 Dec 2024 15:41:06 -0600 Subject: [PATCH 09/12] hia: Fix Window icon loading --- src/Ryujinx/Headless/Ryujinx.bmp | Bin 9354 -> 3978 bytes src/Ryujinx/Headless/WindowBase.cs | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx/Headless/Ryujinx.bmp b/src/Ryujinx/Headless/Ryujinx.bmp index 1daa7ce9406ae1ad56b4e697582c3e746a52da7e..36bf2f8ac129d43565ea345df7bb33b3b86e251b 100644 GIT binary patch literal 3978 zcmb`}dr(x@836E)i8v*#uV|(=srEUUO#X6Nz+k7flXlualy=f2{i8{cec#6}%kHv@ zCb*+1J`pCdF)>d5$%B}X1vMce0Txtr#z!QXaS{^{d?1<&q8Jr*&-wbDyL(|5xauFd zeE3^!&)jHelc0Wcjb^G@1S$}-+>jxfxp*nl){{u`({pClTM-V|BLGa#0 zi#*IVX^3GmxO|mqN*{Y)X=iz%BX_{l%ziXE;c`%K@@#pSjk#AC$`|4&9g3se%(|3T z_RsJ!Djq$3M}GkYu|068>PcvNJqNmK7Q?v@7sFYK^ROv>I9L1E%rkZQ`90CR(CNCI%AWe1s@^(l)rn93ytbyb z@X?Q2UxfEM{&Ro%USpb>soPOD(6=Wa&erA6?XAoE-|0H*V0SbdyP_8CrZ}-HAGgMq z4F!*cvsWAq!J1h4JzeiDo~`uL`li&@$Wx4-8iqxs9DAaR9_p>ntx0&o%+}4$`h2Wv zDMH`jkXCXuY$-V&hDa=8?)FeGf6Kx59l3|dO|Ivt*-YjKWCjom?TEegc`L}gZMY)O z&iX~V>Fo$&@!_BrxF5Hb_E9_8ZM2VC$W5-LK8`aB9f-7+Cxw{^s;zF?P?)}sK$^Sy z;07q9rd~qvkMF;S=X!ggdSnDdPeo$n9^l~J&(mFDirOag6sCyosP(p}?P;-|6DHSdYnWm*)^jp5;RdMAK}$(zIVjgyCdFK7 zFrGp0q^ZhHHrI~v3sCe?)cOpR98$)9z;4eV6h0u0K!@O>X**tw(SFDb05aEy@k5?;ZN? z8+;W-PUs@d5Aiihj6WgAILK{eQuq#Vi&Vq3qQJ!3iavG*E@7am3D?D3$=r+_#pp-h zv302Qb)h_r8ohHZ$}pRyUgN)2eqdqs67wscv0srHp>WGB{C70Yv&}(}Hgc=Yj|0L; zT~9H0mXKM!55sRnNBcIlwjxiJOlb(oWx`#c{=oCSH+iW#z$(=vY@gc3!xUS|YgF5L z8TD~t@r^r~b{FO@bK>9xQ{2A`zFlMH@#bhc?JWFb1Gsvo-Jag%tG?V6A zq*}h9@Hw1Dwas*R(p^#8;5c*4o!lICN0`O)I-f#@jl9*AXOv+I#ROfDH@m`FB(uO~yzMW3?U&vhRJ;kTn{rGR23;#>r zY`2VA_GG?yGlQFUpzrwRwDmMRNu1f|1KKV5jwUhx!Lodr0JGL}2|kRD6C^E?%G>;>KMlF(ysvpnf;eyzL2B zyn`JT9Gn|iro+$6D7W7fIWUXzT|@DxTHXVJ4V~7~*Y*vBw?#*-G4lYH~A%NPw) zoMm*jT&6veM|j44o;_&_P1RRm&*l|CzO?y(Y{BRH*cQansOrpddDv%`;_OM zL~T>gY{MR8S{p!L*J&+Md|Bi482O7rzK`+l>}{||ti B6odc( literal 9354 zcmdU!eN2^A9LLY)lBHX$!m=%_+j6d?ISFlR(`2l-^|BHWB)J5LjMbtf%yhNUAC}W) z%RgHFk+YY%_3E0gmswW8r9W_sxm9MO754&8)Us7->C@-?-21@e1>pj&=ef`PaGt~E znV)l>-#O=Z&Mkak{S0GY>-qUC%t;V1ArsUmeBPQBN7eXc8594fs#SP*(NoPWf8JR2 z>9tpe)*cBIZ_b@S%Q^QZg}oKf-eEs2NiBGX;*_5|0L&Z>UTmFR3}kG zeJM$v4ui$-j>BB=!e4D!=J?AY!6eFO z-~`BVB)s;om4CE7;J63yZG@Afcsp(Kg@4ar-xuQDpQ5`T;>YfPeAWMx;iECQ`^|qI z?#I&D|6$xU&p5H2O6iOE$64`HJ4W{*@pHJ}cgO#P{iE#}%w<6nP0RZe?wofl!Sx`763$NS`_W%**aSQKJ4{AnQ->UHf?wcXpZZ^cZyl_F z2G|a4bTV+!7u$U|IeTYh<&^KH`)@JFpY*?zv*B!yuI;0Z8G>V2{P4mm&Rjtra!EW};8+`Ro?TqS?Sy2kG|^12MQA%W-Xm%IOUkF()i zKx4WIa#h!PF~0Xe9VjpLuRnX4n7yB2PZJ56?ntI7z{|}t&YnTh Date: Wed, 11 Dec 2024 16:21:13 -0600 Subject: [PATCH 10/12] hia: Fix last used profile loading, (hopefully) fixed logo loading --- src/Ryujinx/Headless/HeadlessRyujinx.cs | 24 ++++++++++++++++-------- src/Ryujinx/Headless/Options.cs | 7 +++---- src/Ryujinx/Headless/WindowBase.cs | 2 +- src/Ryujinx/Ryujinx.csproj | 2 +- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs index 7f3f697f5..1a2fef64d 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -141,7 +141,11 @@ namespace Ryujinx.Headless string configurationPath = null; // Now load the configuration as the other subsystems are now registered - if (File.Exists(localConfigurationPath)) + if (customConfigPath != null && File.Exists(customConfigPath)) + { + configurationPath = customConfigPath; + } + else if (File.Exists(localConfigurationPath)) { configurationPath = localConfigurationPath; } @@ -149,10 +153,6 @@ namespace Ryujinx.Headless { configurationPath = appDataConfigurationPath; } - else if (customConfigPath != null && File.Exists(customConfigPath)) - { - configurationPath = customConfigPath; - } if (configurationPath == null) { @@ -411,12 +411,20 @@ namespace Ryujinx.Headless static void Load(string[] originalArgs, Options option) { Initialize(); - + + bool useLastUsedProfile = false; + if (option.InheritConfig) - option.InheritMainConfig(originalArgs, ConfigurationState.Instance, out _inputConfiguration); - + { + option.InheritMainConfig(originalArgs, ConfigurationState.Instance, out _inputConfiguration, + out useLastUsedProfile); + } + AppDataManager.Initialize(option.BaseDataDir); + if (useLastUsedProfile && AccountSaveDataManager.GetLastUsedUser().TryGet(out var profile)) + option.UserProfile = profile.Name; + // Check if keys exists. if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys"))) { diff --git a/src/Ryujinx/Headless/Options.cs b/src/Ryujinx/Headless/Options.cs index 3a807cb42..50654dc2f 100644 --- a/src/Ryujinx/Headless/Options.cs +++ b/src/Ryujinx/Headless/Options.cs @@ -17,11 +17,10 @@ namespace Ryujinx.Headless { // General - public void InheritMainConfig(string[] originalArgs, ConfigurationState configurationState, out List inputConfigs) + public void InheritMainConfig(string[] originalArgs, ConfigurationState configurationState, out List inputConfigs, out bool needsProfileSet) { - if (NeedsOverride(nameof(UserProfile)) && AccountSaveDataManager.GetLastUsedUser().TryGet(out var profile)) - UserProfile = profile.Name; - + needsProfileSet = NeedsOverride(nameof(UserProfile)); + if (NeedsOverride(nameof(IsFullscreen))) IsFullscreen = configurationState.UI.StartFullscreen; diff --git a/src/Ryujinx/Headless/WindowBase.cs b/src/Ryujinx/Headless/WindowBase.cs index f318494ce..21bee368a 100644 --- a/src/Ryujinx/Headless/WindowBase.cs +++ b/src/Ryujinx/Headless/WindowBase.cs @@ -137,7 +137,7 @@ namespace Ryujinx.Headless private void SetWindowIcon() { - Stream iconStream = typeof(WindowBase).Assembly.GetManifestResourceStream("Ryujinx.Ryujinx.bmp"); + Stream iconStream = typeof(Program).Assembly.GetManifestResourceStream("HeadlessLogo"); byte[] iconBytes = new byte[iconStream!.Length]; if (iconStream.Read(iconBytes, 0, iconBytes.Length) != iconBytes.Length) diff --git a/src/Ryujinx/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj index 8412044c7..561bbc646 100644 --- a/src/Ryujinx/Ryujinx.csproj +++ b/src/Ryujinx/Ryujinx.csproj @@ -163,7 +163,7 @@ - + From 657bd4a7d0a6bdcd1807137a421f4a1544f522c9 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Sat, 14 Dec 2024 00:24:39 -0600 Subject: [PATCH 11/12] missed this --- .github/workflows/nightly_pr_comment.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/nightly_pr_comment.yml b/.github/workflows/nightly_pr_comment.yml index 85a6e2de4..0b6c86685 100644 --- a/.github/workflows/nightly_pr_comment.yml +++ b/.github/workflows/nightly_pr_comment.yml @@ -38,20 +38,15 @@ jobs: return core.error(`No artifacts found`); } let body = `Download the artifacts for this pull request:\n`; - let hidden_headless_artifacts = `\n\n
GUI-less\n`; let hidden_debug_artifacts = `\n\n
Only for Developers\n`; for (const art of artifacts) { if(art.name.includes('Debug')) { hidden_debug_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`; - } else if(art.name.includes('nogui-ryujinx')) { - hidden_headless_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`; } else { body += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`; } } - hidden_headless_artifacts += `\n
`; hidden_debug_artifacts += `\n
`; - body += hidden_headless_artifacts; body += hidden_debug_artifacts; const {data: comments} = await github.rest.issues.listComments({repo, owner, issue_number}); From 66fdad6d6411c2a0f294523249d6cc9720ddb114 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Sat, 14 Dec 2024 04:41:43 -0600 Subject: [PATCH 12/12] hia: prevent infinite loading --- src/Ryujinx/Headless/HeadlessRyujinx.Init.cs | 361 +++++++++++++++++ src/Ryujinx/Headless/HeadlessRyujinx.cs | 390 +------------------ src/Ryujinx/Headless/Options.cs | 9 +- 3 files changed, 382 insertions(+), 378 deletions(-) create mode 100644 src/Ryujinx/Headless/HeadlessRyujinx.Init.cs diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs new file mode 100644 index 000000000..e43c8a1ca --- /dev/null +++ b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs @@ -0,0 +1,361 @@ +using DiscordRPC; +using LibHac.Tools.FsSystem; +using Ryujinx.Audio.Backends.SDL2; +using Ryujinx.Ava; +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Common.Configuration.Hid.Controller; +using Ryujinx.Common.Configuration.Hid.Controller.Motion; +using Ryujinx.Common.Configuration.Hid.Keyboard; +using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Multithreading; +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; +using System.Text.Json; +using System.Threading.Tasks; +using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId; +using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; +using Key = Ryujinx.Common.Configuration.Hid.Key; + +namespace Ryujinx.Headless +{ + public partial class HeadlessRyujinx + { + public static void Initialize() + { + // Ensure Discord presence timestamp begins at the absolute start of when Ryujinx is launched + DiscordIntegrationModule.StartedAt = Timestamps.Now; + + // Delete backup files after updating. + Task.Run(Updater.CleanupUpdate); + + // Hook unhandled exception and process exit events. + AppDomain.CurrentDomain.UnhandledException += (sender, e) + => Program.ProcessUnhandledException(sender, e.ExceptionObject as Exception, e.IsTerminating); + AppDomain.CurrentDomain.ProcessExit += (_, _) => Program.Exit(); + + // Initialize the configuration. + ConfigurationState.Initialize(); + + // Initialize Discord integration. + DiscordIntegrationModule.Initialize(); + + // Logging system information. + Program.PrintSystemInfo(); + } + + private static InputConfig HandlePlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index) + { + if (inputId == null) + { + if (index == PlayerIndex.Player1) + { + Logger.Info?.Print(LogClass.Application, $"{index} not configured, defaulting to default keyboard."); + + // Default to keyboard + inputId = "0"; + } + else + { + Logger.Info?.Print(LogClass.Application, $"{index} not configured"); + + return null; + } + } + + IGamepad gamepad = _inputManager.KeyboardDriver.GetGamepad(inputId); + + bool isKeyboard = true; + + if (gamepad == null) + { + gamepad = _inputManager.GamepadDriver.GetGamepad(inputId); + isKeyboard = false; + + if (gamepad == null) + { + Logger.Error?.Print(LogClass.Application, $"{index} gamepad not found (\"{inputId}\")"); + + return null; + } + } + + string gamepadName = gamepad.Name; + + gamepad.Dispose(); + + InputConfig config; + + if (inputProfileName == null || inputProfileName.Equals("default")) + { + if (isKeyboard) + { + config = new StandardKeyboardInputConfig + { + Version = InputConfig.CurrentVersion, + Backend = InputBackendType.WindowKeyboard, + Id = null, + ControllerType = ControllerType.JoyconPair, + LeftJoycon = new LeftJoyconCommonConfig + { + DpadUp = Key.Up, + DpadDown = Key.Down, + DpadLeft = Key.Left, + DpadRight = Key.Right, + ButtonMinus = Key.Minus, + ButtonL = Key.E, + ButtonZl = Key.Q, + ButtonSl = Key.Unbound, + ButtonSr = Key.Unbound, + }, + + LeftJoyconStick = new JoyconConfigKeyboardStick + { + StickUp = Key.W, + StickDown = Key.S, + StickLeft = Key.A, + StickRight = Key.D, + StickButton = Key.F, + }, + + RightJoycon = new RightJoyconCommonConfig + { + ButtonA = Key.Z, + ButtonB = Key.X, + ButtonX = Key.C, + ButtonY = Key.V, + ButtonPlus = Key.Plus, + ButtonR = Key.U, + ButtonZr = Key.O, + ButtonSl = Key.Unbound, + ButtonSr = Key.Unbound, + }, + + RightJoyconStick = new JoyconConfigKeyboardStick + { + StickUp = Key.I, + StickDown = Key.K, + StickLeft = Key.J, + StickRight = Key.L, + StickButton = Key.H, + }, + }; + } + else + { + bool isNintendoStyle = gamepadName.Contains("Nintendo"); + + config = new StandardControllerInputConfig + { + Version = InputConfig.CurrentVersion, + Backend = InputBackendType.GamepadSDL2, + Id = null, + ControllerType = ControllerType.JoyconPair, + DeadzoneLeft = 0.1f, + DeadzoneRight = 0.1f, + RangeLeft = 1.0f, + RangeRight = 1.0f, + TriggerThreshold = 0.5f, + LeftJoycon = new LeftJoyconCommonConfig + { + DpadUp = ConfigGamepadInputId.DpadUp, + DpadDown = ConfigGamepadInputId.DpadDown, + DpadLeft = ConfigGamepadInputId.DpadLeft, + DpadRight = ConfigGamepadInputId.DpadRight, + ButtonMinus = ConfigGamepadInputId.Minus, + ButtonL = ConfigGamepadInputId.LeftShoulder, + ButtonZl = ConfigGamepadInputId.LeftTrigger, + ButtonSl = ConfigGamepadInputId.Unbound, + ButtonSr = ConfigGamepadInputId.Unbound, + }, + + LeftJoyconStick = new JoyconConfigControllerStick + { + Joystick = ConfigStickInputId.Left, + StickButton = ConfigGamepadInputId.LeftStick, + InvertStickX = false, + InvertStickY = false, + Rotate90CW = false, + }, + + RightJoycon = new RightJoyconCommonConfig + { + ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B, + ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A, + ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y, + ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X, + ButtonPlus = ConfigGamepadInputId.Plus, + ButtonR = ConfigGamepadInputId.RightShoulder, + ButtonZr = ConfigGamepadInputId.RightTrigger, + ButtonSl = ConfigGamepadInputId.Unbound, + ButtonSr = ConfigGamepadInputId.Unbound, + }, + + RightJoyconStick = new JoyconConfigControllerStick + { + Joystick = ConfigStickInputId.Right, + StickButton = ConfigGamepadInputId.RightStick, + InvertStickX = false, + InvertStickY = false, + Rotate90CW = false, + }, + + Motion = new StandardMotionConfigController + { + MotionBackend = MotionInputBackendType.GamepadDriver, + EnableMotion = true, + Sensitivity = 100, + GyroDeadzone = 1, + }, + Rumble = new RumbleConfigController + { + StrongRumble = 1f, + WeakRumble = 1f, + EnableRumble = false, + }, + }; + } + } + else + { + string profileBasePath; + + if (isKeyboard) + { + profileBasePath = Path.Combine(AppDataManager.ProfilesDirPath, "keyboard"); + } + else + { + profileBasePath = Path.Combine(AppDataManager.ProfilesDirPath, "controller"); + } + + string path = Path.Combine(profileBasePath, inputProfileName + ".json"); + + if (!File.Exists(path)) + { + Logger.Error?.Print(LogClass.Application, $"Input profile \"{inputProfileName}\" not found for \"{inputId}\""); + + return null; + } + + try + { + config = JsonHelper.DeserializeFromFile(path, _serializerContext.InputConfig); + } + catch (JsonException) + { + Logger.Error?.Print(LogClass.Application, $"Input profile \"{inputProfileName}\" parsing failed for \"{inputId}\""); + + return null; + } + } + + config.Id = inputId; + config.PlayerIndex = index; + + string inputTypeName = isKeyboard ? "Keyboard" : "Gamepad"; + + Logger.Info?.Print(LogClass.Application, $"{config.PlayerIndex} configured with {inputTypeName} \"{config.Id}\""); + + // If both stick ranges are 0 (usually indicative of an outdated profile load) then both sticks will be set to 1.0. + if (config is StandardControllerInputConfig controllerConfig) + { + if (controllerConfig.RangeLeft <= 0.0f && controllerConfig.RangeRight <= 0.0f) + { + controllerConfig.RangeLeft = 1.0f; + controllerConfig.RangeRight = 1.0f; + + Logger.Info?.Print(LogClass.Application, $"{config.PlayerIndex} stick range reset. Save the profile now to update your configuration"); + } + } + + return config; + } + + private static IRenderer CreateRenderer(Options options, WindowBase window) + { + if (options.GraphicsBackend == GraphicsBackend.Vulkan && window is VulkanWindow vulkanWindow) + { + string preferredGpuId = string.Empty; + Vk api = Vk.GetApi(); + + if (!string.IsNullOrEmpty(options.PreferredGPUVendor)) + { + string preferredGpuVendor = options.PreferredGPUVendor.ToLowerInvariant(); + var devices = VulkanRenderer.GetPhysicalDevices(api); + + foreach (var device in devices) + { + if (device.Vendor.ToLowerInvariant() == preferredGpuVendor) + { + preferredGpuId = device.Id; + break; + } + } + } + + return new VulkanRenderer( + api, + (instance, vk) => new SurfaceKHR((ulong)(vulkanWindow.CreateWindowSurface(instance.Handle))), + vulkanWindow.GetRequiredInstanceExtensions, + preferredGpuId); + } + + return new OpenGLRenderer(); + } + + private static Switch InitializeEmulationContext(WindowBase window, IRenderer renderer, Options options) + { + BackendThreading threadingMode = options.BackendThreading; + + bool threadedGAL = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading); + + if (threadedGAL) + { + renderer = new ThreadedRenderer(renderer); + } + + HLEConfiguration configuration = new(_virtualFileSystem, + _libHacHorizonManager, + _contentManager, + _accountManager, + _userChannelPersistence, + renderer, + new SDL2HardwareDeviceDriver(), + options.DramSize, + window, + options.SystemLanguage, + options.SystemRegion, + options.VSyncMode, + !options.DisableDockedMode, + !options.DisablePTC, + options.EnableInternetAccess, + !options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, + options.FsGlobalAccessLogMode, + options.SystemTimeOffset, + options.SystemTimeZone, + options.MemoryManagerMode, + options.IgnoreMissingServices, + options.AspectRatio, + options.AudioVolume, + options.UseHypervisor ?? true, + options.MultiplayerLanInterfaceId, + Common.Configuration.Multiplayer.MultiplayerMode.Disabled, + false, + string.Empty, + string.Empty, + options.CustomVSyncInterval); + + return new Switch(configuration); + } + } +} diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs index 1a2fef64d..0ba26c8a3 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -1,18 +1,9 @@ -using Avalonia.Threading; using CommandLine; -using DiscordRPC; using Gommon; -using LibHac.Tools.FsSystem; -using Ryujinx.Audio.Backends.SDL2; using Ryujinx.Ava; -using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; -using Ryujinx.Common.Configuration.Hid.Controller; -using Ryujinx.Common.Configuration.Hid.Controller.Motion; -using Ryujinx.Common.Configuration.Hid.Keyboard; using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.Logging; using Ryujinx.Common.Logging.Targets; @@ -20,11 +11,8 @@ using Ryujinx.Common.SystemInterop; using Ryujinx.Common.Utilities; using Ryujinx.Cpu; using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.GAL.Multithreading; using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu.Shader; -using Ryujinx.Graphics.OpenGL; -using Ryujinx.Graphics.Vulkan; using Ryujinx.Graphics.Vulkan.MoltenVK; using Ryujinx.HLE; using Ryujinx.HLE.FileSystem; @@ -34,24 +22,15 @@ using Ryujinx.Input; using Ryujinx.Input.HLE; using Ryujinx.Input.SDL2; using Ryujinx.SDL2.Common; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common; using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Helper; -using Silk.NET.Vulkan; using System; using System.Collections.Generic; using System.IO; -using System.Text.Json; using System.Threading; -using System.Threading.Tasks; -using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId; -using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; -using Key = Ryujinx.Common.Configuration.Hid.Key; namespace Ryujinx.Headless { - public class HeadlessRyujinx + public partial class HeadlessRyujinx { private static VirtualFileSystem _virtualFileSystem; private static ContentManager _contentManager; @@ -62,35 +41,12 @@ namespace Ryujinx.Headless private static Switch _emulationContext; private static WindowBase _window; private static WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution; - private static List _inputConfiguration; + private static List _inputConfiguration = []; private static bool _enableKeyboard; private static bool _enableMouse; private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - public static void Initialize() - { - // Ensure Discord presence timestamp begins at the absolute start of when Ryujinx is launched - DiscordIntegrationModule.StartedAt = Timestamps.Now; - - // Delete backup files after updating. - Task.Run(Updater.CleanupUpdate); - - // Hook unhandled exception and process exit events. - AppDomain.CurrentDomain.UnhandledException += (sender, e) - => Program.ProcessUnhandledException(sender, e.ExceptionObject as Exception, e.IsTerminating); - AppDomain.CurrentDomain.ProcessExit += (_, _) => Program.Exit(); - - // Initialize the configuration. - ConfigurationState.Initialize(); - - // Initialize Discord integration. - DiscordIntegrationModule.Initialize(); - - // Logging system information. - Program.PrintSystemInfo(); - } - public static void Entrypoint(string[] args) { // Make process DPI aware for proper window sizing on high-res screens. @@ -180,234 +136,6 @@ namespace Ryujinx.Headless } } - private static InputConfig HandlePlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index) - { - if (inputId == null) - { - if (index == PlayerIndex.Player1) - { - Logger.Info?.Print(LogClass.Application, $"{index} not configured, defaulting to default keyboard."); - - // Default to keyboard - inputId = "0"; - } - else - { - Logger.Info?.Print(LogClass.Application, $"{index} not configured"); - - return null; - } - } - - IGamepad gamepad = _inputManager.KeyboardDriver.GetGamepad(inputId); - - bool isKeyboard = true; - - if (gamepad == null) - { - gamepad = _inputManager.GamepadDriver.GetGamepad(inputId); - isKeyboard = false; - - if (gamepad == null) - { - Logger.Error?.Print(LogClass.Application, $"{index} gamepad not found (\"{inputId}\")"); - - return null; - } - } - - string gamepadName = gamepad.Name; - - gamepad.Dispose(); - - InputConfig config; - - if (inputProfileName == null || inputProfileName.Equals("default")) - { - if (isKeyboard) - { - config = new StandardKeyboardInputConfig - { - Version = InputConfig.CurrentVersion, - Backend = InputBackendType.WindowKeyboard, - Id = null, - ControllerType = ControllerType.JoyconPair, - LeftJoycon = new LeftJoyconCommonConfig - { - DpadUp = Key.Up, - DpadDown = Key.Down, - DpadLeft = Key.Left, - DpadRight = Key.Right, - ButtonMinus = Key.Minus, - ButtonL = Key.E, - ButtonZl = Key.Q, - ButtonSl = Key.Unbound, - ButtonSr = Key.Unbound, - }, - - LeftJoyconStick = new JoyconConfigKeyboardStick - { - StickUp = Key.W, - StickDown = Key.S, - StickLeft = Key.A, - StickRight = Key.D, - StickButton = Key.F, - }, - - RightJoycon = new RightJoyconCommonConfig - { - ButtonA = Key.Z, - ButtonB = Key.X, - ButtonX = Key.C, - ButtonY = Key.V, - ButtonPlus = Key.Plus, - ButtonR = Key.U, - ButtonZr = Key.O, - ButtonSl = Key.Unbound, - ButtonSr = Key.Unbound, - }, - - RightJoyconStick = new JoyconConfigKeyboardStick - { - StickUp = Key.I, - StickDown = Key.K, - StickLeft = Key.J, - StickRight = Key.L, - StickButton = Key.H, - }, - }; - } - else - { - bool isNintendoStyle = gamepadName.Contains("Nintendo"); - - config = new StandardControllerInputConfig - { - Version = InputConfig.CurrentVersion, - Backend = InputBackendType.GamepadSDL2, - Id = null, - ControllerType = ControllerType.JoyconPair, - DeadzoneLeft = 0.1f, - DeadzoneRight = 0.1f, - RangeLeft = 1.0f, - RangeRight = 1.0f, - TriggerThreshold = 0.5f, - LeftJoycon = new LeftJoyconCommonConfig - { - DpadUp = ConfigGamepadInputId.DpadUp, - DpadDown = ConfigGamepadInputId.DpadDown, - DpadLeft = ConfigGamepadInputId.DpadLeft, - DpadRight = ConfigGamepadInputId.DpadRight, - ButtonMinus = ConfigGamepadInputId.Minus, - ButtonL = ConfigGamepadInputId.LeftShoulder, - ButtonZl = ConfigGamepadInputId.LeftTrigger, - ButtonSl = ConfigGamepadInputId.Unbound, - ButtonSr = ConfigGamepadInputId.Unbound, - }, - - LeftJoyconStick = new JoyconConfigControllerStick - { - Joystick = ConfigStickInputId.Left, - StickButton = ConfigGamepadInputId.LeftStick, - InvertStickX = false, - InvertStickY = false, - Rotate90CW = false, - }, - - RightJoycon = new RightJoyconCommonConfig - { - ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B, - ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A, - ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y, - ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X, - ButtonPlus = ConfigGamepadInputId.Plus, - ButtonR = ConfigGamepadInputId.RightShoulder, - ButtonZr = ConfigGamepadInputId.RightTrigger, - ButtonSl = ConfigGamepadInputId.Unbound, - ButtonSr = ConfigGamepadInputId.Unbound, - }, - - RightJoyconStick = new JoyconConfigControllerStick - { - Joystick = ConfigStickInputId.Right, - StickButton = ConfigGamepadInputId.RightStick, - InvertStickX = false, - InvertStickY = false, - Rotate90CW = false, - }, - - Motion = new StandardMotionConfigController - { - MotionBackend = MotionInputBackendType.GamepadDriver, - EnableMotion = true, - Sensitivity = 100, - GyroDeadzone = 1, - }, - Rumble = new RumbleConfigController - { - StrongRumble = 1f, - WeakRumble = 1f, - EnableRumble = false, - }, - }; - } - } - else - { - string profileBasePath; - - if (isKeyboard) - { - profileBasePath = Path.Combine(AppDataManager.ProfilesDirPath, "keyboard"); - } - else - { - profileBasePath = Path.Combine(AppDataManager.ProfilesDirPath, "controller"); - } - - string path = Path.Combine(profileBasePath, inputProfileName + ".json"); - - if (!File.Exists(path)) - { - Logger.Error?.Print(LogClass.Application, $"Input profile \"{inputProfileName}\" not found for \"{inputId}\""); - - return null; - } - - try - { - config = JsonHelper.DeserializeFromFile(path, _serializerContext.InputConfig); - } - catch (JsonException) - { - Logger.Error?.Print(LogClass.Application, $"Input profile \"{inputProfileName}\" parsing failed for \"{inputId}\""); - - return null; - } - } - - config.Id = inputId; - config.PlayerIndex = index; - - string inputTypeName = isKeyboard ? "Keyboard" : "Gamepad"; - - Logger.Info?.Print(LogClass.Application, $"{config.PlayerIndex} configured with {inputTypeName} \"{config.Id}\""); - - // If both stick ranges are 0 (usually indicative of an outdated profile load) then both sticks will be set to 1.0. - if (config is StandardControllerInputConfig controllerConfig) - { - if (controllerConfig.RangeLeft <= 0.0f && controllerConfig.RangeRight <= 0.0f) - { - controllerConfig.RangeLeft = 1.0f; - controllerConfig.RangeRight = 1.0f; - - Logger.Info?.Print(LogClass.Application, $"{config.PlayerIndex} stick range reset. Save the profile now to update your configuration"); - } - } - - return config; - } - static void Load(string[] originalArgs, Options option) { Initialize(); @@ -416,8 +144,7 @@ namespace Ryujinx.Headless if (option.InheritConfig) { - option.InheritMainConfig(originalArgs, ConfigurationState.Instance, out _inputConfiguration, - out useLastUsedProfile); + option.InheritMainConfig(originalArgs, ConfigurationState.Instance, out useLastUsedProfile); } AppDataManager.Initialize(option.BaseDataDir); @@ -450,7 +177,7 @@ namespace Ryujinx.Headless _inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver()); - GraphicsConfig.EnableShaderCache = true; + GraphicsConfig.EnableShaderCache = !option.DisableShaderCache; if (OperatingSystem.IsMacOS()) { @@ -461,15 +188,13 @@ namespace Ryujinx.Headless } } - IGamepad gamepad; - if (option.ListInputIds) { Logger.Info?.Print(LogClass.Application, "Input Ids:"); foreach (string id in _inputManager.KeyboardDriver.GamepadsIds) { - gamepad = _inputManager.KeyboardDriver.GetGamepad(id); + IGamepad gamepad = _inputManager.KeyboardDriver.GetGamepad(id); Logger.Info?.Print(LogClass.Application, $"- {id} (\"{gamepad.Name}\")"); @@ -478,7 +203,7 @@ namespace Ryujinx.Headless foreach (string id in _inputManager.GamepadDriver.GamepadsIds) { - gamepad = _inputManager.GamepadDriver.GetGamepad(id); + IGamepad gamepad = _inputManager.GamepadDriver.GetGamepad(id); Logger.Info?.Print(LogClass.Application, $"- {id} (\"{gamepad.Name}\")"); @@ -495,7 +220,7 @@ namespace Ryujinx.Headless return; } - _inputConfiguration = new List(); + _inputConfiguration ??= []; _enableKeyboard = option.EnableKeyboard; _enableMouse = option.EnableMouse; @@ -508,19 +233,17 @@ namespace Ryujinx.Headless _inputConfiguration.Add(inputConfig); } } - - if (!option.InheritConfig) - { - LoadPlayerConfiguration(option.InputProfile1Name, option.InputId1, PlayerIndex.Player1); - LoadPlayerConfiguration(option.InputProfile2Name, option.InputId2, PlayerIndex.Player2); - LoadPlayerConfiguration(option.InputProfile3Name, option.InputId3, PlayerIndex.Player3); - LoadPlayerConfiguration(option.InputProfile4Name, option.InputId4, PlayerIndex.Player4); - LoadPlayerConfiguration(option.InputProfile5Name, option.InputId5, PlayerIndex.Player5); - LoadPlayerConfiguration(option.InputProfile6Name, option.InputId6, PlayerIndex.Player6); - LoadPlayerConfiguration(option.InputProfile7Name, option.InputId7, PlayerIndex.Player7); - LoadPlayerConfiguration(option.InputProfile8Name, option.InputId8, PlayerIndex.Player8); - LoadPlayerConfiguration(option.InputProfileHandheldName, option.InputIdHandheld, PlayerIndex.Handheld); - } + + LoadPlayerConfiguration(option.InputProfile1Name, option.InputId1, PlayerIndex.Player1); + LoadPlayerConfiguration(option.InputProfile2Name, option.InputId2, PlayerIndex.Player2); + LoadPlayerConfiguration(option.InputProfile3Name, option.InputId3, PlayerIndex.Player3); + LoadPlayerConfiguration(option.InputProfile4Name, option.InputId4, PlayerIndex.Player4); + LoadPlayerConfiguration(option.InputProfile5Name, option.InputId5, PlayerIndex.Player5); + LoadPlayerConfiguration(option.InputProfile6Name, option.InputId6, PlayerIndex.Player6); + LoadPlayerConfiguration(option.InputProfile7Name, option.InputId7, PlayerIndex.Player7); + LoadPlayerConfiguration(option.InputProfile8Name, option.InputId8, PlayerIndex.Player8); + LoadPlayerConfiguration(option.InputProfileHandheldName, option.InputIdHandheld, PlayerIndex.Handheld); + if (_inputConfiguration.Count == 0) { @@ -616,83 +339,6 @@ namespace Ryujinx.Headless : new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet); } - private static IRenderer CreateRenderer(Options options, WindowBase window) - { - if (options.GraphicsBackend == GraphicsBackend.Vulkan && window is VulkanWindow vulkanWindow) - { - string preferredGpuId = string.Empty; - Vk api = Vk.GetApi(); - - if (!string.IsNullOrEmpty(options.PreferredGPUVendor)) - { - string preferredGpuVendor = options.PreferredGPUVendor.ToLowerInvariant(); - var devices = VulkanRenderer.GetPhysicalDevices(api); - - foreach (var device in devices) - { - if (device.Vendor.ToLowerInvariant() == preferredGpuVendor) - { - preferredGpuId = device.Id; - break; - } - } - } - - return new VulkanRenderer( - api, - (instance, vk) => new SurfaceKHR((ulong)(vulkanWindow.CreateWindowSurface(instance.Handle))), - vulkanWindow.GetRequiredInstanceExtensions, - preferredGpuId); - } - - return new OpenGLRenderer(); - } - - private static Switch InitializeEmulationContext(WindowBase window, IRenderer renderer, Options options) - { - BackendThreading threadingMode = options.BackendThreading; - - bool threadedGAL = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading); - - if (threadedGAL) - { - renderer = new ThreadedRenderer(renderer); - } - - HLEConfiguration configuration = new(_virtualFileSystem, - _libHacHorizonManager, - _contentManager, - _accountManager, - _userChannelPersistence, - renderer, - new SDL2HardwareDeviceDriver(), - options.DramSize, - window, - options.SystemLanguage, - options.SystemRegion, - options.VSyncMode, - !options.DisableDockedMode, - !options.DisablePTC, - options.EnableInternetAccess, - !options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, - options.FsGlobalAccessLogMode, - options.SystemTimeOffset, - options.SystemTimeZone, - options.MemoryManagerMode, - options.IgnoreMissingServices, - options.AspectRatio, - options.AudioVolume, - options.UseHypervisor ?? true, - options.MultiplayerLanInterfaceId, - Common.Configuration.Multiplayer.MultiplayerMode.Disabled, - false, - string.Empty, - string.Empty, - options.CustomVSyncInterval); - - return new Switch(configuration); - } - private static void ExecutionEntrypoint() { if (OperatingSystem.IsWindows()) diff --git a/src/Ryujinx/Headless/Options.cs b/src/Ryujinx/Headless/Options.cs index 50654dc2f..0dd4216f0 100644 --- a/src/Ryujinx/Headless/Options.cs +++ b/src/Ryujinx/Headless/Options.cs @@ -15,9 +15,7 @@ namespace Ryujinx.Headless { public class Options { - // General - - public void InheritMainConfig(string[] originalArgs, ConfigurationState configurationState, out List inputConfigs, out bool needsProfileSet) + public void InheritMainConfig(string[] originalArgs, ConfigurationState configurationState, out bool needsProfileSet) { needsProfileSet = NeedsOverride(nameof(UserProfile)); @@ -152,9 +150,6 @@ namespace Ryujinx.Headless if (NeedsOverride(nameof(IgnoreControllerApplet))) IgnoreControllerApplet = configurationState.IgnoreApplet; - - - inputConfigs = configurationState.Hid.InputConfig; return; @@ -164,6 +159,8 @@ namespace Ryujinx.Headless typeof(Options)!.GetProperty(propertyName)!.GetCustomAttribute()!.LongName; } + // General + [Option("use-main-config", Required = false, Default = false, HelpText = "Use the settings from what was configured via the UI.")] public bool InheritConfig { get; set; }