Compare commits
4 Commits
refactor/a
...
3737f5cefe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3737f5cefe | ||
|
|
cea3241066 | ||
|
|
0f36bb0eef | ||
|
|
44c104790b |
23
.github/workflows/build.yml
vendored
23
.github/workflows/build.yml
vendored
@@ -64,9 +64,14 @@ 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
|
||||
@@ -114,6 +119,13 @@ 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
|
||||
@@ -159,9 +171,20 @@ 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'
|
||||
|
||||
20
.github/workflows/canary.yml
vendored
20
.github/workflows/canary.yml
vendored
@@ -104,6 +104,7 @@ 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'
|
||||
@@ -112,6 +113,11 @@ 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
|
||||
@@ -122,6 +128,12 @@ 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)
|
||||
@@ -167,7 +179,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,release_output/*AppImage*"
|
||||
#artifacts: "release_output/*.tar.gz,release_output/*.zip/*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
|
||||
@@ -226,11 +238,15 @@ 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"
|
||||
artifacts: "publish_ava/*.tar.gz, publish_headless/*.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
|
||||
|
||||
5
.github/workflows/nightly_pr_comment.yml
vendored
5
.github/workflows/nightly_pr_comment.yml
vendored
@@ -38,15 +38,20 @@ jobs:
|
||||
return core.error(`No artifacts found`);
|
||||
}
|
||||
let body = `Download the artifacts for this pull request:\n`;
|
||||
let hidden_headless_artifacts = `\n\n <details><summary>GUI-less</summary>\n`;
|
||||
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\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</details>`;
|
||||
hidden_debug_artifacts += `\n</details>`;
|
||||
body += hidden_headless_artifacts;
|
||||
body += hidden_debug_artifacts;
|
||||
|
||||
const {data: comments} = await github.rest.issues.listComments({repo, owner, issue_number});
|
||||
|
||||
17
.github/workflows/release.yml
vendored
17
.github/workflows/release.yml
vendored
@@ -103,6 +103,7 @@ 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'
|
||||
@@ -111,6 +112,11 @@ 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)
|
||||
@@ -157,6 +163,11 @@ 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
|
||||
@@ -222,11 +233,15 @@ 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"
|
||||
artifacts: "publish/*.tar.gz, publish_headless/*.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
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -175,6 +175,3 @@ PublishProfiles/
|
||||
|
||||
# Glade backup files
|
||||
*.glade~
|
||||
|
||||
# Ignore MacOS Attribute Files
|
||||
._*
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
||||
<PackageVersion Include="Gommon" Version="2.6.8" />
|
||||
<PackageVersion Include="Gommon" Version="2.6.6" />
|
||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||
|
||||
@@ -57,6 +57,8 @@ 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}"
|
||||
@@ -202,6 +204,10 @@ 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
|
||||
|
||||
@@ -14,7 +14,7 @@ if [ -z "$RYUJINX_BIN" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
COMMAND="env LANG=C.UTF-8 DOTNET_EnableAlternateStackCheck=1"
|
||||
COMMAND="env DOTNET_EnableAlternateStackCheck=1"
|
||||
|
||||
if command -v gamemoderun > /dev/null 2>&1; then
|
||||
COMMAND="$COMMAND gamemoderun"
|
||||
|
||||
@@ -40,11 +40,11 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.2</string>
|
||||
<string>1.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.2.0</string>
|
||||
<string>1.1.0</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>CSResourcesFileMapped</key>
|
||||
|
||||
@@ -17,7 +17,7 @@ error_handler() {
|
||||
set the button_pressed to the button returned of the result
|
||||
|
||||
if the button_pressed is \"Open Download Page\" then
|
||||
open location \"https://ryujinx.app/download\"
|
||||
open location \"https://ryujinx.org/download\"
|
||||
end if
|
||||
"""
|
||||
|
||||
@@ -54,4 +54,4 @@ if [ "$#" -le 3 ]; then
|
||||
open -a "$INSTALL_DIRECTORY"
|
||||
else
|
||||
open -a "$INSTALL_DIRECTORY" --args "${APP_ARGUMENTS[@]}"
|
||||
fi
|
||||
fi
|
||||
@@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -36,9 +36,9 @@ namespace ARMeilleure.Common
|
||||
/// </summary>
|
||||
/// <param name="address">Guest address</param>
|
||||
/// <returns>Value of the <see cref="AddressTableLevel"/> from the specified guest <paramref name="address"/></returns>
|
||||
public long GetValue(ulong address)
|
||||
public int GetValue(ulong address)
|
||||
{
|
||||
return (long)((address & Mask) >> Index);
|
||||
return (int)((address & Mask) >> Index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace ARMeilleure.Instructions
|
||||
EmitSetTpidrEl0(context);
|
||||
return;
|
||||
case 0b11_011_1101_0000_101:
|
||||
EmitSetTpidr2El0(context);
|
||||
EmitGetTpidr2El0(context);
|
||||
return;
|
||||
|
||||
default:
|
||||
@@ -291,16 +291,5 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())), value);
|
||||
}
|
||||
|
||||
private static void EmitSetTpidr2El0(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||
|
||||
Operand value = GetIntOrZR(context, op.Rt);
|
||||
|
||||
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||
|
||||
context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidr2El0Offset())), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||
|
||||
private const uint InternalVersion = 6997; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
private const uint InternalVersion = 6992; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
|
||||
private const string ActualDir = "0";
|
||||
private const string BackupDir = "1";
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
namespace Ryujinx.Common.Configuration.Hid.Controller
|
||||
{
|
||||
public class JoyconConfigControllerStick<TButton, TStick>
|
||||
where TButton : unmanaged
|
||||
where TStick : unmanaged
|
||||
public class JoyconConfigControllerStick<TButton, TStick> where TButton : unmanaged where TStick : unmanaged
|
||||
{
|
||||
public TStick Joystick { get; set; }
|
||||
public bool InvertStickX { get; set; }
|
||||
|
||||
@@ -4,7 +4,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
@@ -158,16 +157,21 @@ namespace Ryujinx.Common.Logging
|
||||
_time.Restart();
|
||||
}
|
||||
|
||||
private static ILogTarget GetTarget(string targetName)
|
||||
=> _logTargets.FirstOrDefault(target => target.Name.Equals(targetName));
|
||||
private static ILogTarget GetTarget(string targetName)
|
||||
{
|
||||
foreach (var target in _logTargets)
|
||||
{
|
||||
if (target.Name.Equals(targetName))
|
||||
{
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void AddTarget(ILogTarget target)
|
||||
{
|
||||
if (_logTargets.Any(t => t.Name == target.Name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_logTargets.Add(target);
|
||||
|
||||
Updated += target.Log;
|
||||
|
||||
@@ -27,7 +27,11 @@ namespace Ryujinx.Common.Logging.Targets
|
||||
|
||||
private readonly int _overflowTimeout;
|
||||
|
||||
string ILogTarget.Name => _target.Name;
|
||||
string ILogTarget.Name { get => _target.Name; }
|
||||
|
||||
public AsyncLogTargetWrapper(ILogTarget target)
|
||||
: this(target, -1)
|
||||
{ }
|
||||
|
||||
public AsyncLogTargetWrapper(ILogTarget target, int queueLimit = -1, AsyncLogTargetOverflowAction overflowAction = AsyncLogTargetOverflowAction.Block)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -238,7 +238,7 @@ namespace ARMeilleure.Common
|
||||
{
|
||||
TEntry* page = GetPage(address);
|
||||
|
||||
long index = Levels[^1].GetValue(address);
|
||||
int index = Levels[^1].GetValue(address);
|
||||
|
||||
EnsureMapped((IntPtr)(page + index));
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
|
||||
@@ -182,16 +182,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//Prevent the sum of descriptors from exceeding MaxPushDescriptors
|
||||
int totalDescriptors = 0;
|
||||
foreach (ResourceDescriptor desc in layout.Sets.First().Descriptors)
|
||||
{
|
||||
if (!reserved.Contains(desc.Binding))
|
||||
totalDescriptors += desc.Count;
|
||||
}
|
||||
if (totalDescriptors > gd.Capabilities.MaxPushDescriptors)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
|
||||
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
|
||||
<IsRoslynComponent>true</IsRoslynComponent>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -21,7 +21,6 @@ using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Path = System.IO.Path;
|
||||
|
||||
namespace Ryujinx.HLE.FileSystem
|
||||
@@ -475,74 +474,6 @@ namespace Ryujinx.HLE.FileSystem
|
||||
FinishInstallation(temporaryDirectory, registeredDirectory);
|
||||
}
|
||||
|
||||
public void InstallKeys(string keysSource, string installDirectory)
|
||||
{
|
||||
if (Directory.Exists(keysSource))
|
||||
{
|
||||
foreach (var filePath in Directory.EnumerateFiles(keysSource, "*.keys"))
|
||||
{
|
||||
VerifyKeysFile(filePath);
|
||||
File.Copy(filePath, Path.Combine(installDirectory, Path.GetFileName(filePath)), true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!File.Exists(keysSource))
|
||||
{
|
||||
throw new FileNotFoundException("Keys file does not exist.");
|
||||
}
|
||||
|
||||
FileInfo info = new(keysSource);
|
||||
|
||||
using FileStream file = File.OpenRead(keysSource);
|
||||
|
||||
switch (info.Extension)
|
||||
{
|
||||
case ".zip":
|
||||
using (ZipArchive archive = ZipFile.OpenRead(keysSource))
|
||||
{
|
||||
InstallKeysFromZip(archive, installDirectory);
|
||||
}
|
||||
break;
|
||||
case ".keys":
|
||||
VerifyKeysFile(keysSource);
|
||||
File.Copy(keysSource, Path.Combine(installDirectory, info.Name), true);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidFirmwarePackageException("Input file is not a valid key package");
|
||||
}
|
||||
}
|
||||
|
||||
private void InstallKeysFromZip(ZipArchive archive, string installDirectory)
|
||||
{
|
||||
string temporaryDirectory = Path.Combine(installDirectory, "temp");
|
||||
if (Directory.Exists(temporaryDirectory))
|
||||
{
|
||||
Directory.Delete(temporaryDirectory, true);
|
||||
}
|
||||
Directory.CreateDirectory(temporaryDirectory);
|
||||
foreach (var entry in archive.Entries)
|
||||
{
|
||||
if (Path.GetExtension(entry.FullName).Equals(".keys", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string extractDestination = Path.Combine(temporaryDirectory, entry.Name);
|
||||
entry.ExtractToFile(extractDestination, overwrite: true);
|
||||
try
|
||||
{
|
||||
VerifyKeysFile(extractDestination);
|
||||
File.Move(extractDestination, Path.Combine(installDirectory, entry.Name), true);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Directory.Delete(temporaryDirectory, true);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
Directory.Delete(temporaryDirectory, true);
|
||||
}
|
||||
|
||||
private void FinishInstallation(string temporaryDirectory, string registeredDirectory)
|
||||
{
|
||||
if (Directory.Exists(registeredDirectory))
|
||||
@@ -1016,70 +947,5 @@ namespace Ryujinx.HLE.FileSystem
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void VerifyKeysFile(string filePath)
|
||||
{
|
||||
// Verify the keys file format refers to https://github.com/Thealexbarney/LibHac/blob/master/KEYS.md
|
||||
string genericPattern = @"^[a-z0-9_]+ = [a-z0-9]+$";
|
||||
string titlePattern = @"^[a-z0-9]{32} = [a-z0-9]{32}$";
|
||||
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
// Read all lines from the file
|
||||
string fileName = Path.GetFileName(filePath);
|
||||
string[] lines = File.ReadAllLines(filePath);
|
||||
|
||||
bool verified = false;
|
||||
switch (fileName)
|
||||
{
|
||||
case "prod.keys":
|
||||
verified = verifyKeys(lines, genericPattern);
|
||||
break;
|
||||
case "title.keys":
|
||||
verified = verifyKeys(lines, titlePattern);
|
||||
break;
|
||||
case "console.keys":
|
||||
verified = verifyKeys(lines, genericPattern);
|
||||
break;
|
||||
case "dev.keys":
|
||||
verified = verifyKeys(lines, genericPattern);
|
||||
break;
|
||||
default:
|
||||
throw new FormatException($"Keys file name \"{fileName}\" not supported. Only \"prod.keys\", \"title.keys\", \"console.keys\", \"dev.keys\" are supported.");
|
||||
}
|
||||
if (!verified)
|
||||
{
|
||||
throw new FormatException($"Invalid \"{filePath}\" file format.");
|
||||
}
|
||||
} else
|
||||
{
|
||||
throw new FileNotFoundException($"Keys file not found at \"{filePath}\".");
|
||||
}
|
||||
}
|
||||
|
||||
private bool verifyKeys(string[] lines, string regex)
|
||||
{
|
||||
foreach (string line in lines)
|
||||
{
|
||||
if (!Regex.IsMatch(line, regex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool AreKeysAlredyPresent(string pathToCheck)
|
||||
{
|
||||
string[] fileNames = { "prod.keys", "title.keys", "console.keys", "dev.keys" };
|
||||
foreach (var file in fileNames)
|
||||
{
|
||||
if (File.Exists(Path.Combine(pathToCheck, file)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,10 +223,9 @@ namespace Ryujinx.HLE.FileSystem
|
||||
{
|
||||
KeySet ??= KeySet.CreateDefaultKeySet();
|
||||
|
||||
string prodKeyFile = null;
|
||||
string keyFile = null;
|
||||
string titleKeyFile = null;
|
||||
string consoleKeyFile = null;
|
||||
string devKeyFile = null;
|
||||
|
||||
if (AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile)
|
||||
{
|
||||
@@ -237,14 +236,13 @@ namespace Ryujinx.HLE.FileSystem
|
||||
|
||||
void LoadSetAtPath(string basePath)
|
||||
{
|
||||
string localProdKeyFile = Path.Combine(basePath, "prod.keys");
|
||||
string localKeyFile = Path.Combine(basePath, "prod.keys");
|
||||
string localTitleKeyFile = Path.Combine(basePath, "title.keys");
|
||||
string localConsoleKeyFile = Path.Combine(basePath, "console.keys");
|
||||
string localDevKeyFile = Path.Combine(basePath, "dev.keys");
|
||||
|
||||
if (File.Exists(localProdKeyFile))
|
||||
if (File.Exists(localKeyFile))
|
||||
{
|
||||
prodKeyFile = localProdKeyFile;
|
||||
keyFile = localKeyFile;
|
||||
}
|
||||
|
||||
if (File.Exists(localTitleKeyFile))
|
||||
@@ -256,14 +254,9 @@ namespace Ryujinx.HLE.FileSystem
|
||||
{
|
||||
consoleKeyFile = localConsoleKeyFile;
|
||||
}
|
||||
|
||||
if (File.Exists(localDevKeyFile))
|
||||
{
|
||||
devKeyFile = localDevKeyFile;
|
||||
}
|
||||
}
|
||||
|
||||
ExternalKeyReader.ReadKeyFile(KeySet, prodKeyFile, devKeyFile, titleKeyFile, consoleKeyFile, null);
|
||||
ExternalKeyReader.ReadKeyFile(KeySet, keyFile, titleKeyFile, consoleKeyFile, null);
|
||||
}
|
||||
|
||||
public void ImportTickets(IFileSystem fs)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Applets.Browser;
|
||||
using Ryujinx.HLE.HOS.Applets.Cabinet;
|
||||
using Ryujinx.HLE.HOS.Applets.Dummy;
|
||||
using Ryujinx.HLE.HOS.Applets.Error;
|
||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
||||
@@ -24,14 +23,14 @@ namespace Ryujinx.HLE.HOS.Applets
|
||||
case AppletId.SoftwareKeyboard:
|
||||
return new SoftwareKeyboardApplet(system);
|
||||
case AppletId.LibAppletWeb:
|
||||
return new BrowserApplet(system);
|
||||
case AppletId.LibAppletShop:
|
||||
return new BrowserApplet(system);
|
||||
case AppletId.LibAppletOff:
|
||||
return new BrowserApplet();
|
||||
return new BrowserApplet(system);
|
||||
case AppletId.MiiEdit:
|
||||
Logger.Warning?.Print(LogClass.Application, $"Please use the MiiEdit inside File/Open Applet");
|
||||
return new DummyApplet(system);
|
||||
case AppletId.Cabinet:
|
||||
return new CabinetApplet(system);
|
||||
}
|
||||
|
||||
Logger.Warning?.Print(LogClass.Application, $"Applet {applet} not implemented!");
|
||||
|
||||
@@ -18,6 +18,13 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
|
||||
private List<BrowserArgument> _arguments;
|
||||
private ShimKind _shimKind;
|
||||
|
||||
public BrowserApplet(Horizon system) { }
|
||||
|
||||
public ResultCode GetResult()
|
||||
{
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession)
|
||||
{
|
||||
_normalSession = normalSession;
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.HidServer;
|
||||
using Ryujinx.HLE.HOS.Services.Hid;
|
||||
using Ryujinx.HLE.HOS.Services.Nfc.Nfp;
|
||||
using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Applets.Cabinet
|
||||
{
|
||||
internal unsafe class CabinetApplet : IApplet
|
||||
{
|
||||
private readonly Horizon _system;
|
||||
private AppletSession _normalSession;
|
||||
|
||||
public event EventHandler AppletStateChanged;
|
||||
|
||||
public CabinetApplet(Horizon system)
|
||||
{
|
||||
_system = system;
|
||||
}
|
||||
|
||||
public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession)
|
||||
{
|
||||
_normalSession = normalSession;
|
||||
|
||||
byte[] launchParams = _normalSession.Pop();
|
||||
byte[] startParamBytes = _normalSession.Pop();
|
||||
|
||||
StartParamForAmiiboSettings startParam = IApplet.ReadStruct<StartParamForAmiiboSettings>(startParamBytes);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm, $"CabinetApplet Start Type: {startParam.Type}");
|
||||
|
||||
switch (startParam.Type)
|
||||
{
|
||||
case 0:
|
||||
StartNicknameAndOwnerSettings(ref startParam);
|
||||
break;
|
||||
case 1:
|
||||
case 3:
|
||||
StartFormatter(ref startParam);
|
||||
break;
|
||||
default:
|
||||
Logger.Error?.Print(LogClass.ServiceAm, $"Unknown AmiiboSettings type: {startParam.Type}");
|
||||
break;
|
||||
}
|
||||
|
||||
// Prepare the response
|
||||
ReturnValueForAmiiboSettings returnValue = new()
|
||||
{
|
||||
AmiiboSettingsReturnFlag = (byte)AmiiboSettingsReturnFlag.HasRegisterInfo,
|
||||
DeviceHandle = new DeviceHandle
|
||||
{
|
||||
Handle = 0 // Dummy device handle
|
||||
},
|
||||
RegisterInfo = startParam.RegisterInfo
|
||||
};
|
||||
|
||||
// Push the response
|
||||
_normalSession.Push(BuildResponse(returnValue));
|
||||
AppletStateChanged?.Invoke(this, null);
|
||||
|
||||
_system.ReturnFocus();
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
public ResultCode GetResult()
|
||||
{
|
||||
_system.Device.System.NfpDevices.RemoveAt(0);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
private void StartFormatter(ref StartParamForAmiiboSettings startParam)
|
||||
{
|
||||
// Initialize RegisterInfo
|
||||
startParam.RegisterInfo = new RegisterInfo();
|
||||
}
|
||||
|
||||
private void StartNicknameAndOwnerSettings(ref StartParamForAmiiboSettings startParam)
|
||||
{
|
||||
_system.Device.UIHandler.DisplayCabinetDialog(out string newName);
|
||||
byte[] nameBytes = Encoding.UTF8.GetBytes(newName);
|
||||
Array41<byte> nickName = new Array41<byte>();
|
||||
nameBytes.CopyTo(nickName.AsSpan());
|
||||
startParam.RegisterInfo.Nickname = nickName;
|
||||
NfpDevice devicePlayer1 = new()
|
||||
{
|
||||
NpadIdType = NpadIdType.Player1,
|
||||
Handle = HidUtils.GetIndexFromNpadIdType(NpadIdType.Player1),
|
||||
State = NfpDeviceState.SearchingForTag,
|
||||
};
|
||||
_system.Device.System.NfpDevices.Add(devicePlayer1);
|
||||
_system.Device.UIHandler.DisplayCabinetMessageDialog();
|
||||
string amiiboId = string.Empty;
|
||||
bool scanned = false;
|
||||
while (!scanned)
|
||||
{
|
||||
for (int i = 0; i < _system.Device.System.NfpDevices.Count; i++)
|
||||
{
|
||||
if (_system.Device.System.NfpDevices[i].State == NfpDeviceState.TagFound)
|
||||
{
|
||||
amiiboId = _system.Device.System.NfpDevices[i].AmiiboId;
|
||||
scanned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
VirtualAmiibo.UpdateNickName(amiiboId, newName);
|
||||
}
|
||||
|
||||
private static byte[] BuildResponse(ReturnValueForAmiiboSettings returnValue)
|
||||
{
|
||||
int size = Unsafe.SizeOf<ReturnValueForAmiiboSettings>();
|
||||
byte[] bytes = new byte[size];
|
||||
|
||||
fixed (byte* bytesPtr = bytes)
|
||||
{
|
||||
Unsafe.Write(bytesPtr, returnValue);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
#region Structs
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public unsafe struct TagInfo
|
||||
{
|
||||
public fixed byte Data[0x58];
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public unsafe struct StartParamForAmiiboSettings
|
||||
{
|
||||
public byte ZeroValue; // Left at zero by sdknso
|
||||
public byte Type;
|
||||
public byte Flags;
|
||||
public byte AmiiboSettingsStartParamOffset28;
|
||||
public ulong AmiiboSettingsStartParam0;
|
||||
|
||||
public TagInfo TagInfo; // Only enabled when flags bit 1 is set
|
||||
public RegisterInfo RegisterInfo; // Only enabled when flags bit 2 is set
|
||||
|
||||
public fixed byte StartParamExtraData[0x20];
|
||||
|
||||
public fixed byte Reserved[0x24];
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public unsafe struct ReturnValueForAmiiboSettings
|
||||
{
|
||||
public byte AmiiboSettingsReturnFlag;
|
||||
private byte Padding1;
|
||||
private byte Padding2;
|
||||
private byte Padding3;
|
||||
public DeviceHandle DeviceHandle;
|
||||
public TagInfo TagInfo;
|
||||
public RegisterInfo RegisterInfo;
|
||||
public fixed byte IgnoredBySdknso[0x24];
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct DeviceHandle
|
||||
{
|
||||
public ulong Handle;
|
||||
}
|
||||
|
||||
public enum AmiiboSettingsReturnFlag : byte
|
||||
{
|
||||
Cancel = 0,
|
||||
HasTagInfo = 2,
|
||||
HasRegisterInfo = 4,
|
||||
HasTagInfoAndRegisterInfo = 6
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -117,6 +117,11 @@ namespace Ryujinx.HLE.HOS.Applets
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
public ResultCode GetResult()
|
||||
{
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
private static byte[] BuildResponse(ControllerSupportResultInfo result)
|
||||
{
|
||||
using MemoryStream stream = MemoryStreamManager.Shared.GetStream();
|
||||
|
||||
@@ -11,14 +11,11 @@ namespace Ryujinx.HLE.HOS.Applets.Dummy
|
||||
{
|
||||
private readonly Horizon _system;
|
||||
private AppletSession _normalSession;
|
||||
|
||||
public event EventHandler AppletStateChanged;
|
||||
|
||||
public DummyApplet(Horizon system)
|
||||
{
|
||||
_system = system;
|
||||
}
|
||||
|
||||
public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession)
|
||||
{
|
||||
_normalSession = normalSession;
|
||||
@@ -27,7 +24,10 @@ namespace Ryujinx.HLE.HOS.Applets.Dummy
|
||||
_system.ReturnFocus();
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
private static T ReadStruct<T>(byte[] data) where T : struct
|
||||
{
|
||||
return MemoryMarshal.Read<T>(data.AsSpan());
|
||||
}
|
||||
private static byte[] BuildResponse()
|
||||
{
|
||||
using MemoryStream stream = MemoryStreamManager.Shared.GetStream();
|
||||
@@ -35,5 +35,9 @@ namespace Ryujinx.HLE.HOS.Applets.Dummy
|
||||
writer.Write((ulong)ResultCode.Success);
|
||||
return stream.ToArray();
|
||||
}
|
||||
public ResultCode GetResult()
|
||||
{
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,5 +203,10 @@ namespace Ryujinx.HLE.HOS.Applets.Error
|
||||
_horizon.Device.UIHandler.DisplayErrorAppletDialog($"Error Number: {applicationErrorArg.ErrorNumber} (Details)", "\n" + detailsText, buttons.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
public ResultCode GetResult()
|
||||
{
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Applets
|
||||
ResultCode Start(AppletSession normalSession,
|
||||
AppletSession interactiveSession);
|
||||
|
||||
ResultCode GetResult() => ResultCode.Success;
|
||||
ResultCode GetResult();
|
||||
|
||||
bool DrawTo(RenderingSurfaceInfo surfaceInfo, IVirtualMemoryManager destination, ulong position) => false;
|
||||
|
||||
|
||||
@@ -37,6 +37,11 @@ namespace Ryujinx.HLE.HOS.Applets
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
public ResultCode GetResult()
|
||||
{
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
private byte[] BuildResponse()
|
||||
{
|
||||
UserProfile currentUser = _system.AccountManager.LastOpenedUser;
|
||||
|
||||
@@ -144,6 +144,11 @@ namespace Ryujinx.HLE.HOS.Applets
|
||||
}
|
||||
}
|
||||
|
||||
public ResultCode GetResult()
|
||||
{
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
private bool IsKeyboardActive()
|
||||
{
|
||||
return _backgroundState >= InlineKeyboardState.Appearing && _backgroundState < InlineKeyboardState.Disappearing;
|
||||
|
||||
@@ -2,7 +2,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
{
|
||||
/// <summary>
|
||||
/// Wraps a type in a class so it gets stored in the GC managed heap. This is used as communication mechanism
|
||||
/// between classes that need to be disposed and, thus, can't share their references.
|
||||
/// between classed that need to be disposed and, thus, can't share their references.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The internal type.</typeparam>
|
||||
class TRef<T>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using Gommon;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Utilities;
|
||||
@@ -7,13 +6,12 @@ using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||
{
|
||||
public class AccountSaveDataManager
|
||||
class AccountSaveDataManager
|
||||
{
|
||||
private static readonly string _profilesJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "Profiles.json");
|
||||
private readonly string _profilesJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "Profiles.json");
|
||||
|
||||
private static readonly ProfilesJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||
|
||||
@@ -51,16 +49,6 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<UserProfile> 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<string, UserProfile> profiles)
|
||||
{
|
||||
ProfilesJson profilesJson = new()
|
||||
|
||||
@@ -5,23 +5,5 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.Lp2p
|
||||
class IServiceCreator : IpcService
|
||||
{
|
||||
public IServiceCreator(ServiceCtx context) { }
|
||||
|
||||
[CommandCmif(0)]
|
||||
// CreateNetworkService(pid, u64, u32) -> object<nn::ldn::detail::ISfService>
|
||||
public ResultCode CreateNetworkService(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new ISfService(context));
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(8)]
|
||||
// CreateNetworkServiceMonitor(pid, u64) -> object<nn::ldn::detail::ISfServiceMonitor>
|
||||
public ResultCode CreateNetworkServiceMonitor(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new ISfServiceMonitor(context));
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Ldn.Lp2p
|
||||
{
|
||||
class ISfService : IpcService
|
||||
{
|
||||
public ISfService(ServiceCtx context) { }
|
||||
|
||||
[CommandCmif(0)]
|
||||
// Initialize()
|
||||
public ResultCode Initialize(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(0);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(768)]
|
||||
// CreateGroup(buffer<nn::lp2p::GroupInfo, 0x31)
|
||||
public ResultCode CreateGroup(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceLdn);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(776)]
|
||||
// DestroyGroup()
|
||||
public ResultCode DestroyGroup(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceLdn);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1536)]
|
||||
// SendToOtherGroup(nn::lp2p::MacAddress, nn::lp2p::GroupId, s16, s16, u32, buffer<unknown, 0x21>)
|
||||
public ResultCode SendToOtherGroup(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceLdn);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1544)]
|
||||
// RecvFromOtherGroup(u32, buffer<unknown, 0x22>) -> (nn::lp2p::MacAddress, u16, s16, u32, s32)
|
||||
public ResultCode RecvFromOtherGroup(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceLdn);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Ldn.Lp2p
|
||||
{
|
||||
class ISfServiceMonitor : IpcService
|
||||
{
|
||||
private readonly KEvent _stateChangeEvent;
|
||||
private readonly KEvent _jointEvent;
|
||||
private int _stateChangeEventHandle = 0;
|
||||
private int _jointEventHandle = 0;
|
||||
|
||||
public ISfServiceMonitor(ServiceCtx context)
|
||||
{
|
||||
_stateChangeEvent = new KEvent(context.Device.System.KernelContext);
|
||||
_jointEvent = new KEvent(context.Device.System.KernelContext);
|
||||
}
|
||||
|
||||
[CommandCmif(0)]
|
||||
// Initialize()
|
||||
public ResultCode Initialize(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(0);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(256)]
|
||||
// AttachNetworkInterfaceStateChangeEvent() -> handle<copy>
|
||||
public ResultCode AttachNetworkInterfaceStateChangeEvent(ServiceCtx context)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_stateChangeEvent.ReadableEvent, out _stateChangeEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_stateChangeEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(288)]
|
||||
// GetGroupInfo(buffer<nn::lp2p::GroupInfo, 0x32>)
|
||||
public ResultCode GetGroupInfo(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceLdn);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(296)]
|
||||
// GetGroupInfo2(buffer<nn::lp2p::GroupInfo, 0x32>, buffer<nn::lp2p::GroupInfo, 0x31>)
|
||||
public ResultCode GetGroupInfo2(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceLdn);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(312)]
|
||||
// GetIpConfig(buffer<unknown<0x100>, 0x1a>)
|
||||
public ResultCode GetIpConfig(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceLdn);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(328)]
|
||||
// AttachNetworkInterfaceStateChangeEvent() -> handle<copy>
|
||||
public ResultCode AttachJoinEvent(ServiceCtx context)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_jointEvent.ReadableEvent, out _jointEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_jointEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
using Gommon;
|
||||
using Humanizer;
|
||||
using NetCoreServer;
|
||||
using Open.Nat;
|
||||
using Ryujinx.Common.Logging;
|
||||
@@ -155,10 +153,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
|
||||
|
||||
if (_publicPort != 0)
|
||||
{
|
||||
_ = Executor.ExecuteAfterDelayAsync(
|
||||
PortLeaseRenew.Seconds(),
|
||||
_disposedCancellation.Token,
|
||||
RefreshLease);
|
||||
_ = Task.Delay(PortLeaseRenew * 1000, _disposedCancellation.Token).ContinueWith((task) => Task.Run(RefreshLease));
|
||||
}
|
||||
|
||||
_natDevice = device;
|
||||
@@ -262,10 +257,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
|
||||
|
||||
}
|
||||
|
||||
_ = Executor.ExecuteAfterDelayAsync(
|
||||
PortLeaseRenew.Milliseconds(),
|
||||
_disposedCancellation.Token,
|
||||
RefreshLease);
|
||||
_ = Task.Delay(PortLeaseRenew, _disposedCancellation.Token).ContinueWith((task) => Task.Run(RefreshLease));
|
||||
}
|
||||
|
||||
public bool TryRegisterUser(P2pProxySession session, ExternalProxyConfig config)
|
||||
|
||||
@@ -93,13 +93,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||
return registerInfo;
|
||||
}
|
||||
|
||||
public static void UpdateNickName(string amiiboId, string newNickName)
|
||||
{
|
||||
VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId);
|
||||
virtualAmiiboFile.NickName = newNickName;
|
||||
SaveAmiiboFile(virtualAmiiboFile);
|
||||
}
|
||||
|
||||
public static bool OpenApplicationArea(string amiiboId, uint applicationAreaId)
|
||||
{
|
||||
VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId);
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
||||
{
|
||||
private static readonly TitleUpdateMetadataJsonSerializerContext _applicationSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||
|
||||
public static ProcessResult Load(this Nca nca, Switch device, Nca patchNca, Nca controlNca, BlitStruct<ApplicationControlProperty>? customNacpData = null)
|
||||
public static ProcessResult Load(this Nca nca, Switch device, Nca patchNca, Nca controlNca)
|
||||
{
|
||||
// Extract RomFs and ExeFs from NCA.
|
||||
IStorage romFs = nca.GetRomFs(device, patchNca);
|
||||
@@ -55,10 +55,6 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
||||
{
|
||||
nacpData = controlNca.GetNacp(device);
|
||||
}
|
||||
else if (customNacpData != null) // if the Application doesn't provide a nacp file but the Application provides an override, use the provided nacp override
|
||||
{
|
||||
nacpData = (BlitStruct<ApplicationControlProperty>)customNacpData;
|
||||
}
|
||||
|
||||
/* TODO: Rework this since it's wrong and doesn't work as it takes the DisplayVersion from a "potential" non-existent update.
|
||||
|
||||
|
||||
@@ -98,12 +98,12 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool LoadNca(string path, BlitStruct<ApplicationControlProperty>? customNacpData = null)
|
||||
public bool LoadNca(string path)
|
||||
{
|
||||
FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||
Nca nca = new(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false));
|
||||
|
||||
ProcessResult processResult = nca.Load(_device, null, null, customNacpData);
|
||||
ProcessResult processResult = nca.Load(_device, null, null);
|
||||
|
||||
if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult))
|
||||
{
|
||||
|
||||
@@ -84,19 +84,12 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isFirmware = ProgramId is >= 0x0100000000000819 and <= 0x010000000000081C;
|
||||
bool isFirmwareApplication = ProgramId <= 0x0100000000007FFF;
|
||||
|
||||
string name = !isFirmware
|
||||
? (isFirmwareApplication ? "Firmware Application " : "") + (!string.IsNullOrWhiteSpace(Name) ? Name : "<Unknown Name>")
|
||||
: "Firmware";
|
||||
|
||||
// TODO: LibHac npdm currently doesn't support version field.
|
||||
string version = !isFirmware
|
||||
? (!string.IsNullOrWhiteSpace(DisplayVersion) ? DisplayVersion : "<Unknown Version>")
|
||||
string version = ProgramId > 0x0100000000007FFF
|
||||
? DisplayVersion
|
||||
: device.System.ContentManager.GetCurrentFirmwareVersion()?.VersionString ?? "?";
|
||||
|
||||
Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {name} v{version} [{ProgramIdText}] [{(Is64Bit ? "64-bit" : "32-bit")}]");
|
||||
Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {Name} v{version} [{ProgramIdText}] [{(Is64Bit ? "64-bit" : "32-bit")}]");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.Ns;
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.HLE
|
||||
{
|
||||
public static class StructHelpers
|
||||
{
|
||||
public static BlitStruct<ApplicationControlProperty> CreateCustomNacpData(string name, string version)
|
||||
{
|
||||
// https://switchbrew.org/wiki/NACP
|
||||
const int OffsetOfDisplayVersion = 0x3060;
|
||||
|
||||
// https://switchbrew.org/wiki/NACP#ApplicationTitle
|
||||
const int TotalApplicationTitles = 0x10;
|
||||
const int SizeOfApplicationTitle = 0x300;
|
||||
const int OffsetOfApplicationPublisherStrings = 0x200;
|
||||
|
||||
|
||||
var nacpData = new BlitStruct<ApplicationControlProperty>(1);
|
||||
|
||||
// name and publisher buffer
|
||||
// repeat once for each locale (the ApplicationControlProperty has 16 locales)
|
||||
for (int i = 0; i < TotalApplicationTitles; i++)
|
||||
{
|
||||
Encoding.ASCII.GetBytes(name).AsSpan().CopyTo(nacpData.ByteSpan[(i * SizeOfApplicationTitle)..]);
|
||||
"Ryujinx"u8.CopyTo(nacpData.ByteSpan[(i * SizeOfApplicationTitle + OffsetOfApplicationPublisherStrings)..]);
|
||||
}
|
||||
|
||||
// version buffer
|
||||
Encoding.ASCII.GetBytes(version).AsSpan().CopyTo(nacpData.ByteSpan[OffsetOfDisplayVersion..]);
|
||||
|
||||
return nacpData;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.Ns;
|
||||
using Ryujinx.Audio.Backends.CompatLayer;
|
||||
using Ryujinx.Audio.Integration;
|
||||
using Ryujinx.Common.Configuration;
|
||||
@@ -113,7 +111,7 @@ namespace Ryujinx.HLE
|
||||
|
||||
public bool LoadCart(string exeFsDir, string romFsFile = null) => Processes.LoadUnpackedNca(exeFsDir, romFsFile);
|
||||
public bool LoadXci(string xciFile, ulong applicationId = 0) => Processes.LoadXci(xciFile, applicationId);
|
||||
public bool LoadNca(string ncaFile, BlitStruct<ApplicationControlProperty>? customNacpData = null) => Processes.LoadNca(ncaFile, customNacpData);
|
||||
public bool LoadNca(string ncaFile) => Processes.LoadNca(ncaFile);
|
||||
public bool LoadNsp(string nspFile, ulong applicationId = 0) => Processes.LoadNsp(nspFile, applicationId);
|
||||
public bool LoadProgram(string fileName) => Processes.LoadNxo(fileName);
|
||||
|
||||
|
||||
@@ -24,18 +24,6 @@ namespace Ryujinx.HLE.UI
|
||||
/// <returns>True when OK is pressed, False otherwise.</returns>
|
||||
bool DisplayMessageDialog(ControllerAppletUIArgs args);
|
||||
|
||||
/// <summary>
|
||||
/// Displays an Input Dialog box to the user so they can enter the Amiibo's new name
|
||||
/// </summary>
|
||||
/// <param name="userText">Text that the user entered. Set to `null` on internal errors</param>
|
||||
/// <returns>True when OK is pressed, False otherwise. Also returns True on internal errors</returns>
|
||||
bool DisplayCabinetDialog(out string userText);
|
||||
|
||||
/// <summary>
|
||||
/// Displays a Message Dialog box to the user to notify them to scan the Amiibo.
|
||||
/// </summary>
|
||||
void DisplayCabinetMessageDialog();
|
||||
|
||||
/// <summary>
|
||||
/// Tell the UI that we need to transition to another program.
|
||||
/// </summary>
|
||||
|
||||
@@ -2,7 +2,7 @@ using Ryujinx.HLE.UI;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Headless
|
||||
namespace Ryujinx.Headless.SDL2
|
||||
{
|
||||
/// <summary>
|
||||
/// Headless text processing class, right now there is no way to forward the input to it.
|
||||
@@ -1,6 +1,6 @@
|
||||
using Ryujinx.HLE.UI;
|
||||
|
||||
namespace Ryujinx.Headless
|
||||
namespace Ryujinx.Headless.SDL2
|
||||
{
|
||||
internal class HeadlessHostUiTheme : IHostUITheme
|
||||
{
|
||||
@@ -7,7 +7,7 @@ using Ryujinx.Input.HLE;
|
||||
using System;
|
||||
using static SDL2.SDL;
|
||||
|
||||
namespace Ryujinx.Headless
|
||||
namespace Ryujinx.Headless.SDL2.OpenGL
|
||||
{
|
||||
class OpenGLWindow : WindowBase
|
||||
{
|
||||
@@ -1,168 +1,13 @@
|
||||
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
|
||||
namespace Ryujinx.Headless.SDL2
|
||||
{
|
||||
public class Options
|
||||
{
|
||||
public void InheritMainConfig(string[] originalArgs, ConfigurationState configurationState, out bool needsProfileSet)
|
||||
{
|
||||
needsProfileSet = NeedsOverride(nameof(UserProfile));
|
||||
|
||||
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;
|
||||
|
||||
return;
|
||||
|
||||
bool NeedsOverride(string argKey) => originalArgs.None(arg => arg.TrimStart('-').EqualsIgnoreCase(OptionName(argKey)));
|
||||
|
||||
string OptionName(string propertyName) =>
|
||||
typeof(Options)!.GetProperty(propertyName)!.GetCustomAttribute<OptionAttribute>()!.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; }
|
||||
|
||||
[Option("root-data-dir", Required = false, HelpText = "Set the custom folder path for Ryujinx data.")]
|
||||
public string BaseDataDir { get; set; }
|
||||
@@ -327,7 +172,7 @@ namespace Ryujinx.Headless
|
||||
public bool LoggingDisableWarning { get; set; }
|
||||
|
||||
[Option("disable-error-logs", Required = false, HelpText = "Disables printing error log messages.")]
|
||||
public bool LoggingDisableError { get; set; }
|
||||
public bool LoggingEnableError { get; set; }
|
||||
|
||||
[Option("enable-trace-logs", Required = false, Default = false, HelpText = "Enables printing trace log messages.")]
|
||||
public bool LoggingEnableTrace { get; set; }
|
||||
@@ -1,9 +1,12 @@
|
||||
using CommandLine;
|
||||
using Gommon;
|
||||
using Ryujinx.Ava;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Audio.Backends.SDL2;
|
||||
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;
|
||||
@@ -11,9 +14,14 @@ 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.Headless.SDL2.OpenGL;
|
||||
using Ryujinx.Headless.SDL2.Vulkan;
|
||||
using Ryujinx.HLE;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS;
|
||||
@@ -22,16 +30,22 @@ using Ryujinx.Input;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.Input.SDL2;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
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
|
||||
namespace Ryujinx.Headless.SDL2
|
||||
{
|
||||
public partial class HeadlessRyujinx
|
||||
class Program
|
||||
{
|
||||
public static string Version { get; private set; }
|
||||
|
||||
private static VirtualFileSystem _virtualFileSystem;
|
||||
private static ContentManager _contentManager;
|
||||
private static AccountManager _accountManager;
|
||||
@@ -41,18 +55,20 @@ namespace Ryujinx.Headless
|
||||
private static Switch _emulationContext;
|
||||
private static WindowBase _window;
|
||||
private static WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution;
|
||||
private static List<InputConfig> _inputConfiguration = [];
|
||||
private static List<InputConfig> _inputConfiguration;
|
||||
private static bool _enableKeyboard;
|
||||
private static bool _enableMouse;
|
||||
|
||||
private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||
|
||||
public static void Entrypoint(string[] args)
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Version = ReleaseInformation.Version;
|
||||
|
||||
// Make process DPI aware for proper window sizing on high-res screens.
|
||||
ForceDpiAware.Windows();
|
||||
|
||||
Console.Title = $"Ryujinx Console {Program.Version} (Headless)";
|
||||
Console.Title = $"Ryujinx Console {Version} (Headless SDL2)";
|
||||
|
||||
if (OperatingSystem.IsMacOS() || OperatingSystem.IsLinux())
|
||||
{
|
||||
@@ -80,89 +96,242 @@ namespace Ryujinx.Headless
|
||||
}
|
||||
|
||||
Parser.Default.ParseArguments<Options>(args)
|
||||
.WithParsed(options => Load(args, options))
|
||||
.WithNotParsed(errors =>
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Application, "Error parsing command-line arguments:");
|
||||
|
||||
errors.ForEach(err => Logger.Error?.PrintMsg(LogClass.Application, $" - {err.Tag}"));
|
||||
});
|
||||
.WithParsed(Load)
|
||||
.WithNotParsed(errors => errors.Output());
|
||||
}
|
||||
|
||||
public static void ReloadConfig(string customConfigPath = null)
|
||||
|
||||
private static InputConfig HandlePlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index)
|
||||
{
|
||||
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 (customConfigPath != null && File.Exists(customConfigPath))
|
||||
if (inputId == null)
|
||||
{
|
||||
configurationPath = customConfigPath;
|
||||
}
|
||||
else if (File.Exists(localConfigurationPath))
|
||||
{
|
||||
configurationPath = localConfigurationPath;
|
||||
}
|
||||
else if (File.Exists(appDataConfigurationPath))
|
||||
{
|
||||
configurationPath = appDataConfigurationPath;
|
||||
}
|
||||
|
||||
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))
|
||||
if (index == PlayerIndex.Player1)
|
||||
{
|
||||
ConfigurationState.Instance.Load(configurationFileFormat, configurationPath);
|
||||
Logger.Info?.Print(LogClass.Application, $"{index} not configured, defaulting to default keyboard.");
|
||||
|
||||
// Default to keyboard
|
||||
inputId = "0";
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location: {configurationPath}");
|
||||
Logger.Info?.Print(LogClass.Application, $"{index} not configured");
|
||||
|
||||
ConfigurationState.Instance.LoadDefault();
|
||||
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<Key>
|
||||
{
|
||||
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<Key>
|
||||
{
|
||||
StickUp = Key.W,
|
||||
StickDown = Key.S,
|
||||
StickLeft = Key.A,
|
||||
StickRight = Key.D,
|
||||
StickButton = Key.F,
|
||||
},
|
||||
|
||||
RightJoycon = new RightJoyconCommonConfig<Key>
|
||||
{
|
||||
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<Key>
|
||||
{
|
||||
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<ConfigGamepadInputId>
|
||||
{
|
||||
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<ConfigGamepadInputId, ConfigStickInputId>
|
||||
{
|
||||
Joystick = ConfigStickInputId.Left,
|
||||
StickButton = ConfigGamepadInputId.LeftStick,
|
||||
InvertStickX = false,
|
||||
InvertStickY = false,
|
||||
Rotate90CW = false,
|
||||
},
|
||||
|
||||
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
|
||||
{
|
||||
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<ConfigGamepadInputId, ConfigStickInputId>
|
||||
{
|
||||
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)
|
||||
static void Load(Options option)
|
||||
{
|
||||
Initialize();
|
||||
|
||||
bool useLastUsedProfile = false;
|
||||
|
||||
if (option.InheritConfig)
|
||||
{
|
||||
option.InheritMainConfig(originalArgs, ConfigurationState.Instance, 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")))
|
||||
{
|
||||
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();
|
||||
_libHacHorizonManager = new LibHacHorizonManager();
|
||||
|
||||
@@ -177,7 +346,7 @@ namespace Ryujinx.Headless
|
||||
|
||||
_inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver());
|
||||
|
||||
GraphicsConfig.EnableShaderCache = !option.DisableShaderCache;
|
||||
GraphicsConfig.EnableShaderCache = true;
|
||||
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
@@ -188,13 +357,15 @@ namespace Ryujinx.Headless
|
||||
}
|
||||
}
|
||||
|
||||
IGamepad gamepad;
|
||||
|
||||
if (option.ListInputIds)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Application, "Input Ids:");
|
||||
|
||||
foreach (string id in _inputManager.KeyboardDriver.GamepadsIds)
|
||||
{
|
||||
IGamepad gamepad = _inputManager.KeyboardDriver.GetGamepad(id);
|
||||
gamepad = _inputManager.KeyboardDriver.GetGamepad(id);
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, $"- {id} (\"{gamepad.Name}\")");
|
||||
|
||||
@@ -203,7 +374,7 @@ namespace Ryujinx.Headless
|
||||
|
||||
foreach (string id in _inputManager.GamepadDriver.GamepadsIds)
|
||||
{
|
||||
IGamepad gamepad = _inputManager.GamepadDriver.GetGamepad(id);
|
||||
gamepad = _inputManager.GamepadDriver.GetGamepad(id);
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, $"- {id} (\"{gamepad.Name}\")");
|
||||
|
||||
@@ -220,7 +391,7 @@ namespace Ryujinx.Headless
|
||||
return;
|
||||
}
|
||||
|
||||
_inputConfiguration ??= [];
|
||||
_inputConfiguration = new List<InputConfig>();
|
||||
_enableKeyboard = option.EnableKeyboard;
|
||||
_enableMouse = option.EnableMouse;
|
||||
|
||||
@@ -233,9 +404,9 @@ namespace Ryujinx.Headless
|
||||
_inputConfiguration.Add(inputConfig);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LoadPlayerConfiguration(option.InputProfile1Name, option.InputId1, PlayerIndex.Player1);
|
||||
LoadPlayerConfiguration(option.InputProfile2Name, option.InputId2, PlayerIndex.Player2);
|
||||
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);
|
||||
@@ -243,7 +414,6 @@ namespace Ryujinx.Headless
|
||||
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)
|
||||
{
|
||||
@@ -255,7 +425,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.LoggingDisableError);
|
||||
Logger.SetEnable(LogLevel.Error, option.LoggingEnableError);
|
||||
Logger.SetEnable(LogLevel.Trace, option.LoggingEnableTrace);
|
||||
Logger.SetEnable(LogLevel.Guest, !option.LoggingDisableGuest);
|
||||
Logger.SetEnable(LogLevel.AccessLog, option.LoggingEnableFsAccessLog);
|
||||
@@ -339,6 +509,83 @@ 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,
|
||||
"",
|
||||
"",
|
||||
options.CustomVSyncInterval);
|
||||
|
||||
return new Switch(configuration);
|
||||
}
|
||||
|
||||
private static void ExecutionEntrypoint()
|
||||
{
|
||||
if (OperatingSystem.IsWindows())
|
||||
72
src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj
Normal file
72
src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj
Normal file
@@ -0,0 +1,72 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Version>1.0.0-dirty</Version>
|
||||
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
|
||||
<SigningCertificate Condition=" '$(SigningCertificate)' == '' ">-</SigningCertificate>
|
||||
<TieredPGO>true</TieredPGO>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenTK.Core" />
|
||||
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="$([MSBuild]::IsOSPlatform('OSX'))">
|
||||
<Exec Command="codesign --entitlements '$(ProjectDir)..\..\distribution\macos\entitlements.xml' -f -s $(SigningCertificate) '$(TargetDir)$(TargetName)'" />
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" />
|
||||
<ProjectReference Include="..\ARMeilleure\ARMeilleure.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineParser" />
|
||||
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64'" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\..\distribution\legal\THIRDPARTY.md">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>THIRDPARTY.md</TargetPath>
|
||||
</Content>
|
||||
<Content Include="..\..\LICENSE.txt">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>LICENSE.txt</TargetPath>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64' OR ('$(RuntimeIdentifier)' == '' AND $([MSBuild]::IsOSPlatform('Linux')))">
|
||||
<Content Include="..\..\distribution\linux\Ryujinx.sh">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Ryujinx.bmp" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Due to .net core 3.1 embedded resource loading -->
|
||||
<PropertyGroup>
|
||||
<EmbeddedResourceUseDependentUponConvention>false</EmbeddedResourceUseDependentUponConvention>
|
||||
<ApplicationIcon>..\Ryujinx\Ryujinx.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(RuntimeIdentifier)' != ''">
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<PublishTrimmed>true</PublishTrimmed>
|
||||
<TrimMode>partial</TrimMode>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
BIN
src/Ryujinx.Headless.SDL2/Ryujinx.bmp
Normal file
BIN
src/Ryujinx.Headless.SDL2/Ryujinx.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.1 KiB |
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Headless
|
||||
namespace Ryujinx.Headless.SDL2
|
||||
{
|
||||
class StatusUpdatedEventArgs(
|
||||
string vSyncMode,
|
||||
@@ -6,7 +6,7 @@ using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using static SDL2.SDL;
|
||||
|
||||
namespace Ryujinx.Headless
|
||||
namespace Ryujinx.Headless.SDL2.Vulkan
|
||||
{
|
||||
class VulkanWindow : WindowBase
|
||||
{
|
||||
@@ -1,6 +1,4 @@
|
||||
using Humanizer;
|
||||
using LibHac.Tools.Fs;
|
||||
using Ryujinx.Ava;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Logging;
|
||||
@@ -27,7 +25,7 @@ using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing;
|
||||
using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter;
|
||||
using Switch = Ryujinx.HLE.Switch;
|
||||
|
||||
namespace Ryujinx.Headless
|
||||
namespace Ryujinx.Headless.SDL2
|
||||
{
|
||||
abstract partial class WindowBase : IHostUIHandler, IDisposable
|
||||
{
|
||||
@@ -137,7 +135,7 @@ namespace Ryujinx.Headless
|
||||
|
||||
private void SetWindowIcon()
|
||||
{
|
||||
Stream iconStream = typeof(Program).Assembly.GetManifestResourceStream("HeadlessLogo");
|
||||
Stream iconStream = typeof(WindowBase).Assembly.GetManifestResourceStream("Ryujinx.Headless.SDL2.Ryujinx.bmp");
|
||||
byte[] iconBytes = new byte[iconStream!.Length];
|
||||
|
||||
if (iconStream.Read(iconBytes, 0, iconBytes.Length) != iconBytes.Length)
|
||||
@@ -319,7 +317,7 @@ namespace Ryujinx.Headless
|
||||
Device.VSyncMode.ToString(),
|
||||
dockedMode,
|
||||
Device.Configuration.AspectRatio.ToText(),
|
||||
$"{Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)",
|
||||
$"Game: {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)",
|
||||
$"FIFO: {Device.Statistics.GetFifoPercent():0.00} %",
|
||||
$"GPU: {_gpuDriverName}"));
|
||||
|
||||
@@ -487,19 +485,6 @@ namespace Ryujinx.Headless
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool DisplayCabinetDialog(out string userText)
|
||||
{
|
||||
// SDL2 doesn't support input dialogs
|
||||
userText = "Ryujinx";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void DisplayCabinetMessageDialog()
|
||||
{
|
||||
SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags.SDL_MESSAGEBOX_INFORMATION, "Cabinet Dialog", "Please scan your Amiibo now.", WindowHandle);
|
||||
}
|
||||
|
||||
public bool DisplayMessageDialog(ControllerAppletUIArgs args)
|
||||
{
|
||||
if (_ignoreControllerApplet) return false;
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
<TargetOS Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">linux</TargetOS>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<RunSettingsFilePath>$(MSBuildProjectDirectory)\.runsettings</RunSettingsFilePath>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using DynamicData;
|
||||
using DynamicData.Kernel;
|
||||
using Gommon;
|
||||
using LibHac;
|
||||
using LibHac.Common;
|
||||
@@ -36,13 +37,14 @@ using System.Threading.Tasks;
|
||||
using ContentType = LibHac.Ncm.ContentType;
|
||||
using MissingKeyException = LibHac.Common.Keys.MissingKeyException;
|
||||
using Path = System.IO.Path;
|
||||
using SpanHelpers = LibHac.Common.SpanHelpers;
|
||||
using TimeSpan = System.TimeSpan;
|
||||
|
||||
namespace Ryujinx.UI.App.Common
|
||||
{
|
||||
public class ApplicationLibrary
|
||||
{
|
||||
public const string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com";
|
||||
public static string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com";
|
||||
public Language DesiredLanguage { get; set; }
|
||||
public event EventHandler<ApplicationCountUpdatedEventArgs> ApplicationCountUpdated;
|
||||
public event EventHandler<LdnGameDataReceivedEventArgs> LdnGameDataReceived;
|
||||
@@ -189,9 +191,12 @@ namespace Ryujinx.UI.App.Common
|
||||
}
|
||||
}
|
||||
|
||||
return isExeFs
|
||||
? GetApplicationFromExeFs(pfs, filePath)
|
||||
: null;
|
||||
if (isExeFs)
|
||||
{
|
||||
return GetApplicationFromExeFs(pfs, filePath);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <exception cref="LibHac.Common.Keys.MissingKeyException">The configured key set is missing a key.</exception>
|
||||
@@ -507,6 +512,10 @@ namespace Ryujinx.UI.App.Common
|
||||
case ".xci":
|
||||
case ".nsp":
|
||||
{
|
||||
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
|
||||
? IntegrityCheckLevel.ErrorOnInvalid
|
||||
: IntegrityCheckLevel.None;
|
||||
|
||||
using IFileSystem pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(filePath, _virtualFileSystem);
|
||||
|
||||
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
|
||||
@@ -595,7 +604,7 @@ namespace Ryujinx.UI.App.Common
|
||||
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None)
|
||||
.OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read)
|
||||
.ThrowIfFailure();
|
||||
nacpFile.Get.Read(out _, 0, LibHac.Common.SpanHelpers.AsByteSpan(ref controlData),
|
||||
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData),
|
||||
ReadOption.None).ThrowIfFailure();
|
||||
|
||||
var displayVersion = controlData.DisplayVersionString.ToString();
|
||||
@@ -818,7 +827,7 @@ namespace Ryujinx.UI.App.Common
|
||||
{
|
||||
_downloadableContents.Edit(it =>
|
||||
{
|
||||
DownloadableContentsHelper.SaveDownloadableContentsJson(application.IdBase, dlcs);
|
||||
DownloadableContentsHelper.SaveDownloadableContentsJson(_virtualFileSystem, application.IdBase, dlcs);
|
||||
|
||||
it.Remove(it.Items.Where(item => item.Dlc.TitleIdBase == application.IdBase));
|
||||
it.AddOrUpdate(dlcs);
|
||||
@@ -830,7 +839,7 @@ namespace Ryujinx.UI.App.Common
|
||||
{
|
||||
_titleUpdates.Edit(it =>
|
||||
{
|
||||
TitleUpdatesHelper.SaveTitleUpdatesJson(application.IdBase, updates);
|
||||
TitleUpdatesHelper.SaveTitleUpdatesJson(_virtualFileSystem, application.IdBase, updates);
|
||||
|
||||
it.Remove(it.Items.Where(item => item.TitleUpdate.TitleIdBase == application.IdBase));
|
||||
it.AddOrUpdate(updates);
|
||||
@@ -1079,15 +1088,14 @@ namespace Ryujinx.UI.App.Common
|
||||
|
||||
private bool AddAndAutoSelectUpdate(TitleUpdateModel update)
|
||||
{
|
||||
if (update == null) return false;
|
||||
|
||||
var currentlySelected = TitleUpdates.Items.FindFirst(it =>
|
||||
var currentlySelected = TitleUpdates.Items.FirstOrOptional(it =>
|
||||
it.TitleUpdate.TitleIdBase == update.TitleIdBase && it.IsSelected);
|
||||
|
||||
var shouldSelect = currentlySelected.Check(curr => curr.TitleUpdate?.Version < update.Version);
|
||||
var shouldSelect = !currentlySelected.HasValue ||
|
||||
currentlySelected.Value.TitleUpdate.Version < update.Version;
|
||||
|
||||
_titleUpdates.AddOrUpdate((update, shouldSelect));
|
||||
|
||||
|
||||
if (currentlySelected.HasValue && shouldSelect)
|
||||
{
|
||||
_titleUpdates.AddOrUpdate((currentlySelected.Value.TitleUpdate, false));
|
||||
@@ -1456,7 +1464,7 @@ namespace Ryujinx.UI.App.Common
|
||||
if (addedNewDlc)
|
||||
{
|
||||
var gameDlcs = it.Items.Where(dlc => dlc.Dlc.TitleIdBase == application.IdBase).ToList();
|
||||
DownloadableContentsHelper.SaveDownloadableContentsJson(application.IdBase,
|
||||
DownloadableContentsHelper.SaveDownloadableContentsJson(_virtualFileSystem, application.IdBase,
|
||||
gameDlcs);
|
||||
}
|
||||
}
|
||||
@@ -1475,11 +1483,11 @@ namespace Ryujinx.UI.App.Common
|
||||
TitleUpdatesHelper.LoadTitleUpdatesJson(_virtualFileSystem, application.IdBase);
|
||||
it.AddOrUpdate(savedUpdates);
|
||||
|
||||
var selectedUpdate = savedUpdates.FindFirst(update => update.IsSelected);
|
||||
var selectedUpdate = savedUpdates.FirstOrOptional(update => update.IsSelected);
|
||||
|
||||
if (TryGetTitleUpdatesFromFile(application.Path, out var bundledUpdates))
|
||||
{
|
||||
var savedUpdateLookup = savedUpdates.Select(update => update.Update).ToHashSet();
|
||||
var savedUpdateLookup = savedUpdates.Select(update => update.Item1).ToHashSet();
|
||||
bool updatesChanged = false;
|
||||
|
||||
foreach (var update in bundledUpdates.OrderByDescending(bundled => bundled.Version))
|
||||
@@ -1487,11 +1495,12 @@ namespace Ryujinx.UI.App.Common
|
||||
if (!savedUpdateLookup.Contains(update))
|
||||
{
|
||||
bool shouldSelect = false;
|
||||
if (selectedUpdate.Check(su => su.Update?.Version < update.Version))
|
||||
if (!selectedUpdate.HasValue || selectedUpdate.Value.Item1.Version < update.Version)
|
||||
{
|
||||
shouldSelect = true;
|
||||
_titleUpdates.AddOrUpdate((selectedUpdate.Value.Update, false));
|
||||
selectedUpdate = (update, true);
|
||||
if (selectedUpdate.HasValue)
|
||||
_titleUpdates.AddOrUpdate((selectedUpdate.Value.Item1, false));
|
||||
selectedUpdate = DynamicData.Kernel.Optional<(TitleUpdateModel, bool IsSelected)>.Create((update, true));
|
||||
}
|
||||
|
||||
modifiedVersion = modifiedVersion || shouldSelect;
|
||||
@@ -1504,7 +1513,7 @@ namespace Ryujinx.UI.App.Common
|
||||
if (updatesChanged)
|
||||
{
|
||||
var gameUpdates = it.Items.Where(update => update.TitleUpdate.TitleIdBase == application.IdBase).ToList();
|
||||
TitleUpdatesHelper.SaveTitleUpdatesJson(application.IdBase, gameUpdates);
|
||||
TitleUpdatesHelper.SaveTitleUpdatesJson(_virtualFileSystem, application.IdBase, gameUpdates);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1516,14 +1525,14 @@ namespace Ryujinx.UI.App.Common
|
||||
private void SaveDownloadableContentsForGame(ulong titleIdBase)
|
||||
{
|
||||
var dlcs = DownloadableContents.Items.Where(dlc => dlc.Dlc.TitleIdBase == titleIdBase).ToList();
|
||||
DownloadableContentsHelper.SaveDownloadableContentsJson(titleIdBase, dlcs);
|
||||
DownloadableContentsHelper.SaveDownloadableContentsJson(_virtualFileSystem, titleIdBase, dlcs);
|
||||
}
|
||||
|
||||
// Save the _currently tracked_ update state for the game
|
||||
private void SaveTitleUpdatesForGame(ulong titleIdBase)
|
||||
{
|
||||
var updates = TitleUpdates.Items.Where(update => update.TitleUpdate.TitleIdBase == titleIdBase).ToList();
|
||||
TitleUpdatesHelper.SaveTitleUpdatesJson(titleIdBase, updates);
|
||||
TitleUpdatesHelper.SaveTitleUpdatesJson(_virtualFileSystem, titleIdBase, updates);
|
||||
}
|
||||
|
||||
// ApplicationData isnt live-updating (e.g. when an update gets applied) and so this is meant to trigger a refresh
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
using ARMeilleure;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||
using Ryujinx.Common.Configuration.Multiplayer;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.Vulkan;
|
||||
using Ryujinx.HLE;
|
||||
using Ryujinx.UI.Common.Configuration.System;
|
||||
using Ryujinx.UI.Common.Configuration.UI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.UI.Common.Configuration
|
||||
{
|
||||
@@ -17,10 +21,10 @@ namespace Ryujinx.UI.Common.Configuration
|
||||
if (Instance != null)
|
||||
{
|
||||
throw new InvalidOperationException("Configuration is already initialized");
|
||||
}
|
||||
}
|
||||
|
||||
Instance = new ConfigurationState();
|
||||
}
|
||||
}
|
||||
|
||||
public ConfigurationFileFormat ToFileFormat()
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.UI.Common.Configuration.System
|
||||
|
||||
@@ -247,7 +247,6 @@ namespace Ryujinx.UI.Common
|
||||
"0100dbf01000a000", // Burnout Paradise Remastered
|
||||
"0100744001588000", // Cars 3: Driven to Win
|
||||
"0100b41013c82000", // Cruis'n Blast
|
||||
"01001b300b9be000", // Diablo III: Eternal Collection
|
||||
"01008c8012920000", // Dying Light Platinum Edition
|
||||
"010073c01af34000", // LEGO Horizon Adventures
|
||||
"0100770008dd8000", // Monster Hunter Generations Ultimate
|
||||
|
||||
@@ -7,24 +7,6 @@ namespace Ryujinx.UI.Common.Helper
|
||||
{
|
||||
public static partial class ConsoleHelper
|
||||
{
|
||||
[SupportedOSPlatform("windows")]
|
||||
[LibraryImport("kernel32")]
|
||||
private static partial nint GetConsoleWindow();
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[LibraryImport("user32")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool ShowWindow(nint hWnd, int nCmdShow);
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[LibraryImport("user32")]
|
||||
private static partial nint GetForegroundWindow();
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[LibraryImport("user32")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool SetForegroundWindow(nint hWnd);
|
||||
|
||||
public static bool SetConsoleWindowStateSupported => OperatingSystem.IsWindows();
|
||||
|
||||
public static void SetConsoleWindowState(bool show)
|
||||
@@ -53,11 +35,16 @@ namespace Ryujinx.UI.Common.Helper
|
||||
return;
|
||||
}
|
||||
|
||||
SetForegroundWindow(hWnd);
|
||||
|
||||
hWnd = GetForegroundWindow();
|
||||
|
||||
ShowWindow(hWnd, show ? SW_SHOW : SW_HIDE);
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[LibraryImport("kernel32")]
|
||||
private static partial nint GetConsoleWindow();
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[LibraryImport("user32")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool ShowWindow(nint hWnd, int nCmdShow);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Ryujinx.UI.Common.Helper
|
||||
}
|
||||
}
|
||||
|
||||
public static void SaveDownloadableContentsJson(ulong applicationIdBase, List<(DownloadableContentModel, bool IsEnabled)> dlcs)
|
||||
public static void SaveDownloadableContentsJson(VirtualFileSystem vfs, ulong applicationIdBase, List<(DownloadableContentModel, bool IsEnabled)> dlcs)
|
||||
{
|
||||
DownloadableContentContainer container = default;
|
||||
List<DownloadableContentContainer> downloadableContentContainerList = new();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Ryujinx.UI.Common.Helper
|
||||
{
|
||||
private static readonly TitleUpdateMetadataJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||
|
||||
public static List<(TitleUpdateModel Update, bool IsSelected)> LoadTitleUpdatesJson(VirtualFileSystem vfs, ulong applicationIdBase)
|
||||
public static List<(TitleUpdateModel, bool IsSelected)> LoadTitleUpdatesJson(VirtualFileSystem vfs, ulong applicationIdBase)
|
||||
{
|
||||
var titleUpdatesJsonPath = PathToGameUpdatesJson(applicationIdBase);
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Ryujinx.UI.Common.Helper
|
||||
}
|
||||
}
|
||||
|
||||
public static void SaveTitleUpdatesJson(ulong applicationIdBase, List<(TitleUpdateModel, bool IsSelected)> updates)
|
||||
public static void SaveTitleUpdatesJson(VirtualFileSystem vfs, ulong applicationIdBase, List<(TitleUpdateModel, bool IsSelected)> updates)
|
||||
{
|
||||
var titleUpdateWindowData = new TitleUpdateMetadata
|
||||
{
|
||||
@@ -77,7 +77,7 @@ namespace Ryujinx.UI.Common.Helper
|
||||
JsonHelper.SerializeToFile(titleUpdatesJsonPath, titleUpdateWindowData, _serializerContext.TitleUpdateMetadata);
|
||||
}
|
||||
|
||||
private static List<(TitleUpdateModel Update, bool IsSelected)> LoadTitleUpdates(VirtualFileSystem vfs, TitleUpdateMetadata titleUpdateMetadata, ulong applicationIdBase)
|
||||
private static List<(TitleUpdateModel, bool IsSelected)> LoadTitleUpdates(VirtualFileSystem vfs, TitleUpdateMetadata titleUpdateMetadata, ulong applicationIdBase)
|
||||
{
|
||||
var result = new List<(TitleUpdateModel, bool IsSelected)>();
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
<Application.Styles>
|
||||
<sty:FluentAvaloniaTheme PreferUserAccentColor="True" PreferSystemTheme="False" />
|
||||
<sty:FluentAvaloniaTheme PreferSystemTheme="False" />
|
||||
<StyleInclude Source="/Assets/Styles/Styles.xaml" />
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
|
||||
@@ -3,8 +3,6 @@ using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Threading;
|
||||
using LibHac.Common;
|
||||
using LibHac.Ns;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Audio.Backends.Dummy;
|
||||
using Ryujinx.Audio.Backends.OpenAL;
|
||||
@@ -672,7 +670,7 @@ namespace Ryujinx.Ava
|
||||
_cursorState = CursorStates.ForceChangeCursor;
|
||||
}
|
||||
|
||||
public async Task<bool> LoadGuestApplication(BlitStruct<ApplicationControlProperty>? customNacpData = null)
|
||||
public async Task<bool> LoadGuestApplication()
|
||||
{
|
||||
InitializeSwitchInstance();
|
||||
MainWindow.UpdateGraphicsConfig();
|
||||
@@ -742,7 +740,7 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Application, "Loading as Firmware Title (NCA).");
|
||||
|
||||
if (!Device.LoadNca(ApplicationPath, customNacpData))
|
||||
if (!Device.LoadNca(ApplicationPath))
|
||||
{
|
||||
Device.Dispose();
|
||||
|
||||
@@ -1139,7 +1137,7 @@ namespace Ryujinx.Ava
|
||||
LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%",
|
||||
dockedMode,
|
||||
ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(),
|
||||
$"{Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)",
|
||||
LocaleManager.Instance[LocaleKeys.Game] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)",
|
||||
$"FIFO: {Device.Statistics.GetFifoPercent():00.00} %",
|
||||
_displayCount));
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"Language": "اَلْعَرَبِيَّةُ",
|
||||
"MenuBarFileOpenApplet": "فتح التطبيق المصغر",
|
||||
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "افتح تطبيق تحرير Mii في الوضع المستقل",
|
||||
"SettingsTabInputDirectMouseAccess": "الوصول المباشر للفأرة",
|
||||
"SettingsTabSystemMemoryManagerMode": "وضع إدارة الذاكرة:",
|
||||
@@ -31,9 +30,6 @@
|
||||
"MenuBarToolsInstallFirmware": "تثبيت البرنامج الثابت",
|
||||
"MenuBarFileToolsInstallFirmwareFromFile": "تثبيت برنامج ثابت من XCI أو ZIP",
|
||||
"MenuBarFileToolsInstallFirmwareFromDirectory": "تثبيت برنامج ثابت من مجلد",
|
||||
"MenuBarToolsInstallKeys": "Install Keys",
|
||||
"MenuBarFileToolsInstallKeysFromFile": "Install keys from KEYS or ZIP",
|
||||
"MenuBarFileToolsInstallKeysFromFolder": "Install keys from a directory",
|
||||
"MenuBarToolsManageFileTypes": "إدارة أنواع الملفات",
|
||||
"MenuBarToolsInstallFileTypes": "تثبيت أنواع الملفات",
|
||||
"MenuBarToolsUninstallFileTypes": "إزالة أنواع الملفات",
|
||||
@@ -509,13 +505,6 @@
|
||||
"DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\nهل تريد المتابعة؟",
|
||||
"DialogFirmwareInstallerFirmwareInstallWaitMessage": "تثبيت البرنامج الثابت...",
|
||||
"DialogFirmwareInstallerFirmwareInstallSuccessMessage": "تم تثبيت إصدار النظام {0} بنجاح.",
|
||||
"DialogKeysInstallerKeysNotFoundErrorMessage": "An invalid Keys file was found in {0}",
|
||||
"DialogKeysInstallerKeysInstallTitle": "Install Keys",
|
||||
"DialogKeysInstallerKeysInstallMessage": "New Keys file will be installed.",
|
||||
"DialogKeysInstallerKeysInstallSubMessage": "\n\nThis may replace some of the current installed Keys.",
|
||||
"DialogKeysInstallerKeysInstallConfirmMessage": "\n\nDo you want to continue?",
|
||||
"DialogKeysInstallerKeysInstallWaitMessage": "Installing Keys...",
|
||||
"DialogKeysInstallerKeysInstallSuccessMessage": "New Keys file successfully installed.",
|
||||
"DialogUserProfileDeletionWarningMessage": "لن تكون هناك ملفات الشخصية أخرى لفتحها إذا تم حذف الملف الشخصي المحدد",
|
||||
"DialogUserProfileDeletionConfirmMessage": "هل تريد حذف الملف الشخصي المحدد",
|
||||
"DialogUserProfileUnsavedChangesTitle": "تحذير - التغييرات غير محفوظة",
|
||||
@@ -552,13 +541,15 @@
|
||||
"AboutUrlTooltipMessage": "انقر لفتح موقع ريوجينكس في متصفحك الافتراضي.",
|
||||
"AboutDisclaimerMessage": "ريوجينكس لا ينتمي إلى نينتندو™،\nأو أي من شركائها بأي شكل من الأشكال.",
|
||||
"AboutAmiiboDisclaimerMessage": "AmiiboAPI (www.amiiboapi.com) يتم \nاستخدامه في محاكاة أمبيو لدينا.",
|
||||
"AboutPatreonUrlTooltipMessage": "انقر لفتح صفحة ريوجينكس في باتريون في متصفحك الافتراضي.",
|
||||
"AboutGithubUrlTooltipMessage": "انقر لفتح صفحة ريوجينكس في غيت هاب في متصفحك الافتراضي.",
|
||||
"AboutDiscordUrlTooltipMessage": "انقر لفتح دعوة إلى خادم ريوجينكس في ديكسورد في متصفحك الافتراضي.",
|
||||
"AboutTwitterUrlTooltipMessage": "انقر لفتح صفحة ريوجينكس في تويتر في متصفحك الافتراضي.",
|
||||
"AboutRyujinxAboutTitle": "حول:",
|
||||
"AboutRyujinxAboutContent": "Ryujinx is an emulator for the Nintendo Switch™.\nGet all the latest news in our Discord.\nDevelopers interested in contributing can find out more on our GitHub or Discord.",
|
||||
"AboutRyujinxAboutContent": "ريوجينكس هو محاكي لجهاز نينتندو سويتش™.\nمن فضلك ادعمنا على باتريون.\nاحصل على آخر الأخبار على تويتر أو ديسكورد.\nيمكن للمطورين المهتمين بالمساهمة معرفة المزيد على غيت هاب أو ديسكورد.",
|
||||
"AboutRyujinxMaintainersTitle": "تتم صيانته بواسطة:",
|
||||
"AboutRyujinxFormerMaintainersTitle": "Formerly Maintained By:",
|
||||
"AboutRyujinxMaintainersContentTooltipMessage": "انقر لفتح صفحة المساهمين في متصفحك الافتراضي.",
|
||||
"AboutRyujinxSupprtersTitle": "مدعوم على باتريون بواسطة:",
|
||||
"AmiiboSeriesLabel": "مجموعة أميبو",
|
||||
"AmiiboCharacterLabel": "شخصية",
|
||||
"AmiiboScanButtonLabel": "فحصه",
|
||||
@@ -700,9 +691,6 @@
|
||||
"Never": "مطلقا",
|
||||
"SwkbdMinCharacters": "يجب أن يبلغ طوله {0} حرفا على الأقل",
|
||||
"SwkbdMinRangeCharacters": "يجب أن يتكون من {0}-{1} حرفا",
|
||||
"CabinetTitle": "Cabinet Dialog",
|
||||
"CabinetDialog": "Enter your Amiibo's new name",
|
||||
"CabinetScanDialog": "Please scan your Amiibo now.",
|
||||
"SoftwareKeyboard": "لوحة المفاتيح البرمجية",
|
||||
"SoftwareKeyboardModeNumeric": "يجب أن يكون 0-9 أو '.' فقط",
|
||||
"SoftwareKeyboardModeAlphabet": "يجب أن تكون الأحرف غير CJK فقط",
|
||||
@@ -716,6 +704,7 @@
|
||||
"UpdaterAddingFiles": "إضافة ملفات جديدة...",
|
||||
"UpdaterExtracting": "استخراج التحديث...",
|
||||
"UpdaterDownloading": "تحميل التحديث...",
|
||||
"Game": "لعبة",
|
||||
"Docked": "تركيب بالمنصة",
|
||||
"Handheld": "محمول",
|
||||
"ConnectionError": "خطأ في الاتصال",
|
||||
@@ -786,7 +775,7 @@
|
||||
"CheatWindowHeading": "الغش متوفر لـ {0} [{1}]",
|
||||
"BuildId": "معرف البناء:",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"DlcWindowHeading": "{0} DLC(s) available",
|
||||
"DlcWindowHeading": "المحتويات القابلة للتنزيل {0}",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"Language": "Deutsch",
|
||||
"MenuBarFileOpenApplet": "Öffne Anwendung",
|
||||
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Öffnet das Mii-Editor-Applet im Standalone-Modus",
|
||||
"SettingsTabInputDirectMouseAccess": "Direkter Mauszugriff",
|
||||
"SettingsTabSystemMemoryManagerMode": "Speichermanagermodus:",
|
||||
@@ -31,9 +30,6 @@
|
||||
"MenuBarToolsInstallFirmware": "Firmware installieren",
|
||||
"MenuBarFileToolsInstallFirmwareFromFile": "Firmware von einer XCI- oder einer ZIP-Datei installieren",
|
||||
"MenuBarFileToolsInstallFirmwareFromDirectory": "Firmware aus einem Verzeichnis installieren",
|
||||
"MenuBarToolsInstallKeys": "Install Keys",
|
||||
"MenuBarFileToolsInstallKeysFromFile": "Install keys from KEYS or ZIP",
|
||||
"MenuBarFileToolsInstallKeysFromFolder": "Install keys from a directory",
|
||||
"MenuBarToolsManageFileTypes": "Dateitypen verwalten",
|
||||
"MenuBarToolsInstallFileTypes": "Dateitypen installieren",
|
||||
"MenuBarToolsUninstallFileTypes": "Dateitypen deinstallieren",
|
||||
@@ -509,13 +505,6 @@
|
||||
"DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\nMöchtest du fortfahren?",
|
||||
"DialogFirmwareInstallerFirmwareInstallWaitMessage": "Firmware wird installiert...",
|
||||
"DialogFirmwareInstallerFirmwareInstallSuccessMessage": "Systemversion {0} wurde erfolgreich installiert.",
|
||||
"DialogKeysInstallerKeysNotFoundErrorMessage": "An invalid Keys file was found in {0}",
|
||||
"DialogKeysInstallerKeysInstallTitle": "Install Keys",
|
||||
"DialogKeysInstallerKeysInstallMessage": "New Keys file will be installed.",
|
||||
"DialogKeysInstallerKeysInstallSubMessage": "\n\nThis may replace some of the current installed Keys.",
|
||||
"DialogKeysInstallerKeysInstallConfirmMessage": "\n\nDo you want to continue?",
|
||||
"DialogKeysInstallerKeysInstallWaitMessage": "Installing Keys...",
|
||||
"DialogKeysInstallerKeysInstallSuccessMessage": "New Keys file successfully installed.",
|
||||
"DialogUserProfileDeletionWarningMessage": "Es können keine anderen Profile geöffnet werden, wenn das ausgewählte Profil gelöscht wird.",
|
||||
"DialogUserProfileDeletionConfirmMessage": "Möchtest du das ausgewählte Profil löschen?",
|
||||
"DialogUserProfileUnsavedChangesTitle": "Warnung - Nicht gespeicherte Änderungen",
|
||||
@@ -552,13 +541,15 @@
|
||||
"AboutUrlTooltipMessage": "Klicke hier, um die Ryujinx Website im Standardbrowser zu öffnen.",
|
||||
"AboutDisclaimerMessage": "Ryujinx ist in keinster Weise weder mit Nintendo™, \nnoch mit deren Partnern verbunden.",
|
||||
"AboutAmiiboDisclaimerMessage": "AmiiboAPI (www.amiiboapi.com) wird in unserer Amiibo \nEmulation benutzt.",
|
||||
"AboutPatreonUrlTooltipMessage": "Klicke hier, um die Ryujinx Patreon Seite im Standardbrowser zu öffnen.",
|
||||
"AboutGithubUrlTooltipMessage": "Klicke hier, um die Ryujinx GitHub Seite im Standardbrowser zu öffnen.",
|
||||
"AboutDiscordUrlTooltipMessage": "Klicke hier, um eine Einladung zum Ryujinx Discord Server im Standardbrowser zu öffnen.",
|
||||
"AboutTwitterUrlTooltipMessage": "Klicke hier, um die Ryujinx Twitter Seite im Standardbrowser zu öffnen.",
|
||||
"AboutRyujinxAboutTitle": "Über:",
|
||||
"AboutRyujinxAboutContent": "Ryujinx is an emulator for the Nintendo Switch™.\nGet all the latest news in our Discord.\nDevelopers interested in contributing can find out more on our GitHub or Discord.",
|
||||
"AboutRyujinxAboutContent": "Ryujinx ist ein Nintendo Switch™ Emulator.\nBitte unterstütze uns auf Patreon.\nAuf Twitter oder Discord erfährst du alle Neuigkeiten.\nEntwickler, die an einer Mitarbeit interessiert sind, können auf GitHub oder Discord mehr erfahren.",
|
||||
"AboutRyujinxMaintainersTitle": "Entwickelt von:",
|
||||
"AboutRyujinxFormerMaintainersTitle": "Formerly Maintained By:",
|
||||
"AboutRyujinxMaintainersContentTooltipMessage": "Klicke hier, um die Liste der Mitwirkenden im Standardbrowser zu öffnen.",
|
||||
"AboutRyujinxSupprtersTitle": "Unterstützt auf Patreon von:",
|
||||
"AmiiboSeriesLabel": "Amiibo-Serie",
|
||||
"AmiiboCharacterLabel": "Charakter",
|
||||
"AmiiboScanButtonLabel": "Einscannen",
|
||||
@@ -700,9 +691,6 @@
|
||||
"Never": "Niemals",
|
||||
"SwkbdMinCharacters": "Muss mindestens {0} Zeichen lang sein",
|
||||
"SwkbdMinRangeCharacters": "Muss {0}-{1} Zeichen lang sein",
|
||||
"CabinetTitle": "Cabinet Dialog",
|
||||
"CabinetDialog": "Enter your Amiibo's new name",
|
||||
"CabinetScanDialog": "Please scan your Amiibo now.",
|
||||
"SoftwareKeyboard": "Software-Tastatur",
|
||||
"SoftwareKeyboardModeNumeric": "Darf nur 0-9 oder \".\" sein",
|
||||
"SoftwareKeyboardModeAlphabet": "Keine CJK-Zeichen",
|
||||
@@ -716,6 +704,7 @@
|
||||
"UpdaterAddingFiles": "Neue Dateien hinzufügen...",
|
||||
"UpdaterExtracting": "Update extrahieren...",
|
||||
"UpdaterDownloading": "Update herunterladen...",
|
||||
"Game": "Spiel",
|
||||
"Docked": "Docked",
|
||||
"Handheld": "Handheld",
|
||||
"ConnectionError": "Verbindungsfehler.",
|
||||
@@ -786,7 +775,7 @@
|
||||
"CheatWindowHeading": "Cheats verfügbar für {0} [{1}]",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"BuildId": "BuildId:",
|
||||
"DlcWindowHeading": "{0} DLC(s) available",
|
||||
"DlcWindowHeading": "DLC verfügbar für {0} [{1}]",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user